YouTube-simulations/sub_lj.c
2025-01-11 19:25:51 +01:00

16655 lines
599 KiB
C

/*********************/
/* Graphics routines */
/*********************/
#include "colors_waves.c"
// #define HUE_TYPE0 260.0 /* hue of particles of type 0 */
// #define HUE_TYPE0 300.0 /* hue of particles of type 0 */
// #define HUE_TYPE1 90.0 /* hue of particles of type 1 */
int writetiff(char *filename, char *description, int x, int y, int width, int height, int compression)
{
TIFF *file;
GLubyte *image, *p;
int i;
file = TIFFOpen(filename, "w");
if (file == NULL)
{
return 1;
}
image = (GLubyte *) malloc(width * height * sizeof(GLubyte) * 3);
/* OpenGL's default 4 byte pack alignment would leave extra bytes at the
end of each image row so that each full row contained a number of bytes
divisible by 4. Ie, an RGB row with 3 pixels and 8-bit componets would
be laid out like "RGBRGBRGBxxx" where the last three "xxx" bytes exist
just to pad the row out to 12 bytes (12 is divisible by 4). To make sure
the rows are packed as tight as possible (no row padding), set the pack
alignment to 1. */
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, image);
TIFFSetField(file, TIFFTAG_IMAGEWIDTH, (uint32_t) width);
TIFFSetField(file, TIFFTAG_IMAGELENGTH, (uint32_t) height);
TIFFSetField(file, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(file, TIFFTAG_COMPRESSION, compression);
TIFFSetField(file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(file, TIFFTAG_ORIENTATION, ORIENTATION_BOTLEFT);
TIFFSetField(file, TIFFTAG_SAMPLESPERPIXEL, 3);
TIFFSetField(file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(file, TIFFTAG_ROWSPERSTRIP, 1);
TIFFSetField(file, TIFFTAG_IMAGEDESCRIPTION, description);
p = image;
for (i = height - 1; i >= 0; i--)
{
// if (TIFFWriteScanline(file, p, height - i - 1, 0) < 0)
if (TIFFWriteScanline(file, p, i, 0) < 0)
{
free(image);
TIFFClose(file);
return 1;
}
p += width * sizeof(GLubyte) * 3;
}
TIFFClose(file);
if (SAVE_MEMORY) free(image);
return 0;
}
void init() /* initialisation of window */
{
glLineWidth(3);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glOrtho(XMIN, XMAX, YMIN, YMAX , -1.0, 1.0);
// glOrtho(0.0, NX, 0.0, NY, -1.0, 1.0);
}
void blank()
{
if (BLACK) glClearColor(0.0, 0.0, 0.0, 1.0);
else glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
void save_frame_lj()
{
static int counter = 0;
char *name="lj.", n2[100];
char format[6]=".%05i";
counter++;
// printf (" p2 counter = %d \n",counter);
strcpy(n2, name);
sprintf(strstr(n2,"."), format, counter);
strcat(n2, ".tif");
printf(" saving frame %s \n",n2);
writetiff(n2, "Particles with Lennard-Jones interaction in a planar domain", 0, 0,
WINWIDTH, WINHEIGHT, COMPRESSION_LZW);
}
void save_frame_lj_counter(int counter)
{
char *name="lj.", n2[100];
char format[6]=".%05i";
strcpy(n2, name);
sprintf(strstr(n2,"."), format, counter);
strcat(n2, ".tif");
printf(" saving frame %s \n",n2);
writetiff(n2, "Particles with Lennard-Jones interaction in a planar domain", 0, 0,
WINWIDTH, WINHEIGHT, COMPRESSION_LZW);
}
void write_text_fixedwidth( double x, double y, char *st)
{
int l, i;
l=strlen( st ); // see how many characters are in text string.
glRasterPos2d( x, y); // location to start printing text
for( i=0; i < l; i++) // loop until i is greater then l
{
// glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, st[i]); // Print a character on the screen
// glutBitmapCharacter(GLUT_BITMAP_8_BY_13, st[i]); // Print a character on the screen
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, st[i]); // Print a character on the screen
}
}
void write_text( double x, double y, char *st)
{
int l,i;
l=strlen( st ); // see how many characters are in text string.
glRasterPos2d( x, y); // location to start printing text
for( i=0; i < l; i++) // loop until i is greater then l
{
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, st[i]); // Print a character on the screen
// glutBitmapCharacter(GLUT_BITMAP_8_BY_13, st[i]); // Print a character on the screen
}
}
/*********************/
/* some basic math */
/*********************/
double vabs(double x) /* absolute value */
{
double res;
if (x<0.0) res = -x;
else res = x;
return(res);
}
double argument(double x, double y)
{
double alph;
if (x!=0.0)
{
alph = atan(y/x);
if (x<0.0)
alph += PI;
}
else
{
alph = PID;
if (y<0.0)
alph = PI*1.5;
}
return(alph);
}
double ipow(double x, int n)
{
double y;
int i;
y = x;
for (i=1; i<n; i++) y *= x;
return(y);
}
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;
}
/*********************/
/* drawing routines */
/*********************/
void erase_area(double x, double y, double dx, double dy)
{
double pos[2], rgb[3];
hsl_to_rgb(220.0, 0.8, 0.7, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_QUADS);
glVertex2d(x - dx, y - dy);
glVertex2d(x + dx, y - dy);
glVertex2d(x + dx, y + dy);
glVertex2d(x - dx, y + dy);
glEnd();
}
void erase_area_rgb(double x, double y, double dx, double dy, double rgb[3])
{
double pos[2];
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_QUADS);
glVertex2d(x - dx, y - dy);
glVertex2d(x + dx, y - dy);
glVertex2d(x + dx, y + dy);
glVertex2d(x - dx, y + dy);
glEnd();
}
void erase_rect_rgb(double xmin, double ymin, double xmax, double ymax, double rgb[3])
{
double pos[2];
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_QUADS);
glVertex2d(xmin, ymin);
glVertex2d(xmax, ymin);
glVertex2d(xmax, ymax);
glVertex2d(xmin, ymax);
glEnd();
}
void erase_area_hsl(double x, double y, double dx, double dy, double h, double s, double l)
{
double pos[2], rgb[3];
hsl_to_rgb(h, s, l, rgb);
erase_area_rgb(x, y, dx, dy, rgb);
}
void erase_area_hsl_turbo(double x, double y, double dx, double dy, double h, double s, double l)
{
double pos[2], rgb[3];
hsl_to_rgb_turbo(h, s, l, rgb);
erase_area_rgb(x, y, dx, dy, rgb);
}
void erase_rectangle_hsl_turbo(double xmin, double ymin, double xmax, double ymax, double h, double s, double l)
{
double pos[2], rgb[3];
hsl_to_rgb_turbo(h, s, l, rgb);
erase_rect_rgb(xmin, ymin, xmax, ymax, rgb);
}
void draw_line(double x1, double y1, double x2, double y2)
{
glBegin(GL_LINE_STRIP);
glVertex2d(x1, y1);
glVertex2d(x2, y2);
glEnd();
}
void draw_arrow(double x1, double y1, double x2, double y2, double angle, double length)
{
double alpha, beta, x3, y3, x4, y4, x5, y5;
alpha = argument(x2 - x1, y2 - y1);
beta = angle*PI/180.0;
x3 = x2 - length*cos(alpha - beta);
y3 = y2 - length*sin(alpha - beta);
x4 = x2 - length*cos(alpha + beta);
y4 = y2 - length*sin(alpha + beta);
x5 = x2 - 0.5*length*cos(alpha);
y5 = y2 - 0.5*length*sin(alpha);
glBegin(GL_LINE_STRIP);
glVertex2d(x1, y1);
glVertex2d(x5, y5);
glEnd();
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x2, y2);
glVertex2d(x3, y3);
glVertex2d(x4, y4);
glEnd();
}
void draw_rectangle(double x1, double y1, double x2, double y2)
{
glBegin(GL_LINE_LOOP);
glVertex2d(x1, y1);
glVertex2d(x2, y1);
glVertex2d(x2, y2);
glVertex2d(x1, y2);
glEnd();
}
void draw_colored_rectangle(double x1, double y1, double x2, double y2, double rgb[3])
{
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x1, y1);
glVertex2d(x2, y1);
glVertex2d(x2, y2);
glVertex2d(x1, y2);
glEnd();
}
void draw_triangle(double x1, double y1, double x2, double y2, double x3, double y3)
{
glBegin(GL_LINE_LOOP);
glVertex2d(x1, y1);
glVertex2d(x2, y2);
glVertex2d(x3, y3);
glEnd();
}
void draw_colored_triangle(double x1, double y1, double x2, double y2, double x3, double y3, double rgb[3])
{
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x1, y1);
glVertex2d(x2, y2);
glVertex2d(x3, y3);
glEnd();
}
double triangle_area(double x1, double y1, double x2, double y2, double x3, double y3)
{
return((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1));
}
void draw_colored_triangle_turbo(double x1, double y1, double x2, double y2, double x3, double y3, double h, double s, double l)
{
double rgb[3];
hsl_to_rgb_turbo(h, s, l, rgb);
draw_colored_triangle(x1, y1, x2, y2, x3, y3, rgb);
}
void draw_circle(double x, double y, double r, int nseg)
{
int i;
double pos[2], alpha, dalpha;
dalpha = DPI/(double)nseg;
glBegin(GL_LINE_LOOP);
for (i=0; i<=nseg; i++)
{
alpha = (double)i*dalpha;
glVertex2d(x + r*cos(alpha), y + r*sin(alpha));
}
glEnd();
}
void draw_colored_circle(double x, double y, double r, int nseg, double rgb[3])
{
int i;
double pos[2], alpha, dalpha;
dalpha = DPI/(double)nseg;
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x, y);
for (i=0; i<=nseg; i++)
{
alpha = (double)i*dalpha;
glVertex2d(x + r*cos(alpha), y + r*sin(alpha));
}
glEnd();
}
void draw_circle_precomp(double x, double y, double r)
{
int i;
glBegin(GL_LINE_LOOP);
for (i=0; i<=NSEG; i++) glVertex2d(x + r*cosangle[i], y + r*sinangle[i]);
glEnd();
}
void draw_colored_circle_precomp(double x, double y, double r, double rgb[3])
{
int i;
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x, y);
for (i=0; i<=NSEG; i++) glVertex2d(x + r*cosangle[i], y + r*sinangle[i]);
glEnd();
}
void draw_polygon(double x, double y, double r, int nsides, double angle)
{
int i;
double pos[2], alpha, dalpha;
dalpha = DPI/(double)nsides;
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
for (i=0; i<=nsides; i++)
{
alpha = angle + (double)i*dalpha;
glVertex2d(x + r*cos(alpha), y + r*sin(alpha));
}
glEnd();
}
void draw_colored_polygon(double x, double y, double r, int nsides, double angle, double rgb[3])
{
int i;
double pos[2], alpha, dalpha;
dalpha = DPI/(double)nsides;
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x, y);
for (i=0; i<=nsides; i++)
{
alpha = angle + (double)i*dalpha;
glVertex2d(x + r*cos(alpha), y + r*sin(alpha));
}
glEnd();
}
void draw_rhombus(double x, double y, double r, double angle)
{
int i;
static int first = 1;
static double ratio;
if (first)
{
ratio = tan(0.1*PI);
first = 0;
}
glBegin(GL_LINE_LOOP);
glVertex2d(x + r*cos(angle), y + r*sin(angle));
glVertex2d(x - ratio*r*sin(angle), y + ratio*r*cos(angle));
glVertex2d(x - r*cos(angle), y - r*sin(angle));
glVertex2d(x + ratio*r*sin(angle), y - ratio*r*cos(angle));
glEnd();
}
void draw_colored_rhombus(double x, double y, double r, double angle, double rgb[3])
{
int i;
static int first = 1;
static double ratio;
if (first)
{
ratio = tan(0.1*PI);
first = 0;
}
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x, y);
glVertex2d(x + r*cos(angle), y + r*sin(angle));
glVertex2d(x - ratio*r*sin(angle), y + ratio*r*cos(angle));
glVertex2d(x - r*cos(angle), y - r*sin(angle));
glVertex2d(x + ratio*r*sin(angle), y - ratio*r*cos(angle));
glVertex2d(x + r*cos(angle), y + r*sin(angle));
glEnd();
}
void draw_rotated_rect(double x, double y, double l, double w, double angle)
{
int i;
double ca, sa, lca, lsa, wca, wsa;
ca = cos(angle);
sa = sin(angle);
lca = l*ca;
lsa = l*sa;
wca = w*ca;
wsa = w*sa;
glBegin(GL_LINE_LOOP);
glVertex2d(x + lca - wsa, y + lsa + wca);
glVertex2d(x - lca - wsa, y - lsa + wca);
glVertex2d(x - lca + wsa, y - lsa - wca);
glVertex2d(x + lca + wsa, y + lsa - wca);
glEnd();
}
void draw_colored_rotated_rect(double x, double y, double l, double w, double angle, double rgb[3])
{
int i;
double ca, sa, lca, lsa, wca, wsa;
ca = cos(angle);
sa = sin(angle);
lca = l*ca;
lsa = l*sa;
wca = w*ca;
wsa = w*sa;
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x, y);
glVertex2d(x + lca - wsa, y + lsa + wca);
glVertex2d(x - lca - wsa, y - lsa + wca);
glVertex2d(x - lca + wsa, y - lsa - wca);
glVertex2d(x + lca + wsa, y + lsa - wca);
glVertex2d(x + lca - wsa, y + lsa + wca);
glEnd();
}
void draw_colored_sector(double xc, double yc, double r1, double r2, double angle1, double angle2, double rgb[3], int nsides)
{
int i;
double angle, dangle;
dangle = (angle2 - angle1)/(double)nsides;
glColor3f(rgb[0], rgb[1], rgb[2]);
glBegin(GL_TRIANGLE_FAN);
glVertex2d(xc + r1*cos(angle1), yc + r1*sin(angle1));
for (i = 0; i < nsides+1; i++)
{
angle = angle1 + dangle*(double)i;
glVertex2d(xc + r2*cos(angle), yc + r2*sin(angle));
}
glEnd();
glBegin(GL_TRIANGLE_FAN);
glVertex2d(xc + r2*cos(angle2), yc + r2*sin(angle2));
for (i = 0; i < nsides+1; i++)
{
angle = angle1 + dangle*(double)i;
glVertex2d(xc + r1*cos(angle), yc + r1*sin(angle));
}
glEnd();
}
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);
}
}
double partner_color(int np)
{
switch(np){
case (0): return(340.0);
case (1): return(260.0);
case (2): return(210.0);
case (3): return(140.0);
case (4): return(70.0);
default: return(20.0);
// case (0): return(70.0);
// case (1): return(200.0);
// case (2): return(280.0);
// case (3): return(140.0);
// case (4): return(320.0);
// default: return(20.0);
}
}
double type_hue(int type)
{
int hue;
double t2;
static double b, hmax;
static int first = 1;
if (first)
{
hmax = 360.0;
b = 16.0*(hmax - HUE_TYPE3);
first = 0;
}
if ((RD_REACTION == CHEM_CATALYTIC_A2D)&&(type == 4)) return(HUE_TYPE3);
if ((RD_REACTION == CHEM_ABDACBE)&&(type == 4)) return(HUE_TYPE3);
if ((RD_REACTION == CHEM_ABDACBE)&&(type == 5)) return(280.0);
switch (type) {
case (0): return(HUE_TYPE0);
case (1): return(HUE_TYPE1);
case (2): return(HUE_TYPE2);
case (3): return(HUE_TYPE3);
case (4): return(HUE_TYPE4);
case (5): return(HUE_TYPE5);
case (6): return(HUE_TYPE6);
case (7):
{
if (RD_REACTION == CHEM_BZ) return(HUE_TYPE2);
else return(HUE_TYPE7);
}
default:
{
if (RD_REACTION == CHEM_BZ)
{
if (type == 7) return(HUE_TYPE2);
if (type == 8) type = 5;
}
else if ((RD_REACTION == CHEM_BRUSSELATOR)&&(type >= 5)) return(70.0);
t2 = (double)(type*type);
hue = (hmax*t2 - b)/t2;
return(hue);
}
}
}
void set_type_color(int type, double lum, double rgb[3])
{
int hue;
if (COUNT_PARTNER_TYPE) hue = partner_color(type-1);
else hue = type_hue(type);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE);
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
}
double distance_to_segment(double x, double y, double x1, double y1, double x2, double y2)
/* distance of (x,y) to segment from (x1,y1) to (x2,y2) */
{
double xp, yp, angle, length, ca, sa;
angle = argument(x2 - x1, y2 - y1);
length = module2(x2 - x1, y2 - y1);
ca = cos(angle);
sa = sin(angle);
xp = ca*(x - x1) + sa*(y - y1);
yp = -sa*(x - x1) + ca*(y - y1);
if ((xp >= 0)&&(xp <= length)) return(vabs(yp));
else if (xp < 0) return(module2(xp, yp));
else return(module2(xp-length, yp));
}
int in_polygon(double x, double y, double r, int npoly, double apoly)
/* test whether (x,y) is in regular polygon of npoly sides inscribed in circle of radius r, turned by apoly Pi/2 */
{
int condition = 1, k;
double omega, cw, angle;
omega = DPI/((double)npoly);
cw = cos(omega*0.5);
for (k=0; k<npoly; k++)
{
angle = -apoly*PID + ((double)k+0.5)*omega;
condition = condition*(x*cos(angle) + y*sin(angle) < r*cw);
}
return(condition);
}
void init_angles()
/* initialise cos and sin of angles to save computing time */
{
int i;
double alpha, dalpha;
dalpha = DPI/(double)NSEG;
for (i=0; i<=NSEG; i++)
{
alpha = (double)i*dalpha;
cosangle[i] = cos(alpha);
sinangle[i] = sin(alpha);
}
}
int generate_poisson_discs(t_point *point, int nmax, double xmin, double xmax, double ymin, double ymax, double dpoisson)
/* generate a Poisson disc sample in a given rectangle */
{
int i, j, k, n_p_active, ncandidates=PDISC_CANDIDATES, naccepted, npoints;
double r, phi, x, y;
short int active_poisson[nmax], far;
printf("Generating Poisson disc sample\n");
/* generate first circle */
point[0].xc = (xmax - xmin)*(double)rand()/RAND_MAX + xmin;
point[0].yc = (ymax - ymin)*(double)rand()/RAND_MAX + ymin;
active_poisson[0] = 1;
n_p_active = 1;
npoints = 1;
while ((n_p_active > 0)&&(npoints < nmax))
{
/* randomly select an active circle */
i = rand()%(npoints);
while (!active_poisson[i]) i = rand()%(npoints);
/* generate new candidates */
naccepted = 0;
for (j=0; j<ncandidates; j++)
{
r = dpoisson*(2.0*(double)rand()/RAND_MAX + 1.0);
phi = DPI*(double)rand()/RAND_MAX;
x = point[i].xc + r*cos(phi);
y = point[i].yc + r*sin(phi);
far = 1;
for (k=0; k<npoints; k++) if ((k!=i))
{
/* new circle is far away from circle k */
far = far*((x - point[k].xc)*(x - point[k].xc) + (y - point[k].yc)*(y - point[k].yc) >= dpoisson*dpoisson);
/* new circle is in domain */
far = far*(x < xmax)*(x > xmin)*(y < ymax)*(y > ymin);
}
if (far) /* accept new circle */
{
printf("New npoint at (%.3f,%.3f) accepted\n", x, y);
point[npoints].xc = x;
point[npoints].yc = y;
active_poisson[npoints] = 1;
npoints++;
n_p_active++;
naccepted++;
}
}
if (naccepted == 0) /* inactivate circle i */
{
active_poisson[i] = 0;
n_p_active--;
}
printf("%i active npoints\n", n_p_active);
}
printf("Generated %i points\n", npoints);
return(npoints);
}
void init_particle_config(t_particle particles[NMAXCIRCLES])
/* initialise particle configuration */
{
int i, j, k, n, ncirc0, n_p_active, ncandidates = PDISC_CANDIDATES, naccepted;
double dx, dy, p, phi, r, r0, ra[5], sa[5], height, x, y = 0.0, gamma, dpoisson = PDISC_DISTANCE*MU, xx[4], yy[4];
short int active_poisson[NMAXCIRCLES], far;
switch (CIRCLE_PATTERN) {
case (C_SQUARE):
{
ncircles = NGRIDX*NGRIDY;
dx = (INITXMAX - INITXMIN)/((double)NGRIDX);
dy = (INITYMAX - INITYMIN)/((double)NGRIDY);
for (i = 0; i < NGRIDX; i++)
for (j = 0; j < NGRIDY; j++)
{
n = NGRIDY*i + j;
particles[n].xc = INITXMIN + ((double)i - 0.5)*dx;
particles[n].yc = INITYMIN + ((double)j - 0.5)*dy;
particles[n].radius = MU;
/* activate only circles that intersect the domain */
if ((particles[n].yc < INITYMAX + MU)&&(particles[n].yc > INITYMIN - MU)&&(particles[n].xc < INITXMAX + MU)&&(particles[n].xc > INITXMIN - MU)) particles[n].active = 1;
else particles[n].active = 0;
}
break;
}
case (C_HEX):
{
ncircles = NGRIDX*(NGRIDY+1);
dx = (INITXMAX - INITXMIN)/((double)NGRIDX);
dy = (INITYMAX - INITYMIN)/((double)NGRIDY);
// dx = dy*0.5*sqrt(3.0);
for (i = 0; i < NGRIDX; i++)
for (j = 0; j < NGRIDY+1; j++)
{
n = (NGRIDY+1)*i + j;
// particles[n].xc = ((double)(i-NGRIDX/2) + 0.5)*dx; /* is +0.5 needed? */
particles[n].xc = INITXMIN + ((double)i - 0.5)*dx;
particles[n].yc = INITYMIN + ((double)j - 0.5)*dy;
if ((i+NGRIDX)%2 == 1) particles[n].yc += 0.5*dy;
if (particles[n].yc > INITYMAX) particles[n].yc += INITYMIN - INITYMAX;
// else if (particles[n].yc < YMIN) particles[n].yc += YMAX - YMIN;
particles[n].radius = MU;
/* activate only circles that intersect the domain */
if ((particles[n].yc < INITYMAX + MU)&&(particles[n].yc > INITYMIN - MU)&&(particles[n].xc < INITXMAX + MU)&&(particles[n].xc > INITXMIN - MU)) particles[n].active = 1;
else particles[n].active = 0;
}
break;
}
case (C_RAND_DISPLACED):
{
ncircles = NGRIDX*NGRIDY;
dy = (YMAX - YMIN)/((double)NGRIDY);
for (i = 0; i < NGRIDX; i++)
for (j = 0; j < NGRIDY; j++)
{
n = NGRIDY*i + j;
particles[n].xc = ((double)(i-NGRIDX/2) + 0.5*((double)rand()/RAND_MAX - 0.5))*dy;
particles[n].yc = YMIN + ((double)j + 0.5 + 0.5*((double)rand()/RAND_MAX - 0.5))*dy;
particles[n].radius = MU;
// particles[n].radius = MU*sqrt(1.0 + 0.8*((double)rand()/RAND_MAX - 0.5));
particles[n].active = 1;
}
break;
}
case (C_RAND_PERCOL):
{
ncircles = NGRIDX*NGRIDY;
dy = (YMAX - YMIN)/((double)NGRIDY);
for (i = 0; i < NGRIDX; i++)
for (j = 0; j < NGRIDY; j++)
{
n = NGRIDY*i + j;
particles[n].xc = ((double)(i-NGRIDX/2) + 0.5)*dy;
particles[n].yc = YMIN + ((double)j + 0.5)*dy;
particles[n].radius = MU;
p = (double)rand()/RAND_MAX;
if (p < P_PERCOL) particles[n].active = 1;
else particles[n].active = 0;
}
break;
}
case (C_RAND_POISSON):
{
ncircles = NPOISSON;
for (n = 0; n < NPOISSON; n++)
{
// particles[n].xc = LAMBDA*(2.0*(double)rand()/RAND_MAX - 1.0);
particles[n].xc = (XMAX - XMIN)*(double)rand()/RAND_MAX + XMIN;
particles[n].yc = (YMAX - YMIN)*(double)rand()/RAND_MAX + YMIN;
particles[n].radius = MU;
particles[n].active = 1;
}
break;
}
case (C_CLOAK):
{
ncircles = 200;
for (i = 0; i < 40; i++)
for (j = 0; j < 5; j++)
{
n = 5*i + j;
phi = (double)i*DPI/40.0;
r = LAMBDA*0.5*(1.0 + (double)j/5.0);
particles[n].xc = r*cos(phi);
particles[n].yc = r*sin(phi);
particles[n].radius = MU;
particles[n].active = 1;
}
break;
}
case (C_CLOAK_A): /* optimized model A1 by C. Jo et al */
{
ncircles = 200;
ra[0] = 0.0731; sa[0] = 1.115;
ra[1] = 0.0768; sa[1] = 1.292;
ra[2] = 0.0652; sa[2] = 1.464;
ra[3] = 0.056; sa[3] = 1.633;
ra[4] = 0.0375; sa[4] = 1.794;
for (i = 0; i < 40; i++)
for (j = 0; j < 5; j++)
{
n = 5*i + j;
phi = (double)i*DPI/40.0;
r = LAMBDA*sa[j];
particles[n].xc = r*cos(phi);
particles[n].yc = r*sin(phi);
particles[n].radius = LAMBDA*ra[j];
particles[n].active = 1;
}
break;
}
case (C_LASER):
{
ncircles = 17;
xx[0] = 0.5*(X_SHOOTER + X_TARGET);
xx[1] = LAMBDA - 0.5*(X_TARGET - X_SHOOTER);
xx[2] = -xx[0];
xx[3] = -xx[1];
yy[0] = 0.5*(Y_SHOOTER + Y_TARGET);
yy[1] = 1.0 - 0.5*(Y_TARGET - Y_SHOOTER);
yy[2] = -yy[0];
yy[3] = -yy[1];
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
{
particles[4*i + j].xc = xx[i];
particles[4*i + j].yc = yy[j];
}
particles[ncircles - 1].xc = X_TARGET;
particles[ncircles - 1].yc = Y_TARGET;
for (i=0; i<ncircles - 1; i++)
{
particles[i].radius = MU;
particles[i].active = 1;
}
particles[ncircles - 1].radius = 0.5*MU;
particles[ncircles - 1].active = 2;
break;
}
case (C_POISSON_DISC):
{
/* TODO: use generate_poisson_discs */
printf("Generating Poisson disc sample\n");
/* generate first circle */
// particles[0].xc = LAMBDA*(2.0*(double)rand()/RAND_MAX - 1.0);
particles[0].xc = (INITXMAX - INITXMIN)*(double)rand()/RAND_MAX + INITXMIN;
particles[0].yc = (INITYMAX - INITYMIN)*(double)rand()/RAND_MAX + INITYMIN;
active_poisson[0] = 1;
// particles[0].active = 1;
n_p_active = 1;
ncircles = 1;
while ((n_p_active > 0)&&(ncircles < NMAXCIRCLES))
{
/* randomly select an active circle */
i = rand()%(ncircles);
while (!active_poisson[i]) i = rand()%(ncircles);
// printf("Starting from circle %i at (%.3f,%.3f)\n", i, particles[i].xc, particles[i].yc);
/* generate new candidates */
naccepted = 0;
for (j=0; j<ncandidates; j++)
{
r = dpoisson*(2.0*(double)rand()/RAND_MAX + 1.0);
phi = DPI*(double)rand()/RAND_MAX;
x = particles[i].xc + r*cos(phi);
y = particles[i].yc + r*sin(phi);
// printf("Testing new circle at (%.3f,%.3f)\t", x, y);
far = 1;
for (k=0; k<ncircles; k++) if ((k!=i))
{
/* new circle is far away from circle k */
far = far*((x - particles[k].xc)*(x - particles[k].xc) + (y - particles[k].yc)*(y - particles[k].yc) >= dpoisson*dpoisson);
/* new circle is in domain */
far = far*(x < INITXMAX)*(x > INITXMIN)*(y < INITYMAX)*(y > INITYMIN);
// far = far*(vabs(x) < LAMBDA)*(y < INITYMAX)*(y > INITYMIN);
}
if (far) /* accept new circle */
{
printf("New circle at (%.3f,%.3f) accepted\n", x, y);
particles[ncircles].xc = x;
particles[ncircles].yc = y;
particles[ncircles].radius = MU;
particles[ncircles].active = 1;
active_poisson[ncircles] = 1;
ncircles++;
n_p_active++;
naccepted++;
}
// else printf("Rejected\n");
}
if (naccepted == 0) /* inactivate circle i */
{
// printf("No candidates work, inactivate circle %i\n", i);
active_poisson[i] = 0;
n_p_active--;
}
printf("%i active circles\n", n_p_active);
}
printf("Generated %i circles\n", ncircles);
break;
}
case (C_GOLDEN_MEAN):
{
ncircles = 300;
gamma = (sqrt(5.0) - 1.0)*0.5; /* golden mean */
height = YMAX - YMIN;
dx = 2.0*LAMBDA/((double)ncircles);
for (n = 0; n < ncircles; n++)
{
particles[n].xc = -LAMBDA + n*dx;
particles[n].yc = y;
y += height*gamma;
if (y > YMAX) y -= height;
particles[n].radius = MU;
particles[n].active = 1;
}
/* test for circles that overlap top or bottom boundary */
ncirc0 = ncircles;
for (n=0; n < ncirc0; n++)
{
if (particles[n].yc + particles[n].radius > YMAX)
{
particles[ncircles].xc = particles[n].xc;
particles[ncircles].yc = particles[n].yc - height;
particles[ncircles].radius = MU;
particles[ncircles].active = 1;
ncircles ++;
}
else if (particles[n].yc - particles[n].radius < YMIN)
{
particles[ncircles].xc = particles[n].xc;
particles[ncircles].yc = particles[n].yc + height;
particles[ncircles].radius = MU;
particles[ncircles].active = 1;
ncircles ++;
}
}
break;
}
case (C_GOLDEN_SPIRAL):
{
ncircles = 1;
particles[0].xc = 0.0;
particles[0].yc = 0.0;
gamma = (sqrt(5.0) - 1.0)*PI; /* golden mean times 2Pi */
phi = 0.0;
r0 = 2.0*MU;
r = r0 + MU;
for (i=0; i<1000; i++)
{
x = r*cos(phi);
y = r*sin(phi);
phi += gamma;
r += MU*r0/r;
if ((vabs(x) < LAMBDA)&&(vabs(y) < YMAX + MU))
{
particles[ncircles].xc = x;
particles[ncircles].yc = y;
ncircles++;
}
}
for (i=0; i<ncircles; i++)
{
particles[i].radius = MU;
/* inactivate circles outside the domain */
if ((particles[i].yc < YMAX + MU)&&(particles[i].yc > YMIN - MU)) particles[i].active = 1;
// printf("i = %i, circlex = %.3lg, circley = %.3lg\n", i, particles[i].xc, particles[i].yc);
}
break;
}
case (C_SQUARE_HEX):
{
ncircles = NGRIDX*(NGRIDY+1);
dy = (YMAX - YMIN)/((double)NGRIDY);
dx = dy*0.5*sqrt(3.0);
for (i = 0; i < NGRIDX; i++)
for (j = 0; j < NGRIDY+1; j++)
{
n = (NGRIDY+1)*i + j;
particles[n].xc = ((double)(i-NGRIDX/2) + 0.5)*dy; /* is +0.5 needed? */
particles[n].yc = YMIN + ((double)j - 0.5)*dy;
if (((i+NGRIDX)%4 == 2)||((i+NGRIDX)%4 == 3)) particles[n].yc += 0.5*dy;
particles[n].radius = MU;
/* activate only circles that intersect the domain */
if ((particles[n].yc < YMAX + MU)&&(particles[n].yc > YMIN - MU)) particles[n].active = 1;
else particles[n].active = 0;
}
break;
}
case (C_POOL_TABLE):
{
for (i=1; i<6; i++) for (j=0; j<i; j++)
{
particles[ncircles].xc = INITXMIN + (double)i*0.25*(INITXMAX - INITXMIN);
particles[ncircles].yc = 0.5*(INITYMIN + INITYMAX) + ((double)j - 0.5*(double)(i-1))*0.25*(INITYMAX - INITYMIN);
particles[ncircles].radius = MU;
particles[ncircles].active = 1;
ncircles += 1;
}
break;
}
case (C_ONE):
{
particles[ncircles].xc = 0.0;
particles[ncircles].yc = 0.0;
particles[ncircles].radius = MU;
particles[ncircles].active = 1;
ncircles += 1;
break;
}
case (C_TWO): /* used for comparison with cloak */
{
particles[ncircles].xc = 0.0;
particles[ncircles].yc = 0.0;
particles[ncircles].radius = MU;
particles[ncircles].active = 2;
ncircles += 1;
particles[ncircles].xc = 0.0;
particles[ncircles].yc = 0.0;
particles[ncircles].radius = 2.0*MU;
particles[ncircles].active = 1;
ncircles += 1;
break;
}
case (C_NOTHING):
{
ncircles += 0;
break;
}
default:
{
printf("Function init_circle_config not defined for this pattern \n");
}
}
for (i=0; i<ncircles; i++)
{
particles[i].coulomb = 1;
particles[i].reactive = 1;
}
}
void add_particle_config(t_particle particles[NMAXCIRCLES], double xmin, double xmax, double ymin, double ymax, int nx, int ny, short int reactive, double radius)
/* add particles to configuration */
{
int i, j, k, n, ncirc0, n_p_active, ncandidates = PDISC_CANDIDATES, naccepted, newcircles;
double dx, dy, p, phi, r, r0, ra[5], sa[5], height, x, y = 0.0, gamma, dpoisson, xx[4], yy[4];
short int active_poisson[NMAXCIRCLES], far;
dpoisson = PDISC_DISTANCE*radius;
switch (CIRCLE_PATTERN_B) {
case (C_SQUARE):
{
n = ncircles;
dx = (xmax - xmin)/((double)nx);
dy = (ymax - ymin)/((double)ny);
for (i = 0; i < nx; i++)
for (j = 0; j < NGRIDY; j++)
{
particles[n].xc = xmin + ((double)i + 0.5)*dx;
particles[n].yc = ymin + ((double)j + 0.5)*dy;
particles[n].radius = MU;
particles[n].active = 1;
particles[n].added = 1;
particles[n].coulomb = 1;
particles[n].reactive = reactive;
n++;
ncircles++;
}
break;
}
case (C_HEX):
{
dx = (xmax - xmin)/((double)NGRIDX);
dy = (ymax - ymin)/((double)NGRIDY);
// dx = dy*0.5*sqrt(3.0);
for (i = 0; i < NGRIDX; i++)
for (j = 0; j < NGRIDY+1; j++)
{
n = ncircles + (NGRIDY+1)*i + j;
// particles[n].xc = ((double)(i-NGRIDX/2) + 0.5)*dx; /* is +0.5 needed? */
particles[n].xc = xmin + ((double)i - 0.5)*dx;
particles[n].yc = ymin + ((double)j - 0.5)*dy;
if ((i+NGRIDX)%2 == 1) particles[n].yc += 0.5*dy;
particles[n].radius = radius;
/* activate only circles that intersect the domain */
if ((particles[n].yc < ymax + radius)&&(particles[n].yc > ymin - radius)&&(particles[n].xc < xmax + radius)&&(particles[n].xc > xmin - radius)) particles[n].active = 1;
else particles[n].active = 0;
}
ncircles += NGRIDX*(NGRIDY+1);
break;
}
case (C_POISSON_DISC):
{
ncirc0 = ncircles;
printf("Generating new Poisson disc sample\n");
/* generate first circle */
particles[ncirc0].xc = (xmax - xmin)*(double)rand()/RAND_MAX + xmin;
particles[ncirc0].yc = (ymax - ymin)*(double)rand()/RAND_MAX + ymin;
active_poisson[0] = 1;
// // particles[0].active = 1;
n_p_active = 1;
newcircles = 1;
while ((n_p_active > 0)&&(ncircles < NMAXCIRCLES))
{
/* randomly select an active circle */
i = rand()%(newcircles);
while (!active_poisson[i]) i = rand()%(ncircles);
// printf("Starting from circle %i at (%.3f,%.3f)\n", i, particles[i].xc, particles[i].yc);
/* generate new candidates */
naccepted = 0;
for (j=0; j<ncandidates; j++)
{
r = dpoisson*(2.0*(double)rand()/RAND_MAX + 1.0);
phi = DPI*(double)rand()/RAND_MAX;
x = particles[ncirc0 + i].xc + r*cos(phi);
y = particles[ncirc0 + i].yc + r*sin(phi);
// printf("Testing new circle at (%.3f,%.3f)\t", x, y);
far = 1;
for (k=0; k<ncircles; k++) if ((k!=i))
{
/* new circle is far away from circle k */
far = far*((x - particles[k].xc)*(x - particles[k].xc) + (y - particles[k].yc)*(y - particles[k].yc) >= dpoisson*dpoisson);
/* new circle is in domain */
far = far*(x < xmax)*(x > xmin)*(y < ymax)*(y > ymin);
// far = far*(vabs(x) < LAMBDA)*(y < INITYMAX)*(y > INITYMIN);
}
if (far) /* accept new circle */
{
printf("New circle at (%.3f,%.3f) accepted\n", x, y);
particles[ncircles].xc = x;
particles[ncircles].yc = y;
particles[ncircles].radius = radius;
particles[ncircles].active = 1;
particles[ncircles].added = 1;
particles[ncircles].coulomb = 1;
particles[ncircles].reactive = reactive;
active_poisson[ncircles] = 1;
ncircles++;
ncirc0++;
n_p_active++;
naccepted++;
}
// else printf("Rejected\n");
}
if (naccepted == 0) /* inactivate circle i */
{
// printf("No candidates work, inactivate circle %i\n", i);
active_poisson[i] = 0;
n_p_active--;
}
printf("%i active circles\n", n_p_active);
}
printf("Generated %i circles\n", ncircles);
break;
}
default:
{
printf("Function init_circle_config not defined for this pattern \n");
}
}
}
void init_people_config(t_person people[NMAXCIRCLES])
/* initialise particle configuration */
{
t_particle particles[NMAXCIRCLES];
int n;
init_particle_config(particles);
for (n=0; n<ncircles; n++)
{
people[n].xc = particles[n].xc;
people[n].yc = particles[n].yc;
people[n].radius = particles[n].radius;
people[n].active = particles[n].active;
}
}
void add_obstacle(double x, double y, double radius, t_obstacle obstacle[NMAXOBSTACLES])
/* add a circular obstacle to obstacle configuration */
{
if (nobstacles + 1 < NMAXOBSTACLES)
{
obstacle[nobstacles].xc = x;
obstacle[nobstacles].yc = y;
obstacle[nobstacles].radius = radius;
obstacle[nobstacles].active = 1;
nobstacles++;
}
else printf("Warning: NMAXOBSTACLES should be increased\n");
}
void sort_angles(double *angle, int n, int *table)
/* writes to table positions of angles in increasing order */
{
int i, j, temp;
double min, temp_angle[NMAX_OBSTACLE_NEIGHBOURS];
for (i=0; i<n; i++) table[i] = i;
/* bubble sort */
for (i = 0; i < n - 1; i++)
{
for (j = 0; j < n - i - 1; j++)
{
if (angle[table[j]] > angle[table[j+1]])
{
temp = table[j];
table[j] = table[j+1];
table[j+1] = temp;
}
}
}
/* TEST */
for (i=0; i<n; i++) temp_angle[i] = angle[i];
for (i=0; i<n; i++) angle[i] = temp_angle[table[i]];
}
int triangle_vertex_overlap(int s1, int t1, int u1, int s2, int t2, int u2, t_otriangle triangle1, t_otriangle triangle2, t_obstacle obstacle[NMAXOBSTACLES])
/* returns 1 if triangles with specified common vertices overlap */
{
int i1, j1, k1, i2, j2, k2;
double x1, y1, x2, y2, x3, y3, xp3, yp3, a, b, q1, q2;
i1 = triangle1.i[s1];
j1 = triangle1.i[t1];
k1 = triangle1.i[u1];
i2 = triangle2.i[s2];
j2 = triangle2.i[t2];
k2 = triangle2.i[u2];
x1 = obstacle[i1].xc;
y1 = obstacle[i1].yc;
x2 = obstacle[j1].xc;
y2 = obstacle[j1].yc;
x3 = obstacle[k1].xc;
y3 = obstacle[k1].yc;
xp3 = obstacle[k2].xc;
yp3 = obstacle[k2].yc;
a =y2 - y1;
b = x2 - x1;
q1 = a*(x3-x1) - b*(y3-y1);
q2 = a*(xp3-x1) - b*(yp3-y1);
return(q1*q2 > 0.0);
}
int old_triangle_overlap(t_otriangle triangle1, t_otriangle triangle2, t_obstacle obstacle[NMAXOBSTACLES])
/* returns 1 if triangles overlap */
{
int j, jplus, jminus;
/* find first vertex equal to triangle1.i[0] */
j = 0;
while (triangle2.i[j] != triangle1.i[0]) j++;
if (j < 3)
{
jplus = (j+1)%3;
jminus = (j+5)%3;
if (triangle1.i[1] == triangle2.i[jplus])
return(triangle_vertex_overlap(0, 1, 2, j, jplus, jminus, triangle1, triangle2, obstacle));
else if (triangle1.i[2] == triangle2.i[jminus])
return(triangle_vertex_overlap(2, 0, 1, j, jminus, jplus, triangle1, triangle2, obstacle));
else return(0);
}
else
{
if ((triangle1.i[1] == triangle2.i[1])&&(triangle1.i[2] == triangle2.i[2]))
return(triangle_vertex_overlap(1, 2, 0, 1, 2, 0, triangle1, triangle2, obstacle));
else return(0);
}
}
int triangle_overlap(t_otriangle triangle1, t_otriangle triangle2)
/* returns 1 if triangles overlap */
{
int j, jplus, jminus;
// printf("Testing triangles (%i, %i, %i) and (%i, %i, %i)\n", triangle1.i[0], triangle1.i[1], triangle1.i[2], triangle2.i[0], triangle2.i[1], triangle2.i[2]);
/* find first vertex equal to triangle1.i[0] */
j = 0;
while ((j<3)&&(triangle2.i[j] != triangle1.i[0])) j++;
if (j < 3)
{
jplus = (j+1)%3;
jminus = (j+5)%3;
if (triangle1.i[1] == triangle2.i[jplus])
return(1);
else if (triangle1.i[2] == triangle2.i[jminus])
return(1);
else return(0);
}
else
{
if ((triangle1.i[1] == triangle2.i[1])&&(triangle1.i[2] == triangle2.i[2]))
return(1);
else return(0);
}
}
int init_obstacle_facets(t_otriangle otriangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES], t_ofacet ofacet[NMAXOBSTACLES])
/* group triangles into facets, for option COUPLE_OBSTACLES */
{
int i, j, k, n, ik, f, nt;
printf("Initializing obstacle facets from %i triangles\n", n_otriangles);
// printf("1\n");
// printf("2\n");
for (i=0; i<n_otriangles; i++) otriangle[i].infacet = 0;
for (i=0; i<NMAXOBSTACLES; i++) ofacet[i].ntriangles = 0;
// printf("3\n");
n_ofacets = 0;
i = 0;
while (i < n_otriangles)
{
// printf("Triangle %i\t", i);
if (!otriangle[i].infacet)
{
otriangle[i].infacet = 1;
ofacet[n_ofacets].ntriangles = 1;
ofacet[n_ofacets].triangle[0] = i;
j = i + 1;
while ((j < n_otriangles)&&(ofacet[n_ofacets].ntriangles < NMAX_TRIANGLES_PER_FACET))
{
n = ofacet[n_ofacets].ntriangles;
for (k=0; k<n; k++)
{
ik = ofacet[n_ofacets].triangle[k];
if ((!otriangle[j].infacet)&&(triangle_overlap(otriangle[ik], otriangle[j])))
{
ofacet[n_ofacets].ntriangles++;
ofacet[n_ofacets].triangle[n] = j;
otriangle[j].infacet = 1;
}
}
j++;
}
nt = ofacet[n_ofacets].ntriangles;
// printf("Facet %i contains %i triangles: ", n_ofacets, nt);
// for (f=0; f<nt; f++) printf("%i ", ofacet[n_ofacets].triangle[f]);
// printf("\n");
n_ofacets++;
}
i++;
}
if (n_ofacets > NMAXOBSTACLES)
{
printf("NMAXOBSTACLES should be at least %i\n", n_ofacets);
exit(1);
}
return(n_ofacets);
}
double otriangle_area(t_obstacle obstacle[NMAXOBSTACLES], t_otriangle triangle)
{
int s1, s2, s3;
s1 = triangle.i[0];
s2 = triangle.i[1];
s3 = triangle.i[2];
return(vabs(triangle_area(obstacle[s1].xc, obstacle[s1].yc, obstacle[s2].xc, obstacle[s2].yc, obstacle[s3].xc, obstacle[s3].yc)));
}
int init_obstacle_coupling(t_obstacle obstacle[NMAXOBSTACLES], t_otriangle otriangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES], t_ofacet ofacet[NMAXOBSTACLES])
/* initialize list of neighbours, for option COUPLE_OBSTACLES */
{
int i, j, n, m, table[NMAX_OBSTACLE_NEIGHBOURS], neigh_table[NMAX_OBSTACLE_NEIGHBOURS];
double dist, bdistx, bdisty, angle[NMAX_OBSTACLE_NEIGHBOURS], dist_table[NMAX_OBSTACLE_NEIGHBOURS];
short int near_boundary;
printf("Initializing obstacle neighbours\n");
n_otriangles = 0;
for (i=0; i<nobstacles; i++) obstacle[i].nneighb = 0;
for (i=0; i<nobstacles; i++)
{
for (j=i+1; j<nobstacles; j++) if (j != i)
{
dist = module2(obstacle[i].xc - obstacle[j].xc, obstacle[i].yc - obstacle[j].yc);
if ((dist < OBSTACLE_COUPLING_DIST)&&(obstacle[i].nneighb < NMAX_OBSTACLE_NEIGHBOURS))
{
n = obstacle[i].nneighb;
obstacle[i].neighb[n] = j;
obstacle[i].eqdist[n] = dist;
obstacle[i].nneighb++;
n = obstacle[j].nneighb;
obstacle[j].neighb[n] = i;
obstacle[j].eqdist[n] = dist;
obstacle[j].nneighb++;
}
}
// for (i=0; i<nobstacles; i++)
// {
// obstacle[i].nneighb = 0;
// for (j=0; j<nobstacles; j++) if (j != i)
// {
// dist = module2(obstacle[i].xc - obstacle[j].xc, obstacle[i].yc - obstacle[j].yc);
// if ((dist < OBSTACLE_COUPLING_DIST)&&(obstacle[i].nneighb < NMAX_OBSTACLE_NEIGHBOURS))
// {
// n = obstacle[i].nneighb;
// obstacle[i].neighb[n] = j;
// obstacle[i].eqdist[n] = dist;
// obstacle[i].nneighb++;
// }
// }
// printf("Obstacle %i has %i neighbours\t", i, obstacle[i].nneighb);
/* sort neighbours by angle */
n = obstacle[i].nneighb;
for (j=0; j<n; j++)
{
m = obstacle[i].neighb[j];
angle[j] = argument(obstacle[m].xc - obstacle[i].xc, obstacle[m].yc - obstacle[i].yc);
if (angle[j] < 0.0) angle[j] += DPI;
}
sort_angles(angle, n, table);
// for (j=0; j<n; j++) printf("angle %i = %.3lg\t", j, angle[j]*180.0/PI);
// printf("\n");
/* update neighbour list */
for (j=0; j<n; j++)
{
neigh_table[j] = obstacle[i].neighb[j];
dist_table[j] = obstacle[i].eqdist[j];
}
for (j=0; j<n; j++)
{
obstacle[i].neighb[j] = neigh_table[table[j]];
obstacle[i].eqdist[j] = dist_table[table[j]];
}
/* build list of triangles */
if (FILL_OBSTACLE_TRIANGLES)
{
// printf("Building triangle %i\n", n_otriangles);
for (j=0; j<n-1; j++) if (angle[j+1] - angle[j] < PI)
{
otriangle[n_otriangles].i[0] = i;
otriangle[n_otriangles].i[1] = obstacle[i].neighb[j];
otriangle[n_otriangles].i[2] = obstacle[i].neighb[j+1];
n_otriangles++;
}
if (angle[0] - angle[n-1] + DPI < PI)
{
otriangle[n_otriangles].i[0] = i;
otriangle[n_otriangles].i[1] = obstacle[i].neighb[n-1];
otriangle[n_otriangles].i[2] = obstacle[i].neighb[0];
n_otriangles++;
}
}
}
/* initialize triangle areas */
for (i=0; i<n_otriangles; i++)
otriangle[i].area0 = otriangle_area(obstacle, otriangle[i]);
/* build facet table */
printf("Initializing facets from %i triangles\n", n_otriangles);
if (FILL_OBSTACLE_TRIANGLES) n_ofacets = init_obstacle_facets(otriangle, ofacet);
/* pin obstacles that have less than NMAX_OBSTACLE_PINNED neighbours */
bdistx = (BCXMAX - BCXMIN)/(double)NOBSX;
bdisty = (BCYMAX - BCYMIN)/(double)NOBSY;
for (i=0; i<nobstacles; i++)
{
near_boundary = ((obstacle[i].xc <= BCXMIN + bdistx)||(obstacle[i].xc >= BCXMAX - bdistx)||(obstacle[i].yc <= BCYMIN + bdisty)||(obstacle[i].yc >= BCYMAX - bdisty));
switch (OBSTACLE_PINNING_TYPE) {
case (OP_NPINNED):
{
obstacle[i].pinned = (obstacle[i].nneighb <= NMAX_OBSTACLE_PINNED);
break;
}
case (OP_LEFT):
{
obstacle[i].pinned = (obstacle[i].xc <= BCXMIN + bdistx);
break;
}
case (OP_LEFTTOPBOT):
{
obstacle[i].pinned = near_boundary;
break;
}
case (OP_CORNERS):
{
obstacle[i].pinned = (((obstacle[i].xc <= BCXMIN + bdistx)||(obstacle[i].xc >= BCXMAX - bdistx))&&((obstacle[i].yc <= BCYMIN + bdisty)||(obstacle[i].yc >= BCYMAX - bdisty)));
break;
}
case (OP_LEFTCORNERS):
{
obstacle[i].pinned = ((near_boundary)&&((obstacle[i].xc < 0.0)||(obstacle[i].xc > BCXMAX - bdistx)));
break;
}
case (OP_BNDRY_STEP):
{
obstacle[i].pinned = ((near_boundary)&&(obstacle[i].chessboard == 0));
}
}
}
printf("Found %i facets\n", n_ofacets);
// for (i=0; i<n_ofacets; i++)
// {
// printf("Facet %i has %i triangles: ", i, ofacet[i].ntriangles);
// for (j=0; j<ofacet[i].ntriangles; j++)
// {
// n = ofacet[i].triangle[j];
// printf("%i (%i, %i, %i)", n, otriangle[n].i[0], otriangle[n].i[1], otriangle[n].i[2]);
// }
// printf("\n");
// }
// for (i=0; i<n_otriangles; i++)
// {
// printf("Triangle %i has vertices (%i, %i, %i)\n", i, otriangle[i].i[0], otriangle[i].i[1], otriangle[i].i[2]);
// }
//
// exit(0);
printf("Facets initialized\n");
return(n_ofacets);
}
int reset_obstacle_coupling(t_obstacle obstacle[NMAXOBSTACLES], t_otriangle otriangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES], t_ofacet ofacet[NMAXOBSTACLES])
/* initialize list of neighbours, for option COUPLE_OBSTACLES */
{
int i, j, n, m, table[NMAX_OBSTACLE_NEIGHBOURS], neigh_table[NMAX_OBSTACLE_NEIGHBOURS];
double dist, dist1, bdistx, bdisty, angle[NMAX_OBSTACLE_NEIGHBOURS], dist_table[NMAX_OBSTACLE_NEIGHBOURS];
short int *connected;
connected = (short int *)malloc(nobstacles*sizeof(short int));
printf("Resetting obstacle neighbours\n");
n_otriangles = 0;
// for (i=0; i<nobstacles; i++) obstacle[i].nneighb = 0;
for (i=0; i<nobstacles; i++)
{
for (j=0; j<nobstacles; j++) connected[j] = 0;
n = obstacle[i].nneighb;
for (j=0; j<n; j++)
{
m = obstacle[i].neighb[j];
connected[m] = 1;
// printf("Obstacle %i is connected to obstacle %i\n", m, i);
}
obstacle[i].nneighb = 0;
for (j=0; j<nobstacles; j++) if (j!=i)
{
dist = module2(obstacle[i].xc - obstacle[j].xc, obstacle[i].yc - obstacle[j].yc);
if (connected[j])
{
if ((dist < UNCOUPLE_MAXLENGTH*OBSTACLE_COUPLING_DIST)&&(obstacle[i].nneighb < NMAX_OBSTACLE_NEIGHBOURS))
{
n = obstacle[i].nneighb;
obstacle[i].neighb[n] = j;
dist1 = obstacle[i].eqdist[n];
obstacle[i].eqdist[n] = dist;
// obstacle[i].eqdist[n] = 0.99*dist + 0.01*dist1;
obstacle[i].nneighb++;
}
}
else
{
if ((dist < COUPLE_MINLENGTH*OBSTACLE_COUPLING_DIST)&&(obstacle[i].nneighb < NMAX_OBSTACLE_NEIGHBOURS))
{
n = obstacle[i].nneighb;
obstacle[i].neighb[n] = j;
obstacle[i].eqdist[n] = dist;
obstacle[i].nneighb++;
}
}
}
// for (j=i+1; j<nobstacles; j++)
// {
// dist = module2(obstacle[i].xc - obstacle[j].xc, obstacle[i].yc - obstacle[j].yc);
// if ((dist < OBSTACLE_COUPLING_DIST)&&(obstacle[i].nneighb < NMAX_OBSTACLE_NEIGHBOURS))
// {
// n = obstacle[i].nneighb;
// obstacle[i].neighb[n] = j;
// obstacle[i].eqdist[n] = dist;
// obstacle[i].nneighb++;
//
// n = obstacle[j].nneighb;
// obstacle[j].neighb[n] = i;
// obstacle[j].eqdist[n] = dist;
// obstacle[j].nneighb++;
// }
// }
// printf("Obstacle %i has %i neighbours\t", i, obstacle[i].nneighb);
// }
// for (i=0; i<nobstacles; i++)
// {
/* sort neighbours by angle */
n = obstacle[i].nneighb;
for (j=0; j<n; j++)
{
m = obstacle[i].neighb[j];
angle[j] = argument(obstacle[m].xc - obstacle[i].xc, obstacle[m].yc - obstacle[i].yc);
if (angle[j] < 0.0) angle[j] += DPI;
}
sort_angles(angle, n, table);
// for (j=0; j<n; j++) printf("angle %i = %.3lg\t", j, angle[j]*180.0/PI);
// printf("\n");
/* update neighbour list */
for (j=0; j<n; j++)
{
neigh_table[j] = obstacle[i].neighb[j];
dist_table[j] = obstacle[i].eqdist[j];
}
for (j=0; j<n; j++)
{
obstacle[i].neighb[j] = neigh_table[table[j]];
obstacle[i].eqdist[j] = dist_table[table[j]];
}
/* build list of triangles */
if (FILL_OBSTACLE_TRIANGLES)
{
// printf("Building triangle %i\n", n_otriangles);
for (j=0; j<n-1; j++) if (angle[j+1] - angle[j] < PI)
{
otriangle[n_otriangles].i[0] = i;
otriangle[n_otriangles].i[1] = obstacle[i].neighb[j];
otriangle[n_otriangles].i[2] = obstacle[i].neighb[j+1];
n_otriangles++;
}
if (angle[0] - angle[n-1] + DPI < PI)
{
otriangle[n_otriangles].i[0] = i;
otriangle[n_otriangles].i[1] = obstacle[i].neighb[n-1];
otriangle[n_otriangles].i[2] = obstacle[i].neighb[0];
n_otriangles++;
}
}
}
/* initialize triangle areas */
for (i=0; i<n_otriangles; i++)
otriangle[i].area0 = otriangle_area(obstacle, otriangle[i]);
/* build facet table */
printf("Initializing facets from %i triangles\n", n_otriangles);
if (FILL_OBSTACLE_TRIANGLES) n_ofacets = init_obstacle_facets(otriangle, ofacet);
free(connected);
printf("Facets initialized\n");
return(n_ofacets);
}
void init_obstacle_config(t_obstacle obstacle[NMAXOBSTACLES], t_otriangle otriangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES], t_ofacet ofacet[NMAXOBSTACLES])
/* initialise circular obstacle configuration */
{
int i, j, n, jmin, jmax, nx, ny, ntot;
double x, y, dx, dy, width, lpocket, xmid = 0.5*(BCXMIN + BCXMAX), radius, c;
t_point *point;
/* set default rotation to 0 */
for (i=0; i<NMAXOBSTACLES; i++)
{
obstacle[i].omega = 0.0;
obstacle[i].angle = 0.0;
obstacle[i].oscillate = 0;
obstacle[i].vx = 0.0;
obstacle[i].vy = 0.0;
obstacle[i].mass = OBSTACLE_MASS;
}
switch (OBSTACLE_PATTERN) {
case (O_CORNERS):
{
n = 0;
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
{
obstacle[n].xc = BCXMIN + ((double)i)*(BCXMAX - BCXMIN);
obstacle[n].yc = BCYMIN + ((double)j)*(BCYMAX - BCYMIN);
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_GALTON_BOARD):
{
dy = (YMAX - YMIN)/((double)NGRIDX + 3);
dx = dy/cos(PI/6.0);
n = 0;
for (i = 0; i < NGRIDX + 1; i++)
for (j = 0; j < i; j++)
{
obstacle[n].yc = YMAX - ((double)i)*dy;
obstacle[n].xc = ((double)j - 0.5*(double)i + 0.5)*dx;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_GALTON_BOARD_WIDE):
{
dy = (YMAX - YMIN)/((double)NGRIDX + 3);
dx = dy/cos(PI/6.0);
n = 0;
for (i = 1; i < NGRIDX + 1; i++)
for (j = -5; j < i+5; j++)
{
obstacle[n].yc = YMAX - ((double)i)*dy;
obstacle[n].xc = ((double)j - 0.5*(double)i + 0.5)*dx;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_GENUS_TWO):
{
n = 0;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
{
obstacle[n].xc = BCXMIN + 0.5*((double)i)*(BCXMAX - BCXMIN);
obstacle[n].yc = BCYMIN + 0.5*((double)j)*(BCYMAX - BCYMIN);
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_POOL_TABLE):
{
lpocket = 0.1;
width = 0.5*MU;
radius = 2.0*width;
add_obstacle(BCXMIN + lpocket, BCYMIN - width, radius, obstacle);
add_obstacle(xmid - lpocket, BCYMIN - width, radius, obstacle);
add_obstacle(xmid + lpocket, BCYMIN - width, radius, obstacle);
add_obstacle(BCXMAX - lpocket, BCYMIN - width, radius, obstacle);
add_obstacle(BCXMAX + width, BCYMIN + lpocket, radius, obstacle);
add_obstacle(BCXMAX + width, BCYMAX - lpocket, radius, obstacle);
add_obstacle(BCXMIN + lpocket, BCYMAX + width, radius, obstacle);
add_obstacle(xmid - lpocket, BCYMAX + width, radius, obstacle);
add_obstacle(xmid + lpocket, BCYMAX + width, radius, obstacle);
add_obstacle(BCXMAX - lpocket, BCYMAX + width, radius, obstacle);
add_obstacle(BCXMIN - width, BCYMIN + lpocket, radius, obstacle);
add_obstacle(BCXMIN - width, BCYMAX - lpocket, radius, obstacle);
break;
}
case (O_HLINE_HOLE_SPOKES):
{
radius = 2.0*MU;
for (i=-3; i<4; i+=2)
add_obstacle(0.5*(double)i, YMIN + 0.3, radius, obstacle);
break;
}
case (O_CIRCLE):
{
n = 0;
obstacle[n].xc = 0.0;
obstacle[n].yc = 0.0;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
nobstacles = 1;
break;
}
case (O_FOUR_CIRCLES):
{
n = 0;
obstacle[n].xc = -1.5;
obstacle[n].yc = -0.5;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
obstacle[n].xc = -0.5;
obstacle[n].yc = 0.5;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
obstacle[n].xc = 0.5;
obstacle[n].yc = -0.5;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
obstacle[n].xc = 1.5;
obstacle[n].yc = 0.5;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
nobstacles = 4;
break;
}
case (O_HEX):
{
dx = (OBSXMAX - OBSXMIN)/(double)NOBSX;
dy = (OBSYMAX - OBSYMIN)/(double)NOBSY;
n = 0;
if ((ADD_FIXED_SEGMENTS)&&(SEGMENT_PATTERN == S_CYLINDER))
/* pseudo-cylindrical boundary conditions (e.g. for Hall effect) */
{
jmin = 1;
jmax = NOBSY-1;
}
else
{
jmin = 0;
jmax = NOBSY;
}
for (i=0; i<NOBSX; i++)
for (j=jmin; j<jmax; j++)
{
obstacle[n].xc = OBSXMIN + ((double)i + 0.25)*dx;
obstacle[n].yc = OBSYMIN + ((double)j + 0.5)*dy;
if (j%2 == 1) obstacle[n].xc += 0.5*dx;
if (obstacle[n].xc > OBSXMAX) obstacle[n].xc += (OBSXMAX - OBSXMIN);
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_SQUARE):
{
dx = (OBSXMAX - OBSXMIN)/(double)NOBSX;
dy = (OBSYMAX - OBSYMIN)/(double)NOBSY;
n = 0;
if ((ADD_FIXED_SEGMENTS)&&(SEGMENT_PATTERN == S_CYLINDER))
/* pseudo-cylindrical boundary conditions (e.g. for Hall effect) */
{
jmin = 1;
jmax = NOBSY-1;
}
else
{
jmin = 0;
jmax = NOBSY;
}
for (i=0; i<NOBSX; i++)
for (j=jmin; j<jmax; j++)
{
obstacle[n].xc = OBSXMIN + ((double)i + 0.25)*dx;
obstacle[n].yc = OBSYMIN + ((double)j + 0.5)*dy;
if (obstacle[n].xc > OBSXMAX) obstacle[n].xc += (OBSXMAX - OBSXMIN);
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
obstacle[n].chessboard = (i+j)%BDRY_PINNING_STEP;
n++;
}
nobstacles = n;
break;
}
case (O_SQUARE_TWOMASSES):
{
dx = (XMAX - XMIN)/(double)NOBSX;
dy = (YMAX - YMIN)/(double)NOBSY;
n = 0;
if ((ADD_FIXED_SEGMENTS)&&(SEGMENT_PATTERN == S_CYLINDER))
/* pseudo-cylindrical boundary conditions (e.g. for Hall effect) */
{
jmin = 1;
jmax = NOBSY-1;
}
else
{
jmin = 0;
jmax = NOBSY;
}
for (i=0; i<NOBSX; i++)
for (j=jmin; j<jmax; j++)
{
obstacle[n].xc = XMIN + ((double)i + 0.25)*dx;
obstacle[n].yc = YMIN + ((double)j + 0.5)*dy;
if (obstacle[n].xc > XMAX) obstacle[n].xc += (XMAX - XMIN);
obstacle[n].radius = OBSTACLE_RADIUS;
if ((i+j)%2 == 0)
{
obstacle[n].radius *= sqrt(2.0);
obstacle[n].mass *= 2.0;
}
obstacle[n].active = 1;
obstacle[n].chessboard = (i+j)%BDRY_PINNING_STEP;
n++;
}
nobstacles = n;
break;
}
case (O_SIDES):
{
n = 0;
for (i = 0; i < 9; i++)
for (j = 0; j < 5; j++)
if ((i == 0)||(i == 8)||(j == 0)||(j == 4))
{
obstacle[n].xc = BCXMIN + 0.125*((double)i)*(BCXMAX - BCXMIN);
obstacle[n].yc = BCYMIN + 0.25*((double)j)*(BCYMAX - BCYMIN);
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_SIDES_B):
{
n = 0;
nx = 16;
ny = 8;
dx = (BCXMAX - BCXMIN)/(double)nx;
dy = (BCYMAX - BCYMIN)/(double)ny;
for (i = 0; i < nx+1; i++)
for (j = 0; j < ny+1; j++)
if ((i == 0)||(i == nx)||(j == 0)||(j == ny))
{
obstacle[n].xc = BCXMIN + (double)i*dx;
obstacle[n].yc = BCYMIN + (double)j*dy;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_SIEVE):
{
n = 0;
width = 1.0;
dx = width/12.0;
dy = dx*0.6;
for (i = 0; i < 13; i++)
{
obstacle[n].xc = -1.0 + (double)i*dx;
obstacle[n].yc = 0.7 - (double)i*dy;
obstacle[n].radius = 0.95*OBSTACLE_RADIUS;
obstacle[n].omega = 1.25*OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
for (i = 0; i < 13; i++)
{
obstacle[n].xc = -1.0 + (double)i*dx;
obstacle[n].yc = 0.2 - (double)i*dy;
obstacle[n].radius = 1.2*OBSTACLE_RADIUS;
obstacle[n].omega = OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
for (i = 0; i < 13; i++)
{
obstacle[n].xc = -1.0 + (double)i*dx;
obstacle[n].yc = -0.3 - (double)i*dy;
obstacle[n].radius = 1.6*OBSTACLE_RADIUS;
obstacle[n].omega = 0.75*OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
nobstacles = n;
break;
}
case (O_SIEVE_B):
{
n = 0;
width = 1.2;
dx = width/14.0;
dy = dx*0.6;
for (i = 0; i < 15; i++)
{
obstacle[n].xc = -1.2 + (double)i*dx;
obstacle[n].yc = 0.85 - (double)i*dy;
obstacle[n].radius = 0.9*OBSTACLE_RADIUS;
obstacle[n].omega = 1.25*OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
for (i = 0; i < 15; i++)
{
obstacle[n].xc = -1.2 + (double)i*dx;
obstacle[n].yc = 0.35 - (double)i*dy;
obstacle[n].radius = 1.2*OBSTACLE_RADIUS;
obstacle[n].omega = OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
for (i = 0; i < 15; i++)
{
obstacle[n].xc = -1.2 + (double)i*dx;
obstacle[n].yc = -0.15 - (double)i*dy;
obstacle[n].radius = 1.6*OBSTACLE_RADIUS;
obstacle[n].omega = 0.75*OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
if (RATTLE_OBSTACLES) for (i = 0; i < n; i++)
{
obstacle[i].oscillate = 1;
obstacle[i].period = 8;
obstacle[i].amplitude = 0.0015;
obstacle[i].phase = (double)i*DPI/10.0;
}
nobstacles = n;
break;
}
case (O_SIEVE_LONG):
{
n = 0;
ntot = 36;
width = 1.2;
dx = (XMAX - XMIN)/(double)ntot;
dy = dx*0.3;
for (i = 0; i < ntot + 1; i++)
{
obstacle[n].xc = XMIN + (double)i*dx;
obstacle[n].yc = 0.6 - (double)i*dy;
obstacle[n].radius = OBSTACLE_RADIUS*(2.4 - 1.6*(double)i/(double)ntot);
obstacle[n].omega = OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
if (RATTLE_OBSTACLES) for (i = 0; i < n; i++)
{
obstacle[i].oscillate = 1;
obstacle[i].period = 8;
obstacle[i].amplitude = 0.0018;
obstacle[i].phase = (double)i*DPI/10.0;
}
nobstacles = n;
break;
}
case (O_SIEVE_LONG_B):
{
n = 0;
ntot = 45;
width = 1.2;
dx = (XMAX - XMIN)/(double)ntot;
dy = dx*0.2;
x = XMIN;
y = 0.4;
c = 0.019;
while (x < XMAX)
{
obstacle[n].xc = x;
obstacle[n].yc = y;
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].omega = OBSTACLE_OMEGA;
obstacle[n].active = 1;
x += dx;
y -= dy;
dx += c*dx;
dy += c*dy;
n++;
}
if (RATTLE_OBSTACLES) for (i = 0; i < n; i++)
{
obstacle[i].oscillate = 1;
obstacle[i].period = 8;
obstacle[i].amplitude = 0.0018;
obstacle[i].phase = (double)i*DPI/10.0;
}
nobstacles = n;
break;
}
case (O_SIEVE_VIDEO1400):
{
n = 0;
ntot = 28;
width = 1.2;
dx = 2.9/(double)ntot;
dy = dx*0.3;
for (i = 0; i < ntot + 1; i++)
{
obstacle[n].xc = 2.5 + (double)i*dx;
obstacle[n].yc = 0.6 - (double)i*dy;
obstacle[n].radius = OBSTACLE_RADIUS*(2.4 - 1.6*(double)i/(double)ntot);
obstacle[n].omega = OBSTACLE_OMEGA;
obstacle[n].active = 1;
n++;
}
if (RATTLE_OBSTACLES) for (i = 0; i < n; i++)
{
obstacle[i].oscillate = 1;
obstacle[i].period = 8;
obstacle[i].amplitude = 0.0018;
obstacle[i].phase = (double)i*DPI/10.0;
}
nobstacles = n;
break;
}
case (O_POISSON_DISC):
{
point = (t_point *)malloc(NMAXOBSTACLES*sizeof(t_point));
nobstacles = generate_poisson_discs(point, NMAXOBSTACLES, OBSXMIN, OBSXMAX, OBSYMIN, OBSYMAX, OBSTACLE_PISC_DISTANCE);
printf("Generated %i obstacles\n", nobstacles);
for (n=0; n<nobstacles; n++)
{
// printf("Obstacle %i at (%.3lg, %.3lg)\n", n, point[n].xc, point[n].yc);
obstacle[n].xc = point[n].xc;
obstacle[n].yc = point[n].yc;
if (obstacle[n].xc > XMAX) obstacle[n].xc += (XMAX - XMIN);
obstacle[n].radius = OBSTACLE_RADIUS;
obstacle[n].active = 1;
}
free(point);
break;
}
default:
{
printf("Function init_obstacle_config not defined for this pattern \n");
}
}
if (CHARGE_OBSTACLES)
for (n=0; n<nobstacles; n++) obstacle[n].charge = OBSTACLE_CHARGE;
for (n=0; n<nobstacles; n++)
{
obstacle[n].omega0 = obstacle[n].omega;
obstacle[n].xc0 = obstacle[n].xc;
obstacle[n].yc0 = obstacle[n].yc;
obstacle[n].nneighb = 0;
}
if (COUPLE_OBSTACLES) init_obstacle_coupling(obstacle, otriangle, ofacet);
}
void add_rotated_angle_to_segments(double x1, double y1, double x2, double y2, double width, int center, t_segment segment[NMAXSEGMENTS], int group)
/* add four segments forming a rectangle, specified by two adjacent corners and width */
{
double tx, ty, ux, uy, norm, x3, y3, x4, y4, angle;
int i, n = nsegments;
tx = x2 - x1;
ty = y2 - y1;
norm = module2(tx, ty);
tx = tx/norm;
ty = ty/norm;
if (center)
{
x2 -= 0.5*width*ty;
y2 += 0.5*width*tx;
x1 -= 0.5*width*ty;
y1 += 0.5*width*tx;
}
x3 = x2 + width*ty;
y3 = y2 - width*tx;
x4 = x1 + width*ty;
y4 = y1 - width*tx;
if (nsegments + 4 < NMAXSEGMENTS)
{
segment[n].x1 = x1;
segment[n].y1 = y1;
segment[n].x2 = x2;
segment[n].y2 = y2;
segment[n+1].x1 = x2;
segment[n+1].y1 = y2;
segment[n+1].x2 = x3;
segment[n+1].y2 = y3;
segment[n+2].x1 = x3;
segment[n+2].y1 = y3;
segment[n+2].x2 = x4;
segment[n+2].y2 = y4;
segment[n+3].x1 = x4;
segment[n+3].y1 = y4;
segment[n+3].x2 = x1;
segment[n+3].y2 = y1;
segment[n].angle1 = -PID;
segment[n].angle2 = 0.0;
segment[n+1].angle1 = PI;
segment[n+1].angle2 = 1.5*PI;
segment[n+2].angle1 = PID;
segment[n+2].angle2 = PI;
segment[n+3].angle1 = 0.0;
segment[n+3].angle2 = PID;
angle = argument(tx, ty) + PI;
for (i=0; i<4; i++)
{
segment[n+i].angle1 += angle;
segment[n+i].angle2 += angle;
segment[n+i].concave = 1;
segment[n+i].group = group;
}
nsegments += 4;
}
else printf("Warning: NMAXSEGMENTS too small\n");
}
void add_rectangle_to_segments(double x1, double y1, double x2, double y2, t_segment segment[NMAXSEGMENTS], int group)
/* add four segements forming a rectangle to linear obstacle configuration */
{
int i, n = nsegments, nplus, nminus;
if (nsegments + 4 < NMAXSEGMENTS)
{
segment[n].x1 = x1;
segment[n].y1 = y1;
segment[n].x2 = x2;
segment[n].y2 = y1;
segment[n+1].x1 = x2;
segment[n+1].y1 = y1;
segment[n+1].x2 = x2;
segment[n+1].y2 = y2;
segment[n+2].x1 = x2;
segment[n+2].y1 = y2;
segment[n+2].x2 = x1;
segment[n+2].y2 = y2;
segment[n+3].x1 = x1;
segment[n+3].y1 = y2;
segment[n+3].x2 = x1;
segment[n+3].y2 = y1;
segment[n].angle1 = -PID;
segment[n].angle2 = 0.0;
segment[n+1].angle1 = PI;
segment[n+1].angle2 = 1.5*PI;
segment[n+2].angle1 = PID;
segment[n+2].angle2 = PI;
segment[n+3].angle1 = 0.0;
segment[n+3].angle2 = PID;
for (i=0; i<4; i++)
{
segment[n+i].concave = 1;
segment[n+i].group = group;
}
nsegments += 4;
}
else printf("Warning: NMAXSEGMENTS too small\n");
}
void add_circle_to_segments(double x, double y, double r, int nsegs, double angle0, t_segment segment[NMAXSEGMENTS], int group)
/* add segments forming a circle/polygon to linear obstacle configuration */
{
int i, n = nsegments, nplus, nminus;
double angle;
angle = DPI/(double)nsegs;
if (nsegments + nsegs < NMAXSEGMENTS)
{
for (i=0; i<nsegs; i++)
{
segment[n+i].x1 = x + r*cos(((double)i)*angle + angle0*PID);
segment[n+i].y1 = y - r*sin(((double)i)*angle + angle0*PID);
segment[n+i].x2 = x + r*cos(((double)(i+1))*angle + angle0*PID);
segment[n+i].y2 = y - r*sin(((double)(i+1))*angle + angle0*PID);
segment[n+i].angle1 = -((double)i + 0.5)*angle - angle0*PID;
segment[n+i].angle2 = -((double)i - 0.5)*angle - angle0*PID;
while (segment[n+i].angle1 < 0.0) segment[n+i].angle1 += DPI;
while (segment[n+i].angle2 < segment[n+i].angle1) segment[n+i].angle2 += DPI;
segment[n+i].concave = 1;
segment[n+i].group = group;
}
nsegments += nsegs;
}
else printf("Warning: NMAXSEGMENTS too small\n");
}
void add_tree_to_segments(double x, double y, double r, double lfoot[2], double rfoot[2], t_segment segment[NMAXSEGMENTS], int group)
/* add segments forming a tree to linear obstacle configuration */
{
int i, n = nsegments, nplus, nminus, nadd;
double angle, h, h1, h2, h3, x1, x2, xp1, xp2, dx1, dx2, xt, yt;
angle = PI/3.0;
h = r*tan(angle);
h1 = 0.56*h;
h2 = h1;
dx1 = h1/tan(angle);
dx2 = 0.5*dx1;
x1 = r - dx1;
xp1 = x1 + dx2;
x2 = xp1 - dx1;
xp2 = x2 + dx2;
h3 = xp2*tan(angle);
xt = 0.5*dx2;
yt = dx1;
nadd = 15;
if (nsegments + nadd < NMAXSEGMENTS)
{
/* bottom left */
segment[n].x1 = x - r;
segment[n].y1 = y;
segment[n].angle1 = PID + angle;
segment[n].angle2 = PI + PID;
segment[n].concave = 1;
n++;
/* level 1 */
segment[n].x1 = x - x1;
segment[n].y1 = y + h1;
segment[n].concave = 0;
n++;
segment[n].x1 = x - xp1;
segment[n].y1 = y + h1;
segment[n].angle1 = PID + angle;
segment[n].angle2 = PI + PID;
segment[n].concave = 1;
n++;
/* level 2 */
segment[n].x1 = x - x2;
segment[n].y1 = y + h1 + h2;
segment[n].concave = 0;
n++;
segment[n].x1 = x - xp2;
segment[n].y1 = y + h1 + h2;
segment[n].angle1 = PID + angle;
segment[n].angle2 = PI + PID;
segment[n].concave = 1;
n++;
/* top */
segment[n].x1 = x;
segment[n].y1 = y + h1 + h2 + h3;
segment[n].angle1 = PID - angle;
segment[n].angle2 = PID + angle;
segment[n].concave = 1;
n++;
/* level 2 */
segment[n].x1 = x + xp2;
segment[n].y1 = y + h1 + h2;
segment[n].angle1 = -PID;
segment[n].angle2 = angle;
segment[n].concave = 1;
n++;
segment[n].x1 = x + x2;
segment[n].y1 = y + h1 + h2;
segment[n].concave = 0;
n++;
/* level 1 */
segment[n].x1 = x + xp1;
segment[n].y1 = y + h1;
segment[n].angle1 = -PID;
segment[n].angle2 = angle;
segment[n].concave = 1;
n++;
segment[n].x1 = x + x1;
segment[n].y1 = y + h1;
segment[n].concave = 0;
n++;
/* bottom right */
segment[n].x1 = x + r;
segment[n].y1 = y;
segment[n].angle1 = -PID;
segment[n].angle2 = angle;
segment[n].concave = 1;
n++;
/*stem */
segment[n].x1 = x + xt;
segment[n].y1 = y;
segment[n].concave = 0;
n++;
segment[n].x1 = x + xt;
segment[n].y1 = y - yt;
segment[n].angle1 = -PID;
segment[n].angle2 = 0.0;
segment[n].concave = 1;
n++;
segment[n].x1 = x - xt;
segment[n].y1 = y - yt;
segment[n].angle1 = PI;
segment[n].angle2 = 3.0*PID;
segment[n].concave = 1;
n++;
segment[n].x1 = x - xt;
segment[n].y1 = y;
segment[n].concave = 0;
n++;
rfoot[0] = x + xt;
rfoot[1] = y - yt;
lfoot[0] = x - xt;
lfoot[1] = y - yt;
for (i=0; i<nadd-1; i++)
{
segment[nsegments+i].x2 = segment[nsegments+i+1].x1;
segment[nsegments+i].y2 = segment[nsegments+i+1].y1;
}
segment[nsegments+nadd-1].x2 = segment[nsegments].x1;
segment[nsegments+nadd-1].y2 = segment[nsegments].y1;
for (i=0; i<nadd; i++)
{
// segment[nsegments+i].concave = 1;
segment[nsegments+i].group = group;
}
nsegments += nadd;
}
else printf("Warning: NMAXSEGMENTS too small\n");
}
double nozzle_width(double x, double width, int nozzle_shape)
/* width of bell-shaped nozzle */
{
double lam = 0.5*LAMBDA, a, b;
if (x >= 0.0) return(width);
else switch (nozzle_shape) {
case (NZ_STRAIGHT): return(width);
case (NZ_BELL): return(sqrt(width*width - 0.5*x));
case (NZ_GLAS): return(sqrt(width*width - 1.2*x) + 1.0*x);
case (NZ_CONE): return(width - (sqrt(width*width + 0.5) - width)*x);
case (NZ_TRUMPET): return(width + (sqrt(width*width + LAMBDA)-width)*x*x);
case (NZ_BROAD):
{
if (-x < 0.1) return(width - (0.5 - width)*x/0.1);
else return(0.5);
}
case (NZ_DELAVAL):
{
a = 1.5;
b = 0.05;
return(sqrt(width*width - 0.5*x) + a*b*x*(1.0 + x)/(b + x*x));
// a = (sqrt(width*width+0.5) - width)/sqrt(0.5);
// c = (a*sqrt(0.5*h) - width)/(h*h);
// if (-x < h) return(width + c*x*x);
// else return(width + a * sqrt(-0.5*x));
}
default: return(0.0);
}
}
void add_rocket_to_segments(t_segment segment[NMAXSEGMENTS], double x0, double y0, int rocket_shape, int nozzle_shape, int nsides, int group)
/* add one or several rocket_shaped set of segments */
{
int i, j, cycle = 0, nsegments0;
double angle, dx, x1, y1, x2, y2, nozx, nozy, a, b, c, w;
nsegments0 = nsegments;
/* compute intersection point of nozzle and ellipse */
angle = -PID + DPI/(double)NPOLY;
nozx = 0.7*LAMBDA*cos(angle);
nozy = y0 + YMIN + LAMBDA*(1.7 + 0.7*sin(angle));
/* form of combustion chamber */
switch (rocket_shape) {
case (RCK_DISC): /* circular chamber */
{
for (i=1; i<NPOLY-1; i++)
{
angle = -PID + (double)i*DPI/(double)NPOLY;
x1 = x0 + 0.7*LAMBDA*cos(angle);
y1 = y0 + YMIN + LAMBDA*(1.7 + 0.7*sin(angle));
angle = -PID + (double)(i+1)*DPI/(double)NPOLY;
x2 = x0 + 0.7*LAMBDA*cos(angle);
y2 = y0 + YMIN + LAMBDA*(1.7 + 0.7*sin(angle));
add_rotated_angle_to_segments(x1, y1, x2, y2, 0.02, 0, segment, 0);
}
break;
}
case (RCK_RECT): /* rectangular chamber */
{
/* dimensions chosen to have same area as circular chamber */
a = 0.5*LAMBDA;
b = 0.49*PI*LAMBDA;
add_rotated_angle_to_segments(x0+nozx, nozy, x0+a, nozy, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+a, nozy, x0+a, nozy+b, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+a, nozy+b, x0-a, nozy+b, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-a, nozy+b, x0-a, nozy, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-a, nozy, x0-nozx, nozy, 0.02, 0, segment, 0);
break;
}
case (RCK_RECT_HAT): /* rectangular chamber with a hat */
{
a = 0.5*LAMBDA;
b = (0.49*PI-0.25)*LAMBDA;
add_rotated_angle_to_segments(x0+nozx, nozy, x0+a, nozy, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+a, nozy, x0+a, nozy+b, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+a, nozy+b, x0, nozy+b+a, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0, nozy+b+a, x0-a, nozy+b, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-a, nozy+b, x0-a, nozy, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-a, nozy, x0-nozx, nozy, 0.02, 0, segment, 0);
break;
}
case (RCK_RECT_BAR): /* rectangular chamber with a hat and separating bar */
{
a = 0.5*LAMBDA;
b = (0.49*PI-0.25)*LAMBDA;
c = 0.5*a;
w = 0.025;
add_rotated_angle_to_segments(x0+nozx, nozy, x0+nozx, nozy+0.5*c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+nozx, nozy+0.5*c, x0+nozx+0.5*c, nozy+c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+nozx+0.5*c, nozy+c, x0+a, nozy+c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+a, nozy+c, x0+a, nozy+b+c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0+a, nozy+b+c, x0, nozy+b+a+c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-w, nozy+a+c, x0-w, nozy+b+a+c, 2.0*w, 0, segment, 0);
add_rotated_angle_to_segments(x0, nozy+b+a+c, x0-a, nozy+b+c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-a, nozy+b+c, x0-a, nozy+c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-a, nozy+c, x0-nozx-0.5*c, nozy+c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-nozx-0.5*c, nozy+c, x0-nozx+w, nozy+0.5*c, 0.02, 0, segment, 0);
add_rotated_angle_to_segments(x0-nozx+w, nozy+0.5*c, x0-nozx+w, nozy, 0.02, 0, segment, 0);
break;
}
}
dx = LAMBDA/(double)(nsides);
/* nozzle */
if (nozzle_shape != NZ_NONE)
{
for (i=0; i<nsides; i++)
{
y1 = y0 - LAMBDA + dx*(double)(i-1);
x1 = x0 + nozzle_width(y1 - y0, nozx, nozzle_shape);
y2 = y1 + dx;
x2 = x0 + nozzle_width(y2 - y0, nozx, nozzle_shape);
add_rotated_angle_to_segments(x1, y1 + YMIN + LAMBDA, x2, y2 + YMIN + LAMBDA, 0.02, 0, segment, 0);
}
add_rotated_angle_to_segments(x2, y2 + YMIN + LAMBDA, x0 + nozx, nozy, 0.02, 0, segment, 0);
for (i=0; i<nsides; i++)
{
y1 = y0 - LAMBDA + dx*(double)(i-1);
x1 = x0 - nozzle_width(y1 - y0, nozx, nozzle_shape);
y2 = y1 + dx;
x2 = x0 - nozzle_width(y2 - y0, nozx, nozzle_shape);
add_rotated_angle_to_segments(x1, y1 + YMIN + LAMBDA, x2, y2 + YMIN + LAMBDA, 0.02, 0, segment, 0);
}
add_rotated_angle_to_segments(x2, y2 + YMIN + LAMBDA, x0 - nozx, nozy, 0.02, 0, segment, 0);
}
for (i=nsegments0; i<nsegments; i++) segment[i].inactivate = 0;
/* closing segment */
segment[nsegments].x1 = x0 - nozx;
segment[nsegments].y1 = nozy;
segment[nsegments].x2 = x0 + nozx;
segment[nsegments].y2 = nozy;
segment[nsegments].inactivate = 1;
nsegments++;
/* set group of segments */
for (i=nsegments0; i<nsegments; i++) segment[i].group = group;
}
int init_maze_segments(t_segment segment[NMAXSEGMENTS], int diag)
/* init segments forming a maze */
{
t_maze* maze;
int i, j, n;
double x1, y1, x2, y2, dx, dy, padding = 0.02, width = MAZE_WIDTH;
maze = (t_maze *)malloc(NXMAZE*NYMAZE*sizeof(t_maze));
init_maze(maze);
/* build walls of maze */
dx = (YMAX - YMIN - 2.0*padding)/(double)(NXMAZE);
dy = (YMAX - YMIN - 2.0*padding)/(double)(NYMAZE);
for (i=0; i<NXMAZE; i++)
for (j=0; j<NYMAZE; j++)
{
n = nmaze(i, j);
x1 = YMIN + padding + (double)i*dx + MAZE_XSHIFT;
y1 = YMIN + padding + (double)j*dy;
if (diag)
{
if (((i>0)||(j<NYMAZE-1))&&(maze[n].west)) add_rectangle_to_segments(x1, y1, x1 - width, y1 + dy, segment, 0);
}
else if (((i>0)||(j!=NYMAZE/2))&&(maze[n].west)) add_rectangle_to_segments(x1, y1, x1 - width, y1 + dy, segment, 0);
if (maze[n].south) add_rectangle_to_segments(x1, y1, x1 + dx, y1 - width, segment, 0);
}
/* top side of maze */
add_rectangle_to_segments(YMIN + padding + MAZE_XSHIFT, YMAX - padding, YMAX - padding + MAZE_XSHIFT, YMAX - padding - width, segment, 0);
/* right side of maze */
x1 = YMAX - padding + MAZE_XSHIFT;
if (diag)
{
y1 = YMIN + padding + dy;
add_rectangle_to_segments(x1, y1, x1 - width, YMAX + 10.0, segment, 0);
}
else
{
y1 = YMIN + padding + dy*((double)NYMAZE/2);
add_rectangle_to_segments(x1, YMIN - 1.0, x1 - width, y1 - dy, segment, 0);
add_rectangle_to_segments(x1, y1, x1 - width, YMAX + 1.0, segment, 0);
}
/* left side of maze */
x1 = YMIN + padding + MAZE_XSHIFT;
add_rectangle_to_segments(x1, YMIN - 1.0, x1 - width, YMIN + padding, segment, 0);
add_rectangle_to_segments(x1, YMAX - padding, x1 - width, YMAX + 10.0, segment, 0);
if (diag)
{
add_rotated_angle_to_segments(XMIN, YMAX - 0.5*dy, x1, YMAX - dy - 2.0*width, width, 0, segment, 0);
add_rectangle_to_segments(XMIN, YMAX - 0.5*dy, XMIN - width, YMAX + 10.0, segment, 0);
}
free(maze);
}
void translate_segments(t_segment segment[NMAXSEGMENTS], double deltax[2], double deltay[2])
/* rotates the repelling segments by given angle */
{
int i, group;
for (i=0; i<nsegments; i++)
{
group = segment[i].group;
if (group == 0)
{
segment[i].x1 = segment[i].x01 + deltax[group] - SEGMENTS_X0;
segment[i].x2 = segment[i].x02 + deltax[group] - SEGMENTS_X0;
}
else
{
segment[i].x1 = segment[i].x01 + deltax[group] + SEGMENTS_X0;
segment[i].x2 = segment[i].x02 + deltax[group] + SEGMENTS_X0;
}
segment[i].y1 = segment[i].y01 + deltay[group] - SEGMENTS_Y0;
segment[i].y2 = segment[i].y02 + deltay[group] - SEGMENTS_Y0;
segment[i].c = segment[i].nx*segment[i].x1 + segment[i].ny*segment[i].y1;
}
}
void translate_one_segment(t_segment segment[NMAXSEGMENTS], int i, double deltax, double deltay)
/* translates the repelling segment by given vector */
{
segment[i].x1 += deltax;
segment[i].x2 += deltax;
segment[i].y1 += deltay;
segment[i].y2 += deltay;
segment[i].c = segment[i].nx*segment[i].x1 + segment[i].ny*segment[i].y1;
segment[i].xc += deltax;
segment[i].yc += deltay;
}
void rotate_one_segment(t_segment segment[NMAXSEGMENTS], int i, double dalpha, double xc, double yc)
/* rotates the repelling segment by given angle around (xc, yc) */
{
double ca, sa, x, y, nx, ny;
ca = cos(dalpha);
sa = sin(dalpha);
x = segment[i].x1 - xc;
y = segment[i].y1 - yc;
segment[i].x1 = xc + x*ca - y*sa;
segment[i].y1 = yc + x*sa + y*ca;
x = segment[i].x2 - xc;
y = segment[i].y2 - yc;
segment[i].x2 = xc + x*ca - y*sa;
segment[i].y2 = yc + x*sa + y*ca;
segment[i].xc = 0.5*(segment[i].x1 + segment[i].x2);
segment[i].yc = 0.5*(segment[i].y1 + segment[i].y2);
nx = segment[i].nx;
ny = segment[i].ny;
segment[i].nx = ca*nx - sa*ny;
segment[i].ny = sa*nx + ca*ny;
segment[i].c = segment[i].nx*segment[i].x1 + segment[i].ny*segment[i].y1;
if (segment[i].concave)
{
segment[i].angle1 += dalpha;
segment[i].angle2 += dalpha;
while (segment[i].angle1 > DPI)
{
segment[i].angle1 -= DPI;
segment[i].angle2 -= DPI;
}
while (segment[i].angle2 < 0.0)
{
segment[i].angle1 += DPI;
segment[i].angle2 += DPI;
}
}
}
int add_shovel_to_belt(double x, double y, double angle, double size, double width, t_segment segment[NMAXSEGMENTS], t_belt belt)
/* add showver to conveyor belt */
{
int i, n, iminus, iplus;
double sq2, angle1, angle2, cg, sg, t;
if (nsegments + 6 > NMAXSEGMENTS)
{
printf("NMAXSEGMENTS too small");
return(0);
}
n = nsegments;
// sq2 = sqrt(2.0)/2.0;
cg = cos(PI/6.0);
sg = sin(PI/6.0);
t = size + width*(cg - 1.0)/sg;
segment[n].x1 = 0.0;
segment[n].y1 = 0.0;
segment[n+1].x1 = 0.0;
segment[n+1].y1 = size;
segment[n+2].x1 = size*sg;
segment[n+2].y1 = size*(1.0+cg);
segment[n+3].x1 = size*sg + width*cg;
segment[n+3].y1 = size*(1.0+cg) - width*sg;
segment[n+4].x1 = width;
segment[n+4].y1 = size*(1.0 + cg) - width*sg - t*cg;
// segment[n+2].x1 = size*sq2;
// segment[n+2].y1 = size*(1.0+sq2);
//
// segment[n+3].x1 = size*sq2 + width*sq2;
// segment[n+3].y1 = size*(1.0+sq2) - width*sq2;
//
// segment[n+4].x1 = width;
// segment[n+4].y1 = size + width*(1.0 - sqrt(2.0));
segment[n+5].x1 = width;
segment[n+5].y1 = 0.0;
for (i=0; i<6; i++)
{
segment[n+i].x2 = segment[n+i+1].x1;
segment[n+i].y2 = segment[n+i+1].y1;
}
segment[n+6].x2 = segment[n].x1;
segment[n+6].y2 = segment[n].y1;
for (i=n; i<n+6; i++)
{
segment[i].inactivate = 0;
segment[i].conveyor = 0;
segment[i].concave = 1;
}
/* deal with concave corners */
for (i=n; i<n+6; i++)
{
iminus = i-1;
iplus = i+1;
if (iminus < n) iminus = n + 5;
if (iplus > n + 5) iplus = n;
angle1 = argument(segment[iplus].x1 - segment[i].x1, segment[iplus].y1 - segment[i].y1) + PID;
angle2 = argument(segment[i].x1 - segment[iminus].x1, segment[i].y1 - segment[iminus].y1) + PID;
if (angle2 < angle1) angle2 += DPI;
segment[i].angle1 = angle1;
segment[i].angle2 = angle2;
}
for (i=n; i<n+6; i++)
{
translate_one_segment(segment, i, x, y);
rotate_one_segment(segment, i, angle, x, y);
}
nsegments += 6;
return(1);
}
int add_conveyor_belt(double x1, double y1, double x2, double y2, double width, double speed, int nshovels, t_segment segment[NMAXSEGMENTS], t_belt conveyor_belt[NMAXBELTS])
/* add segments forming a conveyor belt */
{
double length, tx, ty, angle, angle1, angle2, beltlength, shovel_dist, shovel_pos, x, y, beta;
int i, n, iplus, iminus, shovel;
length = module2(x2 - x1, y2 - y1);
angle = argument(x2 - x1, y2 - y1);
if (angle < 0.0) angle += DPI;
tx = (x2 - x1)/length;
ty = (y2 - y1)/length;
n = nsegments;
if (nsegments + 14 > NMAXSEGMENTS)
{
printf("NMAXSEGMENTS too small");
return(0);
}
if (nbelts + 1 > NMAXBELTS)
{
printf("NMAXBELTS too small");
return(0);
}
segment[n].x1 = x1 - ty*width;
segment[n].y1 = y1 + tx*width;
for (i=0; i<7; i++)
{
segment[n+1+i].x1 = x2 + width*sin(-angle + (double)i*PI/6.0);
segment[n+1+i].y1 = y2 + width*cos(-angle + (double)i*PI/6.0);
}
for (i=7; i<14; i++)
{
segment[n+1+i].x1 = x1 + width*sin(-angle + (double)(i-1)*PI/6.0);
segment[n+1+i].y1 = y1 + width*cos(-angle + (double)(i-1)*PI/6.0);
}
for (i=0; i<13; i++)
{
segment[n+i].x2 = segment[n+i+1].x1;
segment[n+i].y2 = segment[n+i+1].y1;
}
segment[n+13].x2 = segment[n].x1;
segment[n+13].y2 = segment[n].y1;
for (i=n; i<n+14; i++)
{
segment[i].concave = 1;
segment[i].inactivate = 0;
segment[i].conveyor = 1;
segment[i].conveyor_speed = speed;
segment[i].align_torque = 1;
}
/* deal with concave corners */
for (i=n; i<n+14; i++)
{
iminus = i-1;
iplus = i+1;
if (iminus < n) iminus = n + 13;
if (iplus > n + 13) iplus = n;
angle1 = argument(segment[iplus].x1 - segment[i].x1, segment[iplus].y1 - segment[i].y1) + PID;
angle2 = argument(segment[i].x1 - segment[iminus].x1, segment[i].y1 - segment[iminus].y1) + PID;
if (angle2 < angle1) angle2 += DPI;
segment[i].angle1 = angle1;
segment[i].angle2 = angle2;
}
nsegments += 14;
conveyor_belt[nbelts].x1 = x1;
conveyor_belt[nbelts].x2 = x2;
conveyor_belt[nbelts].y1 = y1;
conveyor_belt[nbelts].y2 = y2;
conveyor_belt[nbelts].speed = speed;
conveyor_belt[nbelts].width = width;
conveyor_belt[nbelts].position = 0.0;
conveyor_belt[nbelts].length = length;
conveyor_belt[nbelts].angle = angle;
conveyor_belt[nbelts].tx = tx;
conveyor_belt[nbelts].ty = ty;
conveyor_belt[nbelts].nshovels = nshovels;
nbelts++;
if (nshovels > NMAXSHOVELS)
{
printf("NMAXSHOVELS too small");
conveyor_belt[nbelts].nshovels = 0;
return(0);
}
if (nshovels > 0)
{
beltlength = 2.0*length + DPI*width;
shovel_dist = beltlength/(double)nshovels;
shovel_pos = 0.0;
shovel = 0;
while (shovel_pos < length)
{
x = x1 - width*ty + shovel_pos*tx;
y = y1 + width*tx + shovel_pos*ty;
conveyor_belt[nbelts-1].shovel_segment[shovel] = nsegments;
add_shovel_to_belt(x, y, angle, width, 0.3*width, segment, conveyor_belt[nbelts-1]);
conveyor_belt[nbelts-1].shovel_pos[shovel] = shovel_pos;
shovel_pos += shovel_dist;
shovel++;
}
while (shovel_pos < length + width*PI)
{
beta = (shovel_pos - length)/width;
x = x2 + width*cos(angle + PID - beta);
y = y2 + width*sin(angle + PID - beta);
conveyor_belt[nbelts-1].shovel_segment[shovel] = nsegments;
add_shovel_to_belt(x, y, angle - beta, width, 0.3*width, segment, conveyor_belt[nbelts-1]);
conveyor_belt[nbelts-1].shovel_pos[shovel] = shovel_pos;
shovel_pos += shovel_dist;
shovel++;
}
while (shovel_pos < 2.0*length + width*PI)
{
x = x2 + width*ty - (shovel_pos - length - width*PI)*tx;
y = y2 - width*tx - (shovel_pos - length - width*PI)*ty;
conveyor_belt[nbelts-1].shovel_segment[shovel] = nsegments;
add_shovel_to_belt(x, y, angle - PI, width, 0.3*width, segment, conveyor_belt[nbelts-1]);
conveyor_belt[nbelts-1].shovel_pos[shovel] = shovel_pos;
shovel_pos += shovel_dist;
shovel++;
}
while (shovel_pos < beltlength)
{
beta = (shovel_pos - 2.0*length - width*PI)/width;
x = x1 + width*cos(angle - PID - beta);
y = y1 + width*sin(angle - PID - beta);
conveyor_belt[nbelts-1].shovel_segment[shovel] = nsegments;
add_shovel_to_belt(x, y, angle - PI - beta, width, 0.3*width, segment, conveyor_belt[nbelts-1]);
conveyor_belt[nbelts-1].shovel_pos[shovel] = shovel_pos;
shovel_pos += shovel_dist;
shovel++;
}
}
return(1);
}
void init_segment_config(t_segment segment[NMAXSEGMENTS], t_belt conveyor_belt[NMAXBELTS])
/* initialise linear obstacle configuration */
{
int i, j, cycle = 0, iminus, iplus, nsides, n, concave = 1;
double angle, angle2, dangle, dx, width, height, a, b, length, xmid = 0.5*(BCXMIN + BCXMAX), lpocket, r, x, x1, y1, x2, y2, nozx, nozy, y, yy, dy, ca, sa, padding;
double lfoot[NTREES][2], rfoot[NTREES][2];
/* set default to no conveyor, no torque */
for (i=0; i<NMAXSEGMENTS; i++)
{
segment[i].conveyor = 0;
segment[i].align_torque = 0;
}
switch (SEGMENT_PATTERN) {
case (S_RECTANGLE):
{
segment[0].x1 = BCXMIN;
segment[0].y1 = BCYMAX;
segment[1].x1 = BCXMIN;
segment[1].y1 = BCYMIN;
segment[2].x1 = BCXMAX;
segment[2].y1 = BCYMIN;
segment[3].x1 = BCXMAX;
segment[3].y1 = BCYMAX;
cycle = 1;
nsegments = 4;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 0;
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_CUP):
{
angle = APOLY*PID;
dx = (BCYMAX - BCYMIN)/tan(angle);
segment[0].x1 = BCXMIN;
segment[0].y1 = BCYMAX;
segment[1].x1 = BCXMIN + dx;
segment[1].y1 = BCYMIN;
segment[2].x1 = BCXMAX - dx;
segment[2].y1 = BCYMIN;
segment[3].x1 = BCXMAX;
segment[3].y1 = BCYMAX;
cycle = 1;
nsegments = 4;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 0;
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_HOURGLASS):
{
angle = APOLY*PID;
width = 2.5*MU;
height = 2.5*MU;
segment[0].x1 = BCXMIN;
segment[0].y1 = BCYMAX;
segment[0].concave = 0;
segment[1].x1 = -width;
segment[1].y1 = height;
segment[1].concave = 1;
segment[2].x1 = -width;
segment[2].y1 = -height;
segment[2].concave = 1;
segment[3].x1 = BCXMIN;
segment[3].y1 = BCYMIN;
segment[3].concave = 0;
segment[4].x1 = BCXMAX;
segment[4].y1 = BCYMIN;
segment[4].concave = 0;
segment[5].x1 = width;
segment[5].y1 = -height;
segment[5].concave = 1;
segment[6].x1 = width;
segment[6].y1 = height;
segment[6].concave = 1;
segment[7].x1 = BCXMAX;
segment[7].y1 = BCYMAX;
segment[7].concave = 0;
cycle = 1;
nsegments = 8;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_PENTA):
{
height = 0.5*(BCYMAX - BCYMIN);
width = height/sqrt(3.0);
segment[0].x1 = BCXMIN;
segment[0].y1 = 0.5*(BCYMIN + BCYMAX);
segment[1].x1 = BCXMIN + width;
segment[1].y1 = BCYMIN;
segment[2].x1 = BCXMAX;
segment[2].y1 = BCYMIN;
segment[3].x1 = BCXMAX;
segment[3].y1 = BCYMAX;
segment[4].x1 = BCXMIN + width;
segment[4].y1 = BCYMAX;
cycle = 1;
nsegments = 5;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 0;
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_CENTRIFUGE):
{
angle = DPI/(double)NPOLY;
for (i=0; i<NPOLY; i++)
{
segment[i*4].x1 = LAMBDA*cos(((double)i + 0.02)*angle);
segment[i*4].y1 = LAMBDA*sin(((double)i + 0.02)*angle);
segment[i*4].concave = 1;
segment[i*4 + 1].x1 = cos(((double)i + 0.05)*angle);
segment[i*4 + 1].y1 = sin(((double)i + 0.05)*angle);
segment[i*4 + 1].concave = 0;
segment[i*4 + 2].x1 = cos(((double)i + 0.95)*angle);
segment[i*4 + 2].y1 = sin(((double)i + 0.95)*angle);
segment[i*4 + 2].concave = 0;
segment[i*4 + 3].x1 = LAMBDA*cos(((double)i + 0.98)*angle);
segment[i*4 + 3].y1 = LAMBDA*sin(((double)i + 0.98)*angle);
segment[i*4 + 3].concave = 1;
}
cycle = 1;
nsegments = 4*NPOLY;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_POLY_ELLIPSE):
{
angle = DPI/(double)NPOLY;
for (i=0; i<NPOLY; i++)
{
segment[i].x1 = -cos(((double)i + 0.5)*angle);
segment[i].y1 = -LAMBDA*sin(((double)i + 0.5)*angle);
segment[i].concave = 0;
}
segment[0].concave = 1;
segment[NPOLY-1].concave = 1;
for (i=0; i<NPOLY; i++)
{
segment[NPOLY+i].x1 = 1.05*segment[NPOLY-1-i].x1;
segment[NPOLY+i].y1 = 1.05*segment[NPOLY-1-i].y1;
segment[NPOLY+i].concave = 1;
}
cycle = 1;
nsegments = 2*NPOLY;
for (i=0; i<nsegments; i++) segment[i].inactivate = 0;
break;
}
case (S_POOL_TABLE):
{
width = MU;
lpocket = 0.1;
add_rectangle_to_segments(BCXMIN + lpocket, BCYMIN, xmid - lpocket, BCYMIN - width, segment, 0);
add_rectangle_to_segments(xmid + lpocket, BCYMIN, BCXMAX - lpocket, BCYMIN - width, segment, 0);
add_rectangle_to_segments(BCXMAX + width, BCYMIN + lpocket, BCXMAX, BCYMAX - lpocket, segment, 0);
add_rectangle_to_segments(BCXMAX - lpocket, BCYMAX, xmid + lpocket, BCYMAX + width, segment, 0);
add_rectangle_to_segments(xmid - lpocket, BCYMAX, BCXMIN + lpocket, BCYMAX + width, segment, 0);
add_rectangle_to_segments(BCXMIN - width, BCYMAX - lpocket, BCXMIN, BCYMIN + lpocket, segment, 0);
cycle = 0;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 0;
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_CENTRIFUGE_RND):
{
angle = DPI/(double)NPOLY;
nsides = 24;
if ((nsides+2)*NPOLY > NMAXSEGMENTS)
{
printf("Error: NMAXSEGMENTS is too small\n");
exit(1);
}
for (i=0; i<NPOLY; i++)
{
segment[i*(nsides+2)].x1 = LAMBDA*cos(((double)i + 0.02)*angle);
segment[i*(nsides+2)].y1 = LAMBDA*sin(((double)i + 0.02)*angle);
segment[i*(nsides+2)].concave = 1;
for (j=1; j<=nsides; j++)
{
x = (double)j/(double)(nsides+1);
r = 0.5 + sqrt(x*(1.0-x));
angle2 = (double)i*angle + angle*(double)j/(double)(nsides+1);
segment[i*(nsides+2) + j].x1 = r*cos(angle2);
segment[i*(nsides+2) + j].y1 = r*sin(angle2);
segment[i*(nsides+2) + j].concave = 0;
}
segment[i*(nsides+2) + nsides + 1].x1 = LAMBDA*cos(((double)i + 0.98)*angle);
segment[i*(nsides+2) + nsides + 1].y1 = LAMBDA*sin(((double)i + 0.98)*angle);
segment[i*(nsides+2) + nsides + 1].concave = 1;
}
cycle = 1;
nsegments = (nsides+2)*NPOLY;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_CENTRIFUGE_LEAKY):
{
angle = DPI/(double)NPOLY;
nsides = 20;
if (2*(nsides+2)*NPOLY > NMAXSEGMENTS)
{
printf("Error: NMAXSEGMENTS is too small\n");
exit(1);
}
for (i=0; i<NPOLY; i++)
{
angle2 = (double)i*angle;
x1 = LAMBDA*cos(angle2);
y1 = LAMBDA*sin(angle2);
x2 = 0.7*cos(angle2);
y2 = 0.7*sin(angle2);
add_rotated_angle_to_segments(x1, y1, x2, y2, MU, 0, segment, 0);
for (j=0; j<nsides; j++) if (j!=nsides/2)
{
x = (double)j/(double)(nsides);
r = 0.5 + sqrt(x*(1.0-x) + 0.04);
if (j < nsides/2) angle2 = (double)i*angle + angle*(double)j/((double)(nsides) - 0.15);
else angle2 = (double)i*angle + angle*(double)j/((double)(nsides) + 0.15);
x1 = r*cos(angle2);
y1 = r*sin(angle2);
x = (double)(j+1)/(double)(nsides);
r = 0.5 + sqrt(x*(1.0-x) + 0.04);
if (j < nsides/2) angle2 = (double)i*angle + angle*(double)(j+1)/((double)(nsides) - 0.15);
else angle2 = (double)i*angle + angle*(double)(j+1)/((double)(nsides) + 0.15);
x2 = r*cos(angle2);
y2 = r*sin(angle2);
add_rotated_angle_to_segments(x1, y1, x2, y2, 0.5*MU, 0, segment, 0);
}
}
cycle = 0;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 0;
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_CIRCLE_EXT):
{
angle = DPI/(double)NPOLY;
for (i=0; i<NPOLY; i++)
{
segment[i].x1 = SEGMENTS_X0 + LAMBDA*cos(((double)i)*angle);
segment[i].y1 = SEGMENTS_Y0 - LAMBDA*sin(((double)i)*angle);
segment[i].concave = 1;
}
cycle = 1;
nsegments = NPOLY;
ngroups = 2;
for (i=0; i<nsegments; i++)
{
segment[i].group = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_ROCKET_NOZZLE):
{
/* ellipse */
for (i=1; i<NPOLY-1; i++)
{
angle = -PI + (double)i*DPI/(double)NPOLY;
x1 = 0.7*LAMBDA*(1.0 + cos(angle));
y1 = 0.5*LAMBDA*sin(angle);
angle = -PI + (double)(i+1)*DPI/(double)NPOLY;
x2 = 0.7*LAMBDA*(1.0 + cos(angle));
y2 = 0.5*LAMBDA*sin(angle);
add_rotated_angle_to_segments(x1, y1, x2, y2, 0.02, 0, segment, 0);
}
/* compute intersection point of nozzle and ellipse */
angle = PI - DPI/(double)NPOLY;
nozx = 0.7*LAMBDA*(1.0 + cos(angle));
nozy = 0.5*LAMBDA*sin(angle);
nsides = 10;
dx = LAMBDA/(double)(nsides);
/* nozzle */
for (i=0; i<nsides; i++)
{
x1 = -LAMBDA + dx*(double)(i-1);
y1 = nozzle_width(x1, nozy, NOZZLE_SHAPE);
x2 = x1 + dx;
y2 = nozzle_width(x2, nozy, NOZZLE_SHAPE);
add_rotated_angle_to_segments(x1, y1, x2, y2, 0.02, 0, segment, 0);
}
add_rotated_angle_to_segments(x2, y2, nozx, nozy, 0.02, 0, segment, 0);
for (i=0; i<nsides; i++)
{
x1 = -LAMBDA + dx*(double)(i-1);
y1 = -nozzle_width(x1, nozy, NOZZLE_SHAPE);
x2 = x1 + dx;
y2 = -nozzle_width(x2, nozy, NOZZLE_SHAPE);
add_rotated_angle_to_segments(x1, y1, x2, y2, 0.02, 0, segment, 0);
}
add_rotated_angle_to_segments(x2, y2, nozx, -nozy, 0.02, 0, segment, 0);
/* closing segment */
segment[nsegments].x1 = nozx;
segment[nsegments].y1 = nozy;
segment[nsegments].x2 = nozx;
segment[nsegments].y2 = -nozy;
nsegments++;
cycle = 0;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
segment[nsegments-1].inactivate = 1;
break;
}
case (S_ROCKET_NOZZLE_ROTATED):
{
add_rocket_to_segments(segment, 0.0, SEGMENTS_Y0, ROCKET_SHAPE, NOZZLE_SHAPE, 10, 1);
/* segments from group 0 are immobile by convention */
ngroups = 2;
cycle = 0;
// for (i=0; i<nsegments; i++) segment[i].group = 1;
break;
}
case (S_TWO_ROCKETS):
{
add_rocket_to_segments(segment, -SEGMENTS_X0, SEGMENTS_Y0, ROCKET_SHAPE, NOZZLE_SHAPE, 10, 1);
add_rocket_to_segments(segment, SEGMENTS_X0, SEGMENTS_Y0, ROCKET_SHAPE_B, NOZZLE_SHAPE_B, 10, 2);
ngroups = 3;
cycle = 0;
break;
}
case (S_TWO_CIRCLES_EXT):
{
angle = DPI/(double)NPOLY;
for (i=0; i<NPOLY; i++)
{
segment[i].x1 = SEGMENTS_X0 + LAMBDA*cos(((double)i)*angle);
segment[i].y1 = SEGMENTS_Y0 - LAMBDA*sin(((double)i)*angle);
segment[i].x2 = SEGMENTS_X0 + LAMBDA*cos(((double)(i+1))*angle);
segment[i].y2 = SEGMENTS_Y0 - LAMBDA*sin(((double)(i+1))*angle);
segment[i].concave = 1;
segment[i].group = 0;
segment[i].inactivate = 0;
}
for (i=NPOLY; i<2*NPOLY; i++)
{
segment[i].x1 = -SEGMENTS_X0 + TWO_CIRCLES_RADIUS_RATIO*LAMBDA*cos(((double)i)*angle);
segment[i].y1 = SEGMENTS_Y0 - TWO_CIRCLES_RADIUS_RATIO*LAMBDA*sin(((double)i)*angle);
segment[i].x2 = -SEGMENTS_X0 + TWO_CIRCLES_RADIUS_RATIO*LAMBDA*cos(((double)(i+1))*angle);
segment[i].y2 = SEGMENTS_Y0 - TWO_CIRCLES_RADIUS_RATIO*LAMBDA*sin(((double)(i+1))*angle);
segment[i].concave = 1;
segment[i].group = 1;
segment[i].inactivate = 0;
}
cycle = 0;
nsegments = 2*NPOLY;
break;
}
case (S_DAM):
{
add_rectangle_to_segments(DAM_WIDTH, BCYMIN - 0.5, -DAM_WIDTH, LAMBDA, segment, 0);
cycle = 0;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 1;
}
break;
}
case (S_DAM_WITH_HOLE):
{
add_rectangle_to_segments(DAM_WIDTH, BCYMIN - 0.5, -DAM_WIDTH, BCYMIN + 0.1, segment, 0);
add_rectangle_to_segments(DAM_WIDTH, BCYMIN + 0.3, -DAM_WIDTH, LAMBDA, segment, 0);
add_rectangle_to_segments(DAM_WIDTH, BCYMIN + 0.1, -DAM_WIDTH, BCYMIN + 0.3, segment, 0);
cycle = 0;
concave = 0; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
if (i > 7) segment[i].inactivate = 1;
else segment[i].inactivate = 0;
}
break;
}
case (S_DAM_WITH_HOLE_AND_RAMP):
{
add_rectangle_to_segments(DAM_WIDTH, BCYMIN - 0.5, -DAM_WIDTH, BCYMIN + 0.2, segment, 0);
add_rectangle_to_segments(DAM_WIDTH, BCYMIN + 0.3, -DAM_WIDTH, LAMBDA, segment, 0);
add_rectangle_to_segments(DAM_WIDTH, BCYMIN + 0.2, -DAM_WIDTH, BCYMIN + 0.3, segment, 0);
r = 1.0;
for (i=0; i<10; i++)
{
angle = 0.1*PID*(double)i;
dangle = 0.1*PID;
x1 = XMAX - r + (r + MU)*cos(angle);
y1 = YMIN + r - (r + MU)*sin(angle);
x2 = XMAX - r + (r + MU)*cos(angle + dangle);
y2 = YMIN + r - (r + MU)*sin(angle + dangle);
add_rotated_angle_to_segments(x1, y1, x2, y2, MU, 0, segment, 0);
}
cycle = 0;
concave = 0; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
if ((i > 7)&&(i < 12)) segment[i].inactivate = 1;
else segment[i].inactivate = 0;
}
break;
}
case (S_MAZE):
{
init_maze_segments(segment, 0);
cycle = 0;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_MAZE_DIAG):
{
init_maze_segments(segment, 1);
cycle = 0;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_EXT_RECTANGLE):
{
width = 0.1*LAMBDA;
segment[0].x1 = SEGMENTS_X0 - LAMBDA;
segment[0].y1 = SEGMENTS_Y0 - width;
segment[1].x1 = SEGMENTS_X0 - LAMBDA;
segment[1].y1 = SEGMENTS_Y0 + width;
segment[2].x1 = SEGMENTS_X0 + LAMBDA;
segment[2].y1 = SEGMENTS_Y0 + width;
segment[3].x1 = SEGMENTS_X0 + LAMBDA;
segment[3].y1 = SEGMENTS_Y0 - width;
cycle = 1;
nsegments = 4;
ngroups = 2;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 1;
segment[i].group = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_DAM_BRICKS):
{
add_rectangle_to_segments(DAM_WIDTH, BCYMIN - 0.5, -DAM_WIDTH, BCYMIN, segment, 0);
dy = 0.1*(LAMBDA - BCYMIN);
for (i=1; i<11; i++)
{
y = BCYMIN + (double)i*dy;
add_rectangle_to_segments(DAM_WIDTH, y-dy+MU, -DAM_WIDTH, y, segment, i);
}
ngroups = 11;
cycle = 0;
concave = 0; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].inactivate = 0;
}
break;
}
case (S_HLINE_HOLE):
{
x = 0.15;
x1 = XMAX + 1.0;
y1 = 0.0;
width = 0.05;
add_rectangle_to_segments(x1, y1 - width, x, y1, segment, 0);
add_rectangle_to_segments(-x, y1 - width, -x1, y1, segment, 0);
/* closing segment */
segment[nsegments].x1 = -x;
segment[nsegments].y1 = y1;
segment[nsegments].x2 = x;
segment[nsegments].y2 = y1;
nsegments++;
cycle = 0;
concave = 0; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].inactivate = 0;
}
segment[nsegments-1].inactivate = 1;
break;
}
case (S_HLINE_HOLE_SPOKES):
{
x = 0.15;
x1 = XMAX + 1.0;
y1 = 0.0;
width = 0.05;
add_rectangle_to_segments(x1, y1 - width, x, y1, segment, 0);
add_rectangle_to_segments(-x, y1 - width, -x1, y1, segment, 0);
/* closing segment */
segment[nsegments].x1 = -x;
segment[nsegments].y1 = y1 - 0.5*width;
segment[nsegments].x2 = x;
segment[nsegments].y2 = y1 - 0.5*width;
nsegments++;
/* spokes */
for (i=-3; i<4; i+=2)
{
x = 0.5*(double)i;
segment[nsegments].x1 = x - 0.1;
segment[nsegments].y1 = BCYMIN - 0.1;
segment[nsegments].x2 = x;
segment[nsegments].y2 = YMIN + 0.3;
nsegments++;
segment[nsegments].x1 = x;
segment[nsegments].y1 = YMIN + 0.3;
segment[nsegments].x2 = x + 0.1;
segment[nsegments].y2 = BCYMIN - 0.1;
nsegments++;
}
cycle = 0;
concave = 0; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].inactivate = 0;
}
segment[8].inactivate = 1;
break;
}
case (S_HLINE_HOLE_SLOPED):
{
x = 0.15;
x1 = XMAX + 1.0;
width = 0.05;
angle = APOLY*DPI;
y1 = (x1 - x)*tan(angle);
segment[0].x1 = x1;
segment[0].y1 = y1;
segment[1].x1 = x1 + width*sin(angle);
segment[1].y1 = y1 - width*cos(angle);
segment[2].x1 = x + width*sin(angle);
segment[2].y1 = -width*cos(angle);
segment[3].x1 = x;
segment[3].y1 = 0.0;
for (i=0; i<3; i++)
{
segment[i].x2 = segment[i+1].x1;
segment[i].y2 = segment[i+1].y1;
}
segment[3].x2 = segment[0].x1;
segment[3].y2 = segment[0].y1;
segment[4].x1 = -x1;
segment[4].y1 = y1;
segment[5].x1 = -x;
segment[5].y1 = 0.0;
segment[6].x1 = -x - width*sin(angle);
segment[6].y1 = -width*cos(angle);
segment[7].x1 = -x1 - width*sin(angle);
segment[7].y1 = y1 - width*cos(angle);
for (i=4; i<7; i++)
{
segment[i].x2 = segment[i+1].x1;
segment[i].y2 = segment[i+1].y1;
}
segment[7].x2 = segment[4].x1;
segment[7].y2 = segment[4].y1;
nsegments = 8;
for (i=0; i<nsegments; i++) segment[i].concave = 1;
/* closing segment */
segment[nsegments].x1 = -x;
segment[nsegments].y1 = 0.0;
segment[nsegments].x2 = x;
segment[nsegments].y2 = 0.0;
nsegments++;
cycle = 0;
concave = 1;
for (i=0; i<nsegments; i++)
{
segment[i].inactivate = 0;
}
segment[nsegments-1].inactivate = 1;
break;
}
case (S_EXT_CIRCLE_RECT):
{
width = 0.1*LAMBDA;
add_rectangle_to_segments(SEGMENTS_X0 + LAMBDA, SEGMENTS_Y0 - width, SEGMENTS_X0 - LAMBDA, SEGMENTS_Y0 + width, segment, 1);
add_circle_to_segments(-SEGMENTS_X0, SEGMENTS_Y0, 0.5*LAMBDA, NPOLY, APOLY, segment, 2);
cycle = 0;
concave = 0;
nsegments = NPOLY + 4;
ngroups = 3;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_BIN_OPENING):
{
add_rectangle_to_segments(LAMBDA, 1.0 - LAMBDA, LAMBDA - MU, YMAX + 1.0, segment, 0);
add_rectangle_to_segments(-LAMBDA + MU, 1.0 - LAMBDA, -LAMBDA, YMAX + 1.0, segment, 0);
add_rectangle_to_segments(LAMBDA, 1.0 - LAMBDA, -LAMBDA, 1.0 - LAMBDA + MU, segment, 0);
cycle = 0;
concave = 0; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 1;
}
break;
}
case (S_BIN_LARGE):
{
add_rectangle_to_segments(LAMBDA, -MU, -LAMBDA, 0.0, segment, 0);
add_rectangle_to_segments(LAMBDA, -MU, LAMBDA - MU, 0.25, segment, 0);
add_rectangle_to_segments(-LAMBDA + MU, -MU, -LAMBDA, 0.25, segment, 0);
cycle = 0;
concave = 0; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_POLYGON_EXT):
{
add_circle_to_segments(0.0, 0.0, LAMBDA, NPOLY, APOLY, segment, 0);
cycle = 0;
concave = 0;
nsegments = NPOLY;
ngroups = 1;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_WEDGE_EXT):
{
angle = AWEDGE*PID;
segment[0].x1 = LAMBDA;
segment[0].y1 = 0.0;
segment[1].x1 = -LAMBDA*cos(angle);
segment[1].y1 = -LAMBDA*sin(angle);
segment[2].x1 = 0.0;
segment[2].y1 = 0.0;
segment[3].x1 = -LAMBDA*cos(angle);
segment[3].y1 = LAMBDA*sin(angle);
cycle = 1;
nsegments = 4;
ngroups = 2;
ca = cos(APOLY*PID);
sa = sin(APOLY*PID);
for (i=0; i<nsegments; i++)
{
x = segment[i].x1;
y = segment[i].y1;
segment[i].x1 = x*ca + y*sa;
segment[i].y1 = -x*sa + y*ca;
segment[i].concave = 1;
segment[i].group = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_MIXER):
{
for (i=0; i<NPOLY; i++)
{
angle = (double)i*DPI/(double)NPOLY;
add_rotated_angle_to_segments(0.0, 0.0, LAMBDA*cos(angle), LAMBDA*sin(angle), 0.05, 1, segment, 0);
}
cycle = 0;
concave = 1; /* add_rectangle_to_segments already deals with concave corners */
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_AIRFOIL):
{
dangle = DPI/(double)NPOLY;
angle = APOLY*PID;
ca = cos(angle);
sa = sin(angle);
for (i=0; i<NPOLY; i++)
{
angle = (double)i*dangle;
x = LAMBDA*cos(angle);
y1 = -0.2*LAMBDA*sin(angle);
y1 -= 0.5*x*x;
segment[i].x1 = x*ca + y1*sa;
segment[i].y1 = -x*sa + y1*ca;
}
cycle = 1;
concave = 1;
nsegments = NPOLY;
ngroups = 1;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_COANDA):
{
y1 = SEGMENTS_Y0;
width = 0.05;
padding = 0.01;
height = 0.25;
dx = (XMAX - XMIN + 2.0*padding)/(double)(NPOLY-1);
for (i=0; i<NPOLY; i++)
{
x = XMIN - padding + (double)i*dx;
y = y1 + height*cos(PI*x/XMAX);
segment[i].x1 = x;
segment[i].y1 = y + width;
segment[i].x2 = x + dx;
segment[i].x2 = y1 + height*cos(PI*(x+dx)/XMAX) + width;
segment[i].concave = 1;
}
for (i=0; i<NPOLY; i++)
{
x = XMAX + padding - (double)i*dx;
y = y1 + height*cos(PI*x/XMAX);
segment[NPOLY+i].x1 = x;
segment[NPOLY+i].y1 = y - width;
segment[NPOLY+i].x2 = x - dx;
segment[NPOLY+i].x2 = y1 + height*cos(PI*(x-dx)/XMAX) - width;
segment[NPOLY+i].concave = 1;
}
nsegments = 2*NPOLY;
cycle = 1;
concave = 1;
for (i=0; i<nsegments; i++)
{
segment[i].inactivate = 0;
}
break;
}
case (S_COANDA_SHORT):
{
y1 = SEGMENTS_Y0;
width = 0.05;
padding = 0.1;
x1 = XMIN + padding;
height = 0.2;
length = 0.85*(XMAX - XMIN - 2.0*padding);
dx = length/(double)(NPOLY-1);
for (i=0; i<NPOLY; i++)
{
x = x1 + (double)i*dx;
y = y1 - height*cos(DPI*(x-x1)/length);
yy = y1 - height*cos(DPI*(x+dx-x1)/length);
add_rotated_angle_to_segments(x, y, x+dx, yy, width, 1, segment, 0);
}
cycle = 0;
concave = 1;
for (i=0; i<nsegments; i++)
{
segment[i].inactivate = 0;
}
break;
}
case (S_CYLINDER):
{
add_rectangle_to_segments(XMAX + 0.1, YMAX - 0.05, XMIN - 0.1, YMAX + 1.0, segment, 0);
add_rectangle_to_segments(XMAX + 0.1, YMIN - 1.0, XMIN - 0.1, YMIN + 0.05, segment, 0);
cycle = 0;
concave = 1;
for (i=0; i<nsegments; i++)
{
segment[i].inactivate = 0;
}
break;
}
case (S_TREE):
{
for (i=0; i<NTREES; i++)
{
x = XMIN + ((double)i - 0.5 + 0.1*(double)rand()/(double)RAND_MAX)*(XMAX-XMIN)*1.1/(double)NTREES;
y = YMIN + 0.1 + 0.014*(double)i*(YMAX - YMIN);
add_tree_to_segments(x, y, LAMBDA*(1.0 + 0.4*(double)rand()/(double)RAND_MAX), lfoot[i], rfoot[i], segment, 0);
}
/* add ground */
for (i=0; i<NTREES-1; i++)
{
segment[nsegments].x1 = rfoot[i][0];
segment[nsegments].y1 = rfoot[i][1];
segment[nsegments].x2 = lfoot[i+1][0];
segment[nsegments].y2 = lfoot[i+1][1];
segment[nsegments].concave = 0;
nsegments++;
}
cycle = 0;
concave = 0;
ngroups = 1;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_TREES_B):
{
for (i=0; i<NTREES; i++)
{
x = XMIN + ((double)i - 0.5 + 0.1*(double)rand()/(double)RAND_MAX)*(XMAX-XMIN)*1.1/(double)NTREES;
if (i<NTREES/2) y = YMIN + 0.03*(double)i*(YMAX - YMIN);
else y = YMIN + 0.03*(double)(NTREES/2)*(YMAX - YMIN) - 0.025*(double)(i - NTREES/2)*(YMAX - YMIN);
add_tree_to_segments(x, y, LAMBDA*(1.0 + 0.4*(double)rand()/(double)RAND_MAX), lfoot[i], rfoot[i], segment, i+1);
}
/* add ground */
for (i=0; i<NTREES-1; i++)
{
segment[nsegments].x1 = rfoot[i][0];
segment[nsegments].y1 = rfoot[i][1];
segment[nsegments].x2 = lfoot[i+1][0];
segment[nsegments].y2 = lfoot[i+1][1];
segment[nsegments].concave = 0;
nsegments++;
segment[nsegments].x1 = lfoot[i+1][0];
segment[nsegments].y1 = lfoot[i+1][1];
segment[nsegments].x2 = rfoot[i+1][0];
segment[nsegments].y2 = rfoot[i+1][1];
segment[nsegments].concave = 0;
nsegments++;
}
cycle = 0;
concave = 0;
ngroups = 1;
for (i=0; i<nsegments; i++)
{
segment[i].concave = 1;
segment[i].inactivate = 0;
}
break;
}
case (S_CONE):
{
add_rotated_angle_to_segments(LAMBDA, LAMBDA, 0.05, -0.2, WALL_WIDTH, 0, segment, 0);
add_rotated_angle_to_segments(-0.05, -0.2, -LAMBDA, LAMBDA, WALL_WIDTH, 0, segment, 0);
cycle = 0;
concave = 1;
for (i=0; i<nsegments; i++)
{
segment[i].group = 0;
segment[i].inactivate = 0;
}
break;
}
case (S_CONVEYOR_BELT):
{
add_conveyor_belt(XMIN + 0.05, 0.0, 0.5, 0.0, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
cycle = 0;
concave = 0;
break;
}
case (S_TWO_CONVEYOR_BELTS):
{
add_conveyor_belt(XMIN + 0.05, 0.2, 0.8, 0.5, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(-0.5, -0.3, XMAX - 0.05, -0.8, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
cycle = 0;
concave = 0;
break;
}
case (S_PERIODIC_CONVEYORS):
{
add_conveyor_belt(0.05, -0.3, XMAX - XMIN - 0.125, 0.4, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(XMIN - XMAX + 0.05, -0.3, -0.125, 0.4, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(-1.0, -0.6, 0.2, -0.6, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
cycle = 0;
concave = 0;
break;
}
case (S_CONVEYOR_SHOVELS):
{
add_conveyor_belt(-1.0, -0.97, 1.0, 0.8, 0.05, BELT_SPEED1, 25, segment, conveyor_belt);
add_rectangle_to_segments(WALL_WIDTH, YMIN - 0.05, -WALL_WIDTH, -0.5, segment, 0);
cycle = 0;
concave = 0;
break;
}
case (S_CONVEYOR_MIXED):
{
add_conveyor_belt(XMIN - 0.32, 0.65, -0.2, 0.65, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(XMAX - 0.32, 0.65, XMAX - XMIN - 0.2, 0.65, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(XMIN + 0.3, 0.3, 0.0, 0.3, 0.05, -BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(XMIN + 0.1, -0.05, -0.2, -0.05, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(XMIN - 0.5, -0.4, 0.0, -0.4, 0.05, -BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(XMAX - 0.5, -0.4, XMAX - XMIN, -0.4, 0.05, -BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(XMIN - 0.3, -0.75, -0.25, -0.75, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(XMAX - 0.7, -0.75, XMAX - XMIN - 0.25, -0.75, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(-0.13, -0.97, 1.67, 0.95, 0.05, BELT_SPEED3, 24, segment, conveyor_belt);
cycle = 0;
concave = 0;
break;
}
case (S_CONVEYOR_SIEVE):
{
add_conveyor_belt(XMIN, 0.8, -0.4, 0.8, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(0.05, 0.0, XMAX - 0.3, 0.0, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(0.05, -0.5, XMAX - 1.0, -0.5, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_rectangle_to_segments(1.3, YMIN - 0.05, 1.3 - WALL_WIDTH, -0.3, segment, 0);
add_rectangle_to_segments(0.6, YMIN - 0.05, 0.6 - WALL_WIDTH, -0.75, segment, 0);
add_rectangle_to_segments(0.0, YMIN - 0.05, - WALL_WIDTH, -0.95, segment, 0);
cycle = 0;
concave = 0;
break;
}
case (S_CONVEYOR_SIEVE_B):
{
add_conveyor_belt(-0.7, 0.8, XMAX + 0.1, 0.8, 0.05, -BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(0.05, 0.0, XMAX - 0.3, 0.0, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(0.05, -0.5, XMAX - 1.0, -0.5, 0.05, BELT_SPEED2, 0, segment, conveyor_belt);
add_rectangle_to_segments(1.5, YMIN - 0.05, 1.5 - WALL_WIDTH, -0.15, segment, 0);
add_rectangle_to_segments(0.75, YMIN - 0.05, 0.75 - WALL_WIDTH, -0.75, segment, 0);
add_rectangle_to_segments(0.0, YMIN - 0.05, 0.0 - WALL_WIDTH, -0.95, segment, 0);
add_rectangle_to_segments(-1.0, YMIN - 0.05, -1.0 - WALL_WIDTH, -0.95, segment, 0);
cycle = 0;
concave = 0;
break;
}
case (S_CONVEYOR_SIEVE_LONG):
{
add_conveyor_belt(-1.4, 0.8, XMAX + 0.1, 0.8, 0.05, -BELT_SPEED1, 0, segment, conveyor_belt);
for (i=0; i<7; i++)
{
x = XMIN + (double)i*(XMAX-XMIN)/7.0;
add_rectangle_to_segments(x, YMIN - 0.05, x - WALL_WIDTH, -0.75, segment, 0);
}
cycle = 0;
concave = 0;
break;
}
case (S_CONVEYORS_1400):
{
add_conveyor_belt(-2.0 + 0.07, 0.7, 0.5, 0.7, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(-2.0 + 0.25, 0.2, 0.7, 0.2, 0.05, -BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(-2.0 + 0.07, -0.3, 0.5, -0.3, 0.05, BELT_SPEED1, 0, segment, conveyor_belt);
add_conveyor_belt(0.3, -0.97, 3.0, 1.5, 0.05, BELT_SPEED1, 35, segment, conveyor_belt);
// add_rotated_angle_to_segments(4.0, 0.6, 2.9, 0.4, WALL_WIDTH, 1, segment, 0);
// add_conveyor_belt(1.5, -0.5, 4.5, -0.5, 0.05, -BELT_SPEED2, 0, segment, conveyor_belt);
add_rectangle_to_segments(2.0, -0.5, 2.0 - WALL_WIDTH, -0.15, segment, 0);
add_rectangle_to_segments(3.5, -0.5, 2.0, -0.5 + WALL_WIDTH, segment, 0);
add_rectangle_to_segments(3.5, -0.5, 3.5 - WALL_WIDTH, -0.15, segment, 0);
add_conveyor_belt(1.5, -1.4, 5.5, -1.4, 0.05, -BELT_SPEED2, 0, segment, conveyor_belt);
add_conveyor_belt(-1.4, -1.4, 2.0, -1.8, 0.05, -BELT_SPEED2, 0, segment, conveyor_belt);
add_rotated_angle_to_segments(-1.0, -1.6, -2.5, -2.6, WALL_WIDTH, 1, segment, 0);
add_rotated_angle_to_segments(-1.9, -2.6, -3.4, -1.6, WALL_WIDTH, 1, segment, 0);
add_conveyor_belt(-2.2, -2.13, -2.2, 1.45, 0.07, 1.5*BELT_SPEED2, 36, segment, conveyor_belt);
cycle = 0;
concave = 0;
break;
}
case (S_MASS_SPECTROMETER):
{
for (i=0; i<7; i++)
{
y = YMIN + (double)i*(YMAX-YMIN)/7.0;
add_rectangle_to_segments(XMAX + 0.2, y, XMAX - 0.6, y + WALL_WIDTH, segment, 0);
}
cycle = 0;
concave = 0;
break;
}
case (S_WIND_FORCE):
{
for (i=0; i<10; i++)
{
x = XMIN + (double)i*(XMAX-XMIN)/10.0;
add_rectangle_to_segments(x, YMIN - 0.05, x - WALL_WIDTH, WIND_YMIN - 0.1, segment, 0);
}
cycle = 0;
concave = 0;
break;
}
case (S_BINS_GALTON):
{
dy = (YMAX - YMIN)/((double)(NGRIDX + 3));
dx = dy/cos(PI/6.0);
for (i=-1; i<=NGRIDX; i++)
{
x = ((double)i - 0.5*(double)NGRIDX + 0.5)*dx;
add_rectangle_to_segments(x + 0.5*WALL_WIDTH, YMIN - 0.05, x - 0.5*WALL_WIDTH, YMIN + 2.75*dy, segment, 0);
}
cycle = 0;
concave = 0;
break;
}
case (S_BINS_GALTON_WIDE):
{
dy = (YMAX - YMIN)/((double)(NGRIDX + 3));
dx = dy/cos(PI/6.0);
for (i=-5; i<=NGRIDX+4; i++)
{
x = ((double)i - 0.5*(double)NGRIDX + 0.5)*dx;
add_rectangle_to_segments(x + 0.5*WALL_WIDTH, YMIN - 0.05, x - 0.5*WALL_WIDTH, YMIN + 2.75*dy, segment, 0);
}
cycle = 0;
concave = 0;
break;
}
case (S_TEST_CONVEYORS):
{
add_conveyor_belt(-1.0, -0.7, 1.0, 0.7, 0.05, BELT_SPEED1, 25, segment, conveyor_belt);
// add_conveyor_belt(-0.5, -0.3, 0.5, 0.3, 0.05, BELT_SPEED1, 100, segment, conveyor_belt);
cycle = 0;
concave = 0;
break;
}
default:
{
printf("Function init_segment_config not defined for this pattern \n");
}
}
if (cycle) for (i=0; i<nsegments; i++)
{
segment[i].x2 = segment[(i+1)%(nsegments)].x1;
segment[i].y2 = segment[(i+1)%(nsegments)].y1;
}
else if (SEGMENT_PATTERN != S_TWO_CIRCLES_EXT) for (i=0; i<nsegments; i++) if (segment[i].cycle)
{
segment[i].x2 = segment[(i+1)%(nsegments)].x1;
segment[i].y2 = segment[(i+1)%(nsegments)].y1;
}
/* add one segment for S_POLY_ELLIPSE configuration */
if (SEGMENT_PATTERN == S_POLY_ELLIPSE)
{
segment[nsegments].x1 = -cos(((double)i + 0.5)*angle);
segment[nsegments].y1 = LAMBDA*sin(((double)i + 0.5)*angle);
segment[nsegments].x2 = -cos(((double)i + 0.5)*angle);
segment[nsegments].y2 = -LAMBDA*sin(((double)i + 0.5)*angle);
segment[nsegments].inactivate = 1;
nsegments++;
}
/* activate all segments */
for (i=0; i<nsegments; i++) segment[i].active = 1;
/* inactivate some segments of leaky centrifuge */
// if (SEGMENT_PATTERN == S_CENTRIFUGE_LEAKY)
// for (i=0; i<NPOLY; i++) segment[(nsides+2)*i + nsides/2].active = 0;
/* compute parameters for slope and normal of segments */
for (i=0; i<nsegments; i++)
{
a = segment[i].y1 - segment[i].y2;
b = segment[i].x2 - segment[i].x1;
length = module2(a, b);
segment[i].nx = a/length;
segment[i].ny = b/length;
segment[i].nangle = argument(segment[i].nx, segment[i].ny);
segment[i].c = segment[i].nx*segment[i].x1 + segment[i].ny*segment[i].y1;
segment[i].length = length;
segment[i].fx = 0.0;
segment[i].fy = 0.0;
segment[i].torque = 0.0;
segment[i].xc = 0.5*(segment[i].x1 + segment[i].x2);
segment[i].yc = 0.5*(segment[i].y1 + segment[i].y2);
}
/* deal with concave corners */
if (concave) for (i=0; i<nsegments; i++) if (segment[i].concave)
{
iminus = i-1;
iplus = i+1;
if (SEGMENT_PATTERN == S_TWO_CIRCLES_EXT)
{
if (iminus == -1) iminus += nsegments/2;
else if (iminus == nsegments/2 - 1) iminus += nsegments/2;
if (iplus == nsegments/2) iplus = 0;
else if (iplus == nsegments) iplus = nsegments/2;
}
else
{
if (iminus < 0) iminus = nsegments - 1;
if (iplus > nsegments - 1) iplus = 0;
}
angle = argument(segment[iplus].x1 - segment[i].x1, segment[iplus].y1 - segment[i].y1) + PID;
angle2 = argument(segment[i].x1 - segment[iminus].x1, segment[i].y1 - segment[iminus].y1) + PID;
if (angle2 < angle) angle2 += DPI;
segment[i].angle1 = angle;
segment[i].angle2 = angle2;
printf("i = %i, iplus = %i, iminus = %i, angle1 = %.0f, angle2 = %.0f\n", i, iplus, iminus, angle*360.0/DPI, angle2*360.0/DPI);
}
/* make copy of initial values in case of rotation/translation */
if ((ROTATE_BOUNDARY)||(MOVE_BOUNDARY)||(MOVE_SEGMENT_GROUPS)) for (i=0; i<nsegments; i++)
{
segment[i].x01 = segment[i].x1;
segment[i].x02 = segment[i].x2;
segment[i].y01 = segment[i].y1;
segment[i].y02 = segment[i].y2;
segment[i].nx0 = segment[i].nx;
segment[i].ny0 = segment[i].ny;
segment[i].angle01 = segment[i].angle1;
segment[i].angle02 = segment[i].angle2;
}
/* initialize averaged pressure */
if (SHOW_SEGMENTS_PRESSURE) for (i=0; i<nsegments; i++)
segment[i].avrg_pressure = 0.0;
// for (i=0; i<nsegments; i++)
// {
// printf("Segment %i: (x1, y1) = (%.3lg,%.3lg), (x2, y2) = (%.3lg,%.3lg)\n (nx, ny) = (%.3lg,%.3lg), c = %.3lg, length = %.3lg\n", i, segment[i].x1, segment[i].y1, segment[i].x2, segment[i].y2, segment[i].nx, segment[i].ny, segment[i].c, segment[i].length);
// if (segment[i].concave) printf("Concave with angles %.3lg Pi, %.3lg Pi\n", segment[i].angle1/PI, segment[i].angle2/PI);
// }
// sleep(4);
}
int in_rocket(double x, double y, int rocket_shape)
/* returns 1 if (x,y) is in rocket chamber, with translated coordinates */
{
double l, y1, a, b, c;
switch (rocket_shape) {
case (RCK_DISC) :
{
l = 0.7*LAMBDA;
y1 = y - YMIN - 1.7*LAMBDA;
return ((x*x + y1*y1)/(l*l) + MU*MU < 0.875);
// return ((x*x + y1*y1)/(l*l) + MU*MU < 0.925*LAMBDA*LAMBDA);
}
case (RCK_RECT) :
{
a = 0.5*LAMBDA;
b = 0.49*PI*LAMBDA;
y1 = y - YMIN - LAMBDA;
return ((vabs(x) < 0.95*a)&&(y1 > 0.05)&&(y1 < b - 0.05));
}
case (RCK_RECT_HAT) :
{
// printf("(%.2lg,%.2lg) in rocket?\n", x, y);
a = 0.5*LAMBDA;
b = (0.49*PI-0.25)*LAMBDA;
y1 = y - YMIN - LAMBDA;
if (vabs(x) > 0.95*a) return(0);
if (y1 < 0.05) return(0);
if (y1 < b - 0.05) return(1);
return(y1 < a + b - 0.05 - vabs(x));
// return(1);
}
case (RCK_RECT_BAR) :
{
a = 0.5*LAMBDA;
b = (0.49*PI-0.25)*LAMBDA;
c = 0.5*a;
y1 = y - YMIN - LAMBDA;
if (vabs(x) > 0.95*a) return(0);
if (vabs(x) < 0.1*a) return(0);
if (y1 < 0.05 + c) return(0);
if (y1 < b - 0.05 + c) return(1);
return(y1 < a + b - 0.05 + c - vabs(x));
}
}
}
int in_segment_region(double x, double y, t_segment segment[NMAXSEGMENTS])
/* returns 1 if (x,y) is inside region delimited by obstacle segments */
{
int i;
double angle, dx, height, width, length, theta, lx, ly, x1, y1, x2, y2, padding, ca, sa, r;
if (x >= BCXMAX) return(0);
if (x <= BCXMIN) return(0);
if (y >= BCYMAX) return(0);
if (y <= BCYMIN) return(0);
switch (SEGMENT_PATTERN) {
case (S_CUP):
{
angle = APOLY*PID;
dx = (BCYMAX - BCYMIN)/tan(angle);
if (y < BCYMAX - (BCYMAX - BCYMIN)*(x - BCXMIN)/dx) return(0);
if (y < BCYMAX - (BCYMAX - BCYMIN)*(BCXMAX - x)/dx) return(0);
}
case (S_HOURGLASS):
{
angle = APOLY*PID;
width = 2.5*MU;
height = 2.5*MU;
x = vabs(x);
y = vabs(y);
if ((x >= width)&&(x - width >= (y - height)*(BCXMAX - width)/(BCYMAX - height))) return(0);
return(1);
}
case (S_PENTA):
{
height = 0.5*(BCYMAX - BCYMIN);
width = height/sqrt(3.0);
if (y < BCYMIN + height*(1.0 - (x - BCXMIN)/width)) return(0);
if (y > BCYMAX - height*(1.0 - (x - BCXMIN)/width)) return(0);
return(1);
}
case (S_CENTRIFUGE):
{
angle = argument(x,y);
theta = DPI/(double)NPOLY;
while (angle > theta) angle -= theta;
while (angle < 0.0) angle += theta;
if (angle < 0.1) return(0);
if (angle > 0.9) return(0);
return(1);
}
case (S_POLY_ELLIPSE):
{
if (x*x + y*y/(LAMBDA*LAMBDA) < 0.95) return(1);
else return(0);
}
case (S_CENTRIFUGE_RND):
{
if (module2(x,y) > 0.75) return(0);
angle = argument(x,y);
theta = DPI/(double)NPOLY;
while (angle > theta) angle -= theta;
while (angle < 0.0) angle += theta;
if (angle < 0.1) return(0);
if (angle > 0.9) return(0);
return(1);
}
case (S_CENTRIFUGE_LEAKY):
{
if (module2(x,y) > 0.75) return(0);
angle = argument(x,y);
theta = DPI/(double)NPOLY;
while (angle > theta) angle -= theta;
while (angle < 0.0) angle += theta;
if (angle < 0.1) return(0);
if (angle > 0.9) return(0);
return(1);
}
case (S_CIRCLE_EXT):
{
if (module2(x - SEGMENTS_X0, y - SEGMENTS_Y0) > LAMBDA + 2.0*MU) return(1);
else return(0);
}
case (S_TWO_CIRCLES_EXT):
{
if ((module2(x - SEGMENTS_X0, y - SEGMENTS_Y0) > LAMBDA + 2.0*MU)&&(module2(x + SEGMENTS_X0, y - SEGMENTS_Y0) > TWO_CIRCLES_RADIUS_RATIO*LAMBDA + 2.0*MU)) return(1);
else return(0);
}
case (S_ROCKET_NOZZLE):
{
if (x < 0.0) return(0);
else if (x > 1.4*LAMBDA) return(0);
else
{
lx = 0.7*LAMBDA;
ly = 0.7*LAMBDA;
x -= lx;
if (x*x/(lx*lx) + y*y/(ly*ly) < 0.95) return(1);
}
return(0);
}
case (S_ROCKET_NOZZLE_ROTATED):
{
y1 = y - ysegments[0];
if (y1 < YMIN + LAMBDA) return(0);
else if (y1 > YMIN + 2.4*LAMBDA) return(0);
else if (in_rocket(x - xsegments[0], y1, ROCKET_SHAPE)) return(1);
// {
// ly = 0.7*LAMBDA;
// lx = 0.7*LAMBDA;
// x1 = x - xsegments[0];
// y1 -= YMIN + 1.7*LAMBDA;
// if (x1*x1/(lx*lx) + y1*y1/(ly*ly) + MU*MU < 0.925) return(1);
// }
return(0);
}
case (S_TWO_ROCKETS):
{
y1 = y - ysegments[0];
y2 = y - ysegments[1];
if ((y1 < YMIN + LAMBDA)&&(y2 < YMIN + LAMBDA)) return(0);
else if ((y1 > YMIN + 3.5*LAMBDA)&&(y2 > YMIN + 3.5*LAMBDA)) return(0);
else
{
if (in_rocket(x - xsegments[0], y1, ROCKET_SHAPE_B)) return(1);
if (in_rocket(x - xsegments[1], y2, ROCKET_SHAPE)) return(1);
}
return(0);
}
case (S_DAM):
{
if (vabs(x) > DAM_WIDTH) return(1);
else if (y > LAMBDA) return(1);
else return(0);
}
case (S_EXT_RECTANGLE):
{
padding = 0.1;
if (vabs(x) > LAMBDA + padding) return(1);
else if (vabs(y) > 0.1*LAMBDA + padding) return(1);
else return(0);
}
case (S_EXT_CIRCLE_RECT):
{
padding = 0.1;
if ((vabs(x - SEGMENTS_X0) < LAMBDA + padding)&&(vabs(y - SEGMENTS_Y0) < 0.1*LAMBDA + padding)) return(0);
else if (module2(x + SEGMENTS_X0, y - SEGMENTS_Y0) < 0.5*LAMBDA + padding) return(0);
else return(1);
}
case (S_BIN_OPENING):
{
padding = 3.0*MU;
if (y < 0.0) return(1);
if ((y > 1.0 - LAMBDA + padding)&&(vabs(x) < LAMBDA - padding)) return(1);
return(0);
}
case (S_POLYGON_EXT):
{
padding = 3.0*MU;
if (in_polygon(x, y, LAMBDA + padding, NPOLY, APOLY)) return(0);
else return(1);
}
case (S_WEDGE_EXT):
{
padding = 3.0*MU;
angle = AWEDGE*PID;
ca = cos(APOLY*PID);
sa = sin(APOLY*PID);
x1 = x*ca - y*sa;
y1 = x*sa + y*ca;
if (vabs(y1) - padding > (LAMBDA-x1)*sin(angle)/(1.0+cos(angle))) return(1);
if (vabs(y1) + padding < -x1*tan(angle)) return(1);
return(0);
}
case (S_MIXER):
{
padding = 1.5*MU;
r = module2(x,y);
if (r > LAMBDA + padding) return(1);
if (r < 2.0*padding) return(0);
for (i=0; i<NPOLY; i++)
{
angle = (double)i*DPI/(double)NPOLY;
ca = cos(angle);
sa = sin(angle);
x1 = x*ca - y*sa;
y1 = x*sa + y*ca;
if ((x1 > 0.0)&&(vabs(y1) < 0.025 + padding)) return(0);
}
return(1);
}
case (S_AIRFOIL):
{
if (vabs(x) > LAMBDA + 4.0*MU) return(1);
padding = 0.35;
angle = APOLY*PID;
ca = cos(angle);
sa = sin(angle);
x1 = x*ca - y*sa;
y1 = x*sa + y*ca;
y1 += 0.5*x1*x1;
return(x1*x1 + 25.0*y1*y1 > LAMBDA*LAMBDA*(1.0 + padding));
}
case (S_COANDA):
{
return(vabs(y - SEGMENTS_Y0 - 0.25*cos(PI*x/XMAX)) > 0.1);
}
case (S_COANDA_SHORT):
{
x1 = XMIN + 0.1;
length = 0.85*(XMAX - XMIN - 0.2);
if (x > x1 + length + 0.1) return(1);
return(vabs(y - SEGMENTS_Y0 + 0.2*cos(DPI*(x-x1)/length)) > 0.05);
}
case (S_CYLINDER):
{
if (y > YMAX - 0.05 - MU) return(0);
if (y < YMIN + 0.05 + MU) return(0);
return(1);
}
case (S_MAZE):
{
for (i=0; i<nsegments; i++)
{
if (distance_to_segment(x, y, segment[i].x1, segment[i].y1, segment[i].x2, segment[i].y2) < 5.0*MAZE_WIDTH) return(0);
}
return(1);
}
case (S_MAZE_DIAG):
{
for (i=0; i<nsegments; i++)
{
if (distance_to_segment(x, y, segment[i].x1, segment[i].y1, segment[i].x2, segment[i].y2) < 5.0*MAZE_WIDTH) return(0);
}
return(1);
}
default: return(1);
}
}
void rotate_segments(t_segment segment[NMAXSEGMENTS], double angle)
/* rotates the repelling segments by given angle */
{
int i;
double ca, sa;
ca = cos(angle);
sa = sin(angle);
for (i=0; i<nsegments; i++)
{
segment[i].x1 = ca*segment[i].x01 - sa*segment[i].y01;
segment[i].y1 = sa*segment[i].x01 + ca*segment[i].y01;
segment[i].x2 = ca*segment[i].x02 - sa*segment[i].y02;
segment[i].y2 = sa*segment[i].x02 + ca*segment[i].y02;
segment[i].nx = ca*segment[i].nx0 - sa*segment[i].ny0;
segment[i].ny = sa*segment[i].nx0 + ca*segment[i].ny0;
if (segment[i].concave)
{
segment[i].angle1 = segment[i].angle01 + angle;
segment[i].angle2 = segment[i].angle02 + angle;
while (segment[i].angle1 > DPI)
{
segment[i].angle1 -= DPI;
segment[i].angle2 -= DPI;
}
}
}
}
/* Computation of interaction force */
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);
}
}
double harmonic_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 = rplus/particle.eq_dist*particle.radius;
ratio = r/particle.eq_dist*particle.radius;
if (ratio < 1.0) return(ratio - 1.0);
else return(0.0);
}
}
double coulomb_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 = rplus/particle.eq_dist*particle.radius;
ratio = r/particle.eq_dist*particle.radius;
return(-1.0/(ratio*ratio + rmin*rmin));
// if (ratio < 1.0) return(ratio - 1.0);
// else return(0.0);
}
}
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;
}
}
double water_torque(double r, double ca, double sa, double ca_rel, double sa_rel, double ck_rel, double sk_rel)
/* compute torque of water molecule #k on water molecule #j (for interaction I_LJ_WATER) - OLD VERSION */
{
double c1p, c1m, c2p, c2m, s2p, s2m, s21, s21p, s21m, c21, c21p, c21m, torque;
double r2, rd, rd2, rr[3][3];
static double cw = -0.5, sw = 0.866025404, delta = 1.5*MU, d2 = 2.25*MU*MU;
int i, j;
c1p = ck_rel*cw - sk_rel*sw;
c1m = ck_rel*cw + sk_rel*sw;
c2p = ca_rel*cw - sa_rel*sw;
c2m = ca_rel*cw + sa_rel*sw;
s2p = sa_rel*cw + ca_rel*sw;
s2m = sa_rel*cw - ca_rel*sw;
s21 = sa_rel*ck_rel - ca_rel*sk_rel;
c21 = ca_rel*ck_rel + sa_rel*sk_rel;
s21p = s21*cw - c21*sw;
s21m = s21*cw + c21*sw;
c21p = c21*cw + s21*sw;
c21m = c21*cw - s21*sw;
r2 = r*r;
rd = 2.0*r*delta;
rd2 = r2 + d2;
rr[0][0] = r;
rr[0][1] = sqrt(rd2 + rd*c2p);
rr[0][2] = sqrt(rd2 + rd*c2m);
rr[1][0] = sqrt(rd2 - rd*c1p);
rr[2][0] = sqrt(rd2 - rd*c1m);
rr[1][1] = sqrt(r2 + rd*(c2p - c1p) + 2.0*d2*(1.0 - c21));
rr[1][2] = sqrt(r2 + rd*(c2m - c1p) + 2.0*d2*(1.0 - c21m));
rr[2][1] = sqrt(r2 + rd*(c2p - c1m) + 2.0*d2*(1.0 - c21p));
rr[2][2] = sqrt(r2 + rd*(c2m - c1m) + 2.0*d2*(1.0 - c21));
for (i=0; i<3; i++) for (j=0; j<3; j++)
{
if (rr[i][j] < 1.0e-4) rr[i][j] = 1.0e-4;
rr[i][j] = rr[i][j]*rr[i][j]*rr[i][j];
}
torque = rd*(s2p/rr[0][1] + s2m/rr[0][2]);
torque += -0.5*rd*(s2p/rr[1][1] + s2p/rr[2][1] + s2m/rr[1][2] + s2m/rr[2][2]);
torque += d2*(s21/rr[1][1] + s21/rr[2][2] + s21m/rr[1][2] + s21p/rr[2][1]);
return(torque);
}
double water_force(double r, double ca, double sa, double ca_rel, double sa_rel, double ck_rel, double sk_rel, double f[2])
/* compute force and torque of water molecule #k on water molecule #j (for interaction I_LJ_WATER) */
{
double x1[3], y1[3], x2[3], y2[3], rr[3][3], dx[3][3], dy[3][3], fx[3][3], fy[3][3], m[3][3], torque = 0.0;
static double cw[3], sw[3], q[3], d[3], delta = 1.25*MU, dmin = 0.5*MU, fscale = 1.0;
int i, j;
static int first = 1;
if (first)
{
cw[0] = 1.0; cw[1] = -0.5; cw[2] = -0.5;
sw[0] = 0.0; sw[1] = 866025404; sw[2] = -866025404; /* sines and cosines of angles */
q[0] = -2.0; q[1] = 1.0; q[2] = 1.0; /* charges */
d[0] = 0.5*delta; d[1] = delta; d[2] = delta; /* distances to center */
first = 0;
}
/* positions of O and H atoms */
for (i=0; i<3; i++)
{
x1[i] = d[i]*(ca_rel*cw[i] - sa_rel*sw[i]);
y1[i] = d[i]*(ca_rel*sw[i] + sa_rel*cw[i]);
x2[i] = r + d[i]*(ck_rel*cw[i] - sk_rel*sw[i]);
y2[i] = d[i]*(ck_rel*sw[i] + sk_rel*cw[i]);
}
/* relative positions */
for (i=0; i<3; i++) for (j=0; j<3; j++)
{
dx[i][j] = x2[j] - x1[i];
dy[i][j] = y2[j] - y1[i];
rr[i][j] = module2(dx[i][j], dy[i][j]);
if (rr[i][j] < dmin) rr[i][j] = dmin;
rr[i][j] = ipow(rr[i][j],3);
// rr[i][j] = rr[i][j]*rr[i][j]*rr[i][j];
}
/* forces between particles */
for (i=0; i<3; i++) for (j=0; j<3; j++)
{
fx[i][j] = -q[i]*q[j]*dx[i][j]/rr[i][j];
fy[i][j] = -q[i]*q[j]*dy[i][j]/rr[i][j];
}
/* torques between particles */
for (i=0; i<3; i++) for (j=0; j<3; j++)
{
m[i][j] = x1[i]*fy[i][j] - y1[i]*fx[i][j];
}
/* total force */
f[0] = 0.0;
f[1] = 0.0;
for (i=0; i<3; i++) for (j=0; j<3; j++)
{
f[0] += fscale*fx[i][j];
f[1] += fscale*fy[i][j];
torque += fscale*m[i][j];
}
return(torque);
}
void segment_interaction(t_particle part_i, t_particle part_k, double ca, double sa, double distance, double ffm[3], int charge)
/* compute interaction for I_SEGMENT interaction */
/* computes force and torque of particle k on particle i */
{
int s;
double x0, y0, x, y, fx, fy, cb, sb, cc, sc, cab, sab, ccb, scb, r;
double width, torque, mu_i, mu_k, r3, endfact, cfact;
fx = 0.0;
fy = 0.0;
torque = 0.0;
mu_i = part_i.radius;
mu_k = part_k.radius;
width = 0.2*mu_i;
cb = cos(part_i.angle);
sb = sin(part_i.angle);
cc = cos(part_k.angle);
sc = sin(part_k.angle);
cab = ca*cb + sa*sb;
sab = sa*cb - ca*sb;
ccb = cc*cb + sc*sb;
scb = sc*cb - cc*sb;
/* relative coordinates of particle k in basis where particle i is horizontal */
x0 = distance*cab;
y0 = distance*sab;
for (s=-1; s<=1; s+=2)
{
x = x0 + (double)s*mu_k*ccb;
y = y0 + (double)s*mu_k*scb;
if (vabs(y) < width)
{
if (vabs(x) < mu_i)
{
// if (((double)s*scb < 0.0)&&(y > 0.0)) fy += y - width;
if (y > 0.0) fy += y - width;
else fy += y + width;
}
else if ((x >= mu_i)&&(x < mu_i+width))
{
r = module2(x-mu_i, y) + 1.0e-10;
if (r < width)
{
fx -= (width - r)*(x-mu_i)/r;
fy -= (width - r)*y/r;
}
}
else if ((x <= -mu_i)&&(x > -mu_i-width))
{
r = module2(x+mu_i, y) + 1.0e-10;
if (r < width)
{
fx -= (width - r)*(x+mu_i)/r;
fy -= (width - r)*y/r;
}
}
torque += x*fy - y*fx;
}
if (charge) /* add Coulomb force between ends */
{
cfact = CHARGE*KREPEL/KSPRING_OBSTACLE;
r = module2(x-mu_i,y) + 1.0e-2;
r3 = r*r*r;
fx += cfact*(double)s*(x+mu_i)/r3;
fy += cfact*(double)s*y/r3;
torque += mu_i*fx;
r = module2(x+mu_i,y) + 1.0e-2;
r3 = r*r*r;
fx -= cfact*(double)s*(x+mu_i)/r3;
fy -= cfact*(double)s*y/r3;
torque -= mu_i*fy;
}
}
ffm[0] = cb*fx - sb*fy;
ffm[1] = sb*fx + cb*fy;
ffm[2] = torque;
}
void polygon_interaction(t_particle part_i, t_particle part_k, double ca, double sa, double distance, double ffm[3], int charge)
/* compute interaction for I_POLYGON interaction */
/* computes force and torque of particle k on particle i */
{
int s, k, s0, s1;
double x0, y0, x, y, f, fx, fy, cb, sb, cab, sab, r, r0, z2, phi, ckt, skt, d, fx1, fy1, dmax2;
double torque, mu_i, mu_k, r3, width, gamma_beta, rot, cr, sr, xx[NPOLY], yy[NPOLY], dd[NPOLY], z, z0, z02, d1, dmin;
static double theta, twotheta, ctheta, stheta, cornerx[NPOLY+1], cornery[NPOLY+1], nx[NPOLY], ny[NPOLY];
static int first = 1;
if (first)
{
theta = PI/(double)NPOLY;
twotheta = 2.0*theta;
ctheta = cos(theta);
stheta = sin(theta);
for (s=0; s<NPOLY+1; s++)
{
cornerx[s] = cos((double)s*twotheta);
cornery[s] = sin((double)s*twotheta);
}
for (s=0; s<NPOLY; s++)
{
nx[s] = cos((double)(2*s+1)*theta);
ny[s] = sin((double)(2*s+1)*theta);
}
first = 0;
}
fx = 0.0;
fy = 0.0;
torque = 0.0;
mu_i = part_i.radius;
mu_k = part_k.radius;
dmax2 = 4.0*mu_i*mu_i;
width = 0.1*mu_i;
mu_i -= width;
gamma_beta = part_k.angle - part_i.angle;
r0 = mu_i*ctheta;
z0 = mu_i*stheta;
z02 = z0*z0;
cb = cos(part_i.angle);
sb = sin(part_i.angle);
cab = ca*cb + sa*sb;
sab = sa*cb - ca*sb;
/* relative coordinates of particle k in basis where particle i is horizontal */
x0 = distance*cab;
y0 = distance*sab;
/* find vertex distances */
for (s = 0; s<NPOLY; s++)
{
rot = gamma_beta + (double)s*twotheta;
cr = cos(rot);
sr = sin(rot);
xx[s] = x0 + cr*mu_k;
yy[s] = y0 + sr*mu_k;
dd[s] = xx[s]*xx[s] + yy[s]*yy[s];
}
/* compute force from vertices */
for (s = 0; s<NPOLY; s++) if (dd[s] < dmax2)
{
x = xx[s];
y = yy[s];
phi = argument(x, y);
if (phi < 0.0) phi += DPI;
k = (int)(phi/twotheta);
d = x*nx[k] + y*ny[k];
if (d < r0 + width)
{
z = -x*ny[k] + y*nx[k];
if (z*z < z02)
{
f = r0 + width - d;
if (f > width) f = width;
fx1 = f*nx[k];
fy1 = f*ny[k];
fx -= fx1;
fy -= fy1;
torque += x*fy1 - y*fx1;
// fx -= f*nx[k];
// fy -= f*ny[k];
// torque += x*fy - y*fx;
}
else for (s1=0; s1<NPOLY; s1++) /* corners */
{
d1 = module2(x - mu_i*cornerx[s1], y - mu_i*cornery[s1]) + 1.0e-8;
fx1 = 0.0;
fy1 = 0.0;
if (d1 < width)
{
f = width - d1;
if (f > width) f = width;
fx1 -= f*(x - mu_i*cornerx[s1])/d1;
fy1 -= f*(y - mu_i*cornery[s1])/d1;
}
fx += fx1;
fy += fy1;
torque += x*fy1 - y*fx1;
}
}
}
ffm[0] = cb*fx - sb*fy;
ffm[1] = sb*fx + cb*fy;
ffm[2] = torque;
}
int dna_charge(int type1, int type2, int coulomb)
/* returns -1 for matching nucleotide ends, 1 for non-matching, and 0 else */
{
int min, max;
// if (coulomb == 0) return(0);
if (type1 == 0) return(0);
if (type2 == 0) return(0);
if ((type1 == 7)&&(type2 == 7)) return(2);
if (type1 == type2) return(1);
if (type1 > type2)
{
max = type1;
min = type2;
}
else
{
max = type2;
min = type1;
}
/* enzymes */
if ((min >= 3)&&(max == 7)) return(-10);
if (max - min >= 2) return(1);
if (min == 1) return(-4*coulomb);
if (min%2 == 1) return(-10*coulomb);
return(1);
}
int compute_particle_interaction(int i, int k, double force[2], double *torque, t_particle* particle, double distance, double krepel, double ca, double sa, double ca_rel, double sa_rel)
/* compute repelling force and torque of particle #k on particle #i */
/* returns 1 if distance between particles is smaller than NBH_DIST_FACTOR*MU */
{
double x1, y1, x2, y2, r, f, angle, aniso, fx, fy, ff[2], dist_scaled, spin_f, ck, sk, ck_rel, sk_rel, alpha, amp, charge, f1, ffm[3];
static double dxhalf = 0.5*(BCXMAX - BCXMIN), dyhalf = 0.5*(BCYMAX - BCYMIN);
int wwrapx, wwrapy, twrapx, twrapy;
if (BOUNDARY_COND == BC_GENUS_TWO)
{
dxhalf *= 0.75;
dyhalf *= 0.75;
}
x1 = particle[i].xc;
y1 = particle[i].yc;
x2 = particle[k].xc;
y2 = particle[k].yc;
wwrapx = ((BOUNDARY_COND == BC_KLEIN)||(BOUNDARY_COND == BC_BOY)||(BOUNDARY_COND == BC_GENUS_TWO))&&(vabs(x2 - x1) > dxhalf);
wwrapy = ((BOUNDARY_COND == BC_BOY)||(BOUNDARY_COND == BC_GENUS_TWO))&&(vabs(y2 - y1) > dyhalf);
twrapx = ((BOUNDARY_COND == BC_KLEIN)||(BOUNDARY_COND == BC_BOY))&&(vabs(x2 - x1) > dxhalf);
twrapy = (BOUNDARY_COND == BC_BOY)&&(vabs(y2 - y1) > dyhalf);
switch (particle[k].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[k]);
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[k]);
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[k]);
force[0] = krepel*ff[0];
force[1] = krepel*ff[1];
break;
}
case (I_GOLDENRATIO):
{
f = krepel*golden_ratio_force(distance, particle[k]);
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[k]);
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[k]);
force[0] = krepel*ff[0];
force[1] = krepel*ff[1];
break;
}
case (I_LJ_WATER):
{
f = krepel*lennard_jones_force(distance, particle[k]);
force[0] = f*ca;
force[1] = f*sa;
break;
}
case (I_VICSEK):
{
force[0] = 0.0;
force[1] = 0.0;
break;
}
case (I_VICSEK_REPULSIVE):
{
f = krepel*coulomb_force(distance, particle[k]);
// f = krepel*harmonic_force(distance, particle[k]);
// f = krepel*lennard_jones_force(distance, particle[k]);
force[0] = f*ca;
force[1] = f*sa;
break;
}
case (I_VICSEK_SPEED):
{
f = cos(0.5*(particle[k].angle - particle[i].angle));
force[0] = f*KSPRING_VICSEK*(particle[k].vx - particle[i].vx);
force[1] = f*KSPRING_VICSEK*(particle[k].vy - particle[i].vy);
break;
}
case (I_VICSEK_SHARK):
{
if (particle[k].type == particle[i].type)
{
f = cos(0.5*(particle[k].angle - particle[i].angle));
force[0] = f*KSPRING_VICSEK*(particle[k].vx - particle[i].vx);
force[1] = f*KSPRING_VICSEK*(particle[k].vy - particle[i].vy);
}
else if (particle[i].type != 2)
{
f = krepel*coulomb_force(distance, particle[k]);
force[0] = f*ca;
force[1] = f*sa;
}
else
{
if (VICSEK_REPULSION > 0.0)
{
// f = VICSEK_REPULSION*harmonic_force(distance, particle[k]);
f = VICSEK_REPULSION*coulomb_force(distance, particle[k]);
force[0] = f*ca;
force[1] = f*sa;
}
else
{
force[0] = 0.0;
force[1] = 0.0;
}
}
break;
}
case (I_COULOMB_LJ):
{
charge = particle[i].charge*particle[k].charge;
f = -100.0*krepel*charge/(1.0e-12 + distance*distance);
if (charge <= 0.0)
f += COULOMB_LJ_FACTOR*krepel*lennard_jones_force(distance, particle[k]);
force[0] = f*ca;
force[1] = f*sa;
break;
}
case (I_COULOMB_PENTA):
{
charge = particle[i].charge*particle[k].charge;
if (charge < 0.0)
{
penta_lj_force(distance, ca, sa, ca_rel, sa_rel, ff, particle[k]);
force[0] = -charge*krepel*ff[0];
force[1] = -charge*krepel*ff[1];
}
else
{
f = krepel*lennard_jones_force(distance, particle[k]);
force[0] = f*ca;
force[1] = f*sa;
}
break;
}
case (I_COULOMB_IMAGINARY):
{
charge = particle[i].charge*particle[k].charge;
f = -100.0*krepel*charge/(1.0e-12 + distance*distance);
if (charge <= 0.0)
f1 = 0.01*krepel*lennard_jones_force(distance, particle[k]);
else f1 = 0.0;
force[0] = f1*ca - f*sa;
force[1] = f1*sa + f*ca;
break;
}
case (I_DNA_CHARGED):
{
f = 0.2*krepel*lennard_jones_force(distance, particle[k]);
charge = CHARGE*(double)dna_charge(particle[i].type, particle[k].type, particle[i].coulomb);
if ((RD_REACTION == CHEM_DNA_ENZYME)||(RD_REACTION == CHEM_DNA_ENZYME_REPAIR))
{
/* make some interactions repulsive */
if ((particle[i].reactive == 0)&&(particle[k].reactive == 0)&&(particle[i].added == particle[k].added))
charge = vabs(charge);
/* TEST */
/* make interactions repulsive between base-paired and other molecules */
// else if (particle[i].paired != particle[k].paired)
// charge = vabs(charge);
}
f -= krepel*charge/(1.0e-12 + distance*distance);
force[0] = f*ca;
force[1] = f*sa;
break;
}
case (I_DNA_CHARGED_B):
{
f = 0.2*krepel*lennard_jones_force(distance, particle[k]);
charge = CHARGE*(double)dna_charge(particle[i].type, particle[k].type, particle[i].coulomb);
if (particle[i].added != particle[k].added) charge *= 1.5;
if ((RD_REACTION == CHEM_DNA_ENZYME)||(RD_REACTION == CHEM_DNA_ENZYME_REPAIR))
{
/* make some interactions repulsive */
if ((particle[i].reactive == 0)&&(particle[k].reactive == 0)&&(particle[i].added == particle[k].added))
charge = vabs(charge);
/* TEST */
/* make interactions repulsive between base-paired and other molecules */
// else if (particle[i].paired != particle[k].paired)
// charge = vabs(charge);
}
f -= krepel*charge/(1.0e-12 + distance*distance);
force[0] = f*ca;
force[1] = f*sa;
break;
}
case (I_SEGMENT):
{
segment_interaction(particle[i], particle[k], ca, sa, distance, ffm, 0);
force[0] = KSPRING_OBSTACLE*ffm[0];
force[1] = KSPRING_OBSTACLE*ffm[1];
*torque = KTORQUE*ffm[2];
f = 0.1*krepel*lennard_jones_force(distance, particle[k]);
force[0] += f*ca;
force[1] += f*sa;
break;
}
case (I_SEGMENT_CHARGED):
{
segment_interaction(particle[i], particle[k], ca, sa, distance, ffm, 1);
force[0] = KSPRING_OBSTACLE*ffm[0];
force[1] = KSPRING_OBSTACLE*ffm[1];
*torque = KTORQUE*ffm[2];
// printf("Force between particles %i and %i: (%.3lg, %.3lg)\n", i, k, force[0], force[1]);
// f = 0.1*krepel*lennard_jones_force(distance, particle[k]);
// force[0] += f*ca;
// force[1] += f*sa;
break;
}
case (I_POLYGON):
{
polygon_interaction(particle[i], particle[k], ca, sa, distance, ffm, 0);
force[0] = KSPRING_OBSTACLE*ffm[0];
force[1] = KSPRING_OBSTACLE*ffm[1];
*torque = KTORQUE*ffm[2];
f = 0.1*krepel*lennard_jones_force(distance, particle[k]);
force[0] += f*ca;
force[1] += f*sa;
break;
}
case (I_POLYGON_ALIGN):
{
polygon_interaction(particle[i], particle[k], ca, sa, distance, ffm, 0);
force[0] = KSPRING_OBSTACLE*ffm[0];
force[1] = KSPRING_OBSTACLE*ffm[1];
*torque = KTORQUE*ffm[2];
f = 0.1*krepel*lennard_jones_force(distance, particle[k]);
force[0] += f*ca;
force[1] += f*sa;
break;
}
}
if (ROTATION)
{
dist_scaled = distance/(particle[i].spin_range*particle[i].radius);
switch (particle[k].interaction) {
case (I_LJ_WATER):
{
ck = cos(particle[k].angle);
sk = sin(particle[k].angle);
ck_rel = ca*ck + sa*sk;
sk_rel = sa*ck - ca*sk;
// *torque = (-3.0*ca_rel*sk_rel + 2.0*sa_rel*ck_rel)/(1.0e-12 + dist_scaled*dist_scaled*dist_scaled);
// *torque = water_torque(distance, ca, sa, ca_rel, sa_rel, ck_rel, sk_rel);
// *torque = (0.5*sin(angle) + 0.5*sin(2.0*angle) - 0.45*sin(3.0*angle))/(1.0e-12 + dist_scaled*dist_scaled*dist_scaled);
*torque = water_force(distance, ca, sa, ca_rel, sa_rel, ck_rel, sk_rel, ff);
force[0] += ff[0];
force[1] += ff[1];
// printf("force = (%.3lg, %.3lg)\n", ff[0], ff[1]);
break;
}
case (I_VICSEK):
{
if (dist_scaled > 1.0) *torque = 0.0;
else if (twrapx||twrapy) *torque = sin(-particle[k].angle - particle[i].angle);
else *torque = sin(particle[k].angle - particle[i].angle);
break;
}
case (I_VICSEK_REPULSIVE):
{
if (dist_scaled > 1.0) *torque = 0.0;
else if (twrapx||twrapy) *torque = sin(-particle[k].angle - particle[i].angle);
else *torque = sin(particle[k].angle - particle[i].angle);
break;
}
case (I_VICSEK_SPEED):
{
if (dist_scaled > 1.0) *torque = 0.0;
else if (twrapx||twrapy) *torque = sin(-particle[k].angle - particle[i].angle);
else *torque = sin(particle[k].angle - particle[i].angle);
break;
}
case (I_VICSEK_SHARK):
{
if (dist_scaled > 10.0) *torque = 0.0;
else if (particle[k].type == particle[i].type) /* fish adjusting direction */
{
if (twrapx||twrapy) *torque = sin(-particle[k].angle - particle[i].angle);
else *torque = sin(particle[k].angle - particle[i].angle);
}
else if (particle[k].type == 2) /* fish fleeing a shark */
{
alpha = argument(ca,sa);
particle[i].angle = alpha + PI;
*torque = 0.0;
}
else /* shark tracking fish */
{
*torque = cos(particle[k].angle)*sa - sin(particle[k].angle)*ca;
}
break;
}
case (I_SEGMENT):
{
/* Do nothing, torque already computed */
break;
}
case (I_SEGMENT_CHARGED):
{
/* Do nothing, torque already computed */
break;
}
case (I_POLYGON):
{
/* Do nothing, torque already computed */
break;
}
case (I_POLYGON_ALIGN):
{
spin_f = particle[i].spin_freq;
if (twrapx||twrapy)
*torque += sin(spin_f*(-particle[k].angle - particle[i].angle))/(1.0e-8 + dist_scaled*dist_scaled);
else
{
if (NPOLY%2 == 0)
*torque += sin(spin_f*(particle[k].angle - particle[i].angle))/(1.0e-8 + dist_scaled*dist_scaled);
else
*torque -= sin(spin_f*(particle[k].angle - particle[i].angle))/(1.0e-8 + dist_scaled*dist_scaled);
}
break;
}
default:
{
spin_f = particle[i].spin_freq;
if (twrapx||twrapy) *torque = sin(spin_f*(-particle[k].angle - particle[i].angle))/(1.0e-8 + dist_scaled*dist_scaled);
else
*torque = sin(spin_f*(particle[k].angle - particle[i].angle))/(1.0e-8 + dist_scaled*dist_scaled);
}
}
if (particle[i].type == particle[k].type)
{
if (particle[i].type == 0) *torque *= KTORQUE;
else *torque *= KTORQUE_B;
}
else *torque *= KTORQUE_DIFF;
}
else *torque = 0.0;
if ((distance < NBH_DIST_FACTOR*particle[i].radius)&&(k != i)) return(1);
// if ((distance < NBH_DIST_FACTOR*particle[i].radius)) return(1);
else return(0);
}
int compute_repelling_force(int i, int j, double force[2], double *torque, t_particle* particle, double krepel)
/* compute repelling force of neighbour #j on particle #i */
/* returns 1 if distance between particles is smaller than NBH_DIST_FACTOR*MU */
{
double distance, ca, sa, cj, sj, ca_rel, sa_rel, f[2], ff[2], torque1, ck, sk, ck_rel, sk_rel;
static double distmin = 10.0*((XMAX - XMIN)/HASHX + (YMAX - YMIN)/HASHY);
int interact, k;
if (BOUNDARY_COND == BC_GENUS_TWO) distmin *= 2.0;
k = particle[i].hashneighbour[j];
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
/* for monitoring purposes */
// if (distance > distmin)
// {
// printf("i = %i, hashcell %i, j = %i, hashcell %i\n", i, particle[i].hashcell, k, particle[k].hashcell);
// printf("X = (%.3lg, %.3lg)\n", particle[i].xc, particle[i].yc);
// printf("Y = (%.3lg, %.3lg) d = %.3lg\n", particle[k].xc, particle[k].yc, distance);
// }
if ((distance == 0.0)||(i == k))
{
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
{
/* to avoid numerical problems, assign minimal value to distance */
if (distance < 0.1*particle[i].radius) distance = 0.1*particle[i].radius;
ca = (particle[i].deltax[j])/distance;
sa = (particle[i].deltay[j])/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;
}
interact = compute_particle_interaction(i, k, f, torque, particle, distance, krepel, ca, sa, ca_rel, sa_rel);
if (SYMMETRIZE_FORCE)
{
torque1 = *torque;
// compute_particle_interaction(k, i, ff, torque, particle, distance, krepel, ca, sa, ca_rel, sa_rel);
ck = cos(particle[j].angle);
sk = sin(particle[j].angle);
ck_rel = ca*ck + sa*sk;
sk_rel = sa*ck - ca*sk;
compute_particle_interaction(k, i, ff, torque, particle, distance, krepel, -ca, -sa, -ck_rel, -sk_rel);
force[0] = 0.5*(f[0] - ff[0]);
force[1] = 0.5*(f[1] - ff[1]);
*torque = 0.5*(torque1 - *torque);
// *torque = 0.5*(*torque + torque1);
}
else
{
force[0] = f[0];
force[1] = f[1];
}
// printf("force = (%.3lg, %.3lg), torque = %.3lg\n", f[0], f[1], *torque);
return(interact);
}
}
int resample_particle(int n, int maxtrials, t_particle particle[NMAXCIRCLES])
/* resample y coordinate of particle n, returns 1 if no collision is created */
{
double x, y, dist, dmin = 10.0;
int i, j, closeby = 0, success = 0, trials = 0;
while ((!success)&&(trials < maxtrials))
{
success = 1;
x = particle[n].xc - MU*(double)rand()/RAND_MAX;
y = 0.95*(BCYMIN + (BCYMAX - BCYMIN)*(double)rand()/RAND_MAX);
i = 0;
while ((success)&&(i<ncircles))
{
if ((i!=n)&&(particle[i].active))
{
// dist = module2(x - particle[i].xc, y - particle[i].yc);
for (j=-1; j<2; j++)
{
dist = module2(x - particle[i].xc - (double)j*(BCXMAX - BCXMIN), y - particle[i].yc);
if (dist < dmin) dmin = dist;
}
if (dmin < SAFETY_FACTOR*MU) success = 0;
}
i++;
}
trials++;
// printf("Trial no %i - (%.3lg, %.3lg)\t", trials, x, y);
}
if (success)
{
printf("\nTrial %i succesful\n", trials);
printf("Moving particle %i from (%.3lg, %.3lg) to (%.3lg, %.3lg)\n\n", n, particle[n].xc, particle[n].yc, x, y);
particle[n].xc = x;
particle[n].yc = y;
particle[n].vx = V_INITIAL*gaussian();
particle[n].vy = V_INITIAL*gaussian();
// particle[n].vy = V_INITIAL*(double)rand()/RAND_MAX;
return(1);
}
else
{
printf("\nCannot move particle %i\n\n", n);
return(0);
}
}
void print_partners(int i, t_particle particle[NMAXCIRCLES])
/* print partner list and properties, for debugging */
{
int p;
if (particle[i].active)
{
// printf("Particle %i, type %i, charge %.3lg\n", i, particle[i].type, particle[i].charge);
fprintf(lj_log, "Particle %i, type %i, charge %.3lg\n", i, particle[i].type, particle[i].charge);
// printf("(x, y) = (%.3lg, %.3lg), radius = %.3lg\n", particle[i].xc, particle[i].yc, particle[i].radius);
printf("%i partners: ", particle[i].npartners);
fprintf(lj_log, "%i partners: ", particle[i].npartners);
for (p=0; p<particle[i].npartners; p++)
{
printf("%i ", particle[i].partner[p]);
fprintf(lj_log, "%i ", particle[i].partner[p]);
}
printf("\n\n");
fprintf(lj_log, "\n\n");
}
}
void add_particle_inpair(int i, int nlist[NMAXPARTNERS], double angle0, int pairing_type, int npartners, int newtype, int rtype, double newr, double newmass, double newcharge, int narms, t_particle particle[NMAXCIRCLES])
/* add new particles in list nlist to old particle i, for paired particles */
{
int n, k, l, p, p1, q, counter, oldtype, closeby = 0, different = 1, armlen, arm, kplus, kminus, kpplus, kmminus;
short int no_change = 0;
double angle, dist, oldr, oldmass_inv, oldcharge, x, y, alpha, beta, beta2, r;
oldtype = particle[i].type;
oldr = particle[i].radius;
oldmass_inv = particle[i].mass_inv;
oldcharge = particle[i].charge;
if (pairing_type == POLY_SOAP_NMIX)
{
if ((double)rand()/RAND_MAX < THIRD_TYPE_PROPORTION) no_change = 1;
}
armlen = npartners/narms;
if (rtype == 0) rtype = 3 + rand()%4;
particle[i].npartners = npartners;
particle[i].p0 = i;
particle[i].p1 = nlist[0];
/* shift center of polymer for long chains */
if ((pairing_type == POLY_SOAP_B)||(pairing_type == POLY_SOAP_N)||(pairing_type == POLY_SOAP_NMIX)||(pairing_type == POLY_PLUSMINUS))
{
dist = 0.5*PAIR_DRATIO*oldr*(double)(npartners);
particle[i].xc -= dist*cos(angle);
particle[i].yc -= dist*cos(angle);
}
/* set some common values for POLY_SEG_POLYGON pairing */
else if (pairing_type == POLY_SEG_POLYGON)
{
// alpha = PI/(double)(npartners+1);
alpha = PI/(double)npartners;
r = MU/tan(alpha);
particle[i].angle = 0.0;
// particle[i].radius *= 0.75;
}
if (pairing_type == POLY_PLUSMINUS)
{
particle[i].type = newtype;
particle[i].radius = newr;
}
if (pairing_type == POLY_DNA_ALT) particle[i].angle = angle0;
/* randomize sign of charge */
if ((pairing_type == POLY_STAR_CHARGED)||(pairing_type == POLY_POLYGON))
if (rand()%2 == 1) newcharge *= -1.0;
for (k=0; k<npartners; k++)
{
n = nlist[k];
particle[n].type = newtype;
particle[n].thermostat = 1;
particle[n].charge = newcharge;
particle[n].active = 1;
particle[n].cluster = particle[i].cluster;
particle[n].p0 = i;
particle[n].p1 = nlist[0];
/* set first partner */
/* TODO; adapt mol_angle to other pairing types? */
// particle[i].mol_angle = 1;
if ((pairing_type == POLY_SOAP_B)||(pairing_type == POLY_SOAP_N)||(pairing_type == POLY_SOAP_NMIX)||(pairing_type == POLY_PLUSMINUS))
{
particle[i].npartners = 1;
particle[i].partner[0] = nlist[0];
}
else if ((pairing_type == POLY_HYDRA)||(pairing_type == POLY_HYDRA_RIGID))
{
particle[i].radius = newr;
particle[i].npartners = narms;
for (l=0; l<narms; l++) particle[i].partner[l] = nlist[l*armlen];
// particle[i].mol_angle = narms;
}
else particle[i].partner[k] = n;
// particle[k].mol_angle = particle[i].mol_angle;
// printf("Particle %i mol_angle = %i\n", i, particle[k].mol_angle);
/* compute positions of new particles */
switch (pairing_type) {
case (POLY_STAR_CHARGED):
{
particle[i].radius = 2.0*MU;
angle = angle0 + DPI*(double)k/(double)npartners;
dist = (oldr + newr)*PAIR_DRATIO;
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
if (k%2 == 1) particle[n].charge *= -1.0;
break;
}
case (POLY_POLYGON):
{
particle[i].radius = 2.0*MU;
particle[n].mass_inv = 1.0/newmass;
if (k < npartners/2)
{
angle = angle0 + DPI*(double)(2*k+1)/(double)npartners;
dist = (oldr + newr)*PAIR_DRATIO*cos(DPI/(double)npartners)*1.2;
particle[n].radius = newr*0.75;
particle[n].charge = 0.0;
}
else
{
angle = angle0 + DPI*(double)(2*k)/(double)npartners;
dist = (oldr + newr)*PAIR_DRATIO;
particle[n].radius = newr;
if (k%2 == 1) particle[n].charge *= -1.0;
}
break;
}
case (POLY_KITE):
{
particle[i].radius = 1.25*MU*sin(PI/5.0);
particle[i].charge = 0.0;
particle[n].mass_inv = 1.0/newmass;
switch (k){
case (0):
{
angle = PI;
dist = 0.5*MU;
particle[n].radius = 0.4*MU;
particle[n].charge = 0.0;
break;
}
case (1):
{
angle = 0.0;
dist = 2.0*MU*cos(DPI/5.0);
particle[n].radius = 0.3*MU;
break;
}
case (2):
{
angle = DPI/5.0;
dist = MU;
particle[n].radius = 0.3*MU;
particle[n].charge *= -1.0;
break;
}
case (3):
{
angle = PI;
dist = MU;
particle[n].radius = 0.3*MU;
break;
}
case (4):
{
angle = -DPI/5.0;
dist = MU;
particle[n].radius = 0.3*MU;
particle[n].charge *= -1.0;
break;
}
}
break;
}
case (POLY_DART):
{
particle[i].radius = 0.3*MU;
particle[i].charge = -newcharge;
particle[n].mass_inv = 1.0/newmass;
switch (k){
case (0):
{
angle = 3.0*PI/10.0;
dist = MU*sin(PI/5.0);
particle[n].radius = 0.3*MU;
particle[n].charge = 0.0;
break;
}
case (1):
{
angle = -3.0*PI/10.0;
dist = MU*sin(PI/5.0);
particle[n].radius = 0.3*MU;
particle[n].charge = 0.0;
break;
}
case (2):
{
angle = 0.0;
dist = MU;
particle[n].radius = 0.3*MU;
particle[n].charge *= -1.0;
break;
}
case (3):
{
angle = 3.0*PI/5.0;
dist = MU;
particle[n].radius = 0.3*MU;
break;
}
case (4):
{
angle = -3.0*PI/5.0;
dist = MU;
particle[n].radius = 0.3*MU;
break;
}
}
break;
}
case (POLY_SEG_POLYGON):
{
dist = r;
angle = (double)(2*k)*alpha;
particle[n].angle = angle + PID;
particle[n].radius = MU;
particle[n].charge = 0.0;
fprintf(lj_log, "k = %i, angle = %.3lg, orientation = %.3lg\n", k, angle, particle[n].angle);
break;
}
case (POLY_WATER):
{
angle = angle0 + (double)(k+1)*PARTNER_ANGLE*PI/180.0;
dist = oldr + newr;
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
break;
}
case (POLY_SOAP):
{
if (k%2 == 0) angle = angle0;
else angle = angle0 + PI;
dist = 2.0*PAIR_DRATIO*oldr*(double)(k/2+1);
particle[i].charge = 0.0;
if (k==npartners-1)
{
particle[n].charge = oldcharge;
particle[n].type = newtype;
}
else
{
particle[n].charge = 0.0;
particle[n].type = oldtype;
}
if (k == npartners-1) particle[n].radius = newr;
else particle[n].radius = oldr;
particle[n].mass_inv = oldmass_inv;
break;
}
case (POLY_SOAP_B):
{
angle = angle0;
dist = 2.0*PAIR_DRATIO*oldr*(double)(k+1);
particle[i].charge = 0.0;
if (k==npartners-1)
{
particle[n].charge = oldcharge;
particle[n].type = newtype;
}
else
{
particle[n].charge = 0.0;
particle[n].type = oldtype;
}
if (k == npartners-1) particle[n].radius = newr;
else particle[n].radius = oldr;
particle[n].mass_inv = oldmass_inv;
break;
}
case (POLY_SOAP_N):
{
angle = angle0;
dist = 2.0*PAIR_DRATIO*oldr*(double)(k+1);
particle[i].charge = 0.0;
if (k==npartners-1)
{
particle[n].charge = oldcharge;
particle[n].type = newtype;
dist += 2.0*PAIR_DRATIO*oldr;
}
else if (k==npartners-2)
{
particle[n].charge = -oldcharge;
particle[n].type = newtype;
dist += PAIR_DRATIO*oldr;
}
else
{
particle[n].charge = 0.0;
particle[n].type = oldtype;
}
if ((k == npartners-1)||(k == npartners-2)) particle[n].radius = newr;
else particle[n].radius = oldr;
particle[n].mass_inv = oldmass_inv;
break;
}
case (POLY_SOAP_NMIX):
{
angle = angle0;
dist = 2.0*PAIR_DRATIO*oldr*(double)(k+1);
particle[i].charge = 0.0;
if (no_change) /* only modify ends with probability 1/2 */
{
particle[n].charge = 0.0;
particle[n].type = oldtype + 3;
particle[n].radius = oldr;
particle[i].charge = 0.0;
particle[i].type = oldtype + 3;
particle[i].radius = oldr;
}
else
{
if (k==npartners-1)
{
particle[n].charge = oldcharge;
particle[n].type = newtype;
dist += 2.0*PAIR_DRATIO*oldr;
}
else if (k==npartners-2)
{
particle[n].charge = -oldcharge;
particle[n].type = newtype;
dist += PAIR_DRATIO*oldr;
}
else
{
particle[n].charge = 0.0;
particle[n].type = oldtype;
}
if ((k == npartners-1)||(k == npartners-2)) particle[n].radius = newr;
else particle[n].radius = oldr;
}
particle[n].mass_inv = oldmass_inv;
break;
}
case (POLY_PLUSMINUS):
{
angle = angle0;
dist = 2.0*PAIR_DRATIO*oldr*(double)(k+1);
if (k==npartners-1)
{
particle[n].charge = oldcharge;
particle[n].type = newtype;
}
else
{
particle[n].charge = 0.0;
particle[n].type = oldtype;
}
if (k == npartners-1) particle[n].radius = newr;
else particle[n].radius = oldr;
particle[n].mass_inv = oldmass_inv;
break;
}
case (POLY_HYDRA):
{
arm = k/armlen;
p = k%armlen;
angle = angle0 + DPI*(double)arm/(double)narms;
dist = PAIR_DRATIO*(newr + oldr + 2.0*oldr*(double)p);
if (p == armlen-1)
{
particle[n].charge = newcharge;
if ((ALTERNATE_POLY_CHARGE)||(arm%2 == 1)) particle[n].charge *= -1.0;
particle[n].type = newtype;
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
}
else
{
particle[n].charge = 0.0;
particle[n].type = oldtype;
particle[n].radius = oldr;
particle[n].mass_inv = oldmass_inv;
}
break;
}
case (POLY_HYDRA_RIGID): /* same as POLY_HYDRA */
{
arm = k/armlen;
p = k%armlen;
angle = angle0 + DPI*(double)arm/(double)narms;
dist = PAIR_DRATIO*(newr + oldr + 2.0*oldr*(double)p);
if (p == armlen-1)
{
particle[n].charge = newcharge;
if ((ALTERNATE_POLY_CHARGE)&&(arm%2 == 0)) particle[n].charge *= -1.0;
particle[n].type = newtype;
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
}
else
{
particle[n].charge = 0.0;
particle[n].type = oldtype;
particle[n].radius = oldr;
particle[n].mass_inv = oldmass_inv;
}
break;
}
case (POLY_DNA):
{
angle = angle0;
dist = PAIR_DRATIO*(newr + oldr);
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
particle[n].type = 0;
switch (k) {
case (0):
{
angle -= PID;
particle[n].type = 2;
break;
}
case (1):
{
angle += PID;
particle[n].type = 2;
break;
}
default:
{
dist += (double)(k-2)*2.0*PAIR_DRATIO*newr;
if (k == npartners - 1) particle[n].type = 3 + rand()%4;
}
}
break;
}
case (POLY_DNA_ALT):
{
angle = angle0;
dist = PAIR_DRATIO*(newr + oldr);
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
particle[n].type = 0;
particle[n].angle = angle0;
switch (k) {
case (0):
{
angle -= PID;
break;
}
case (1):
{
angle -= PID;
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 1;
break;
}
case (2):
{
angle += PID;
break;
}
case (3):
{
angle += PID;
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 2;
break;
}
default:
{
dist += (double)(k-4)*2.0*PAIR_DRATIO*newr;
if (k == npartners - 1) particle[n].type = 3 + rand()%4;
}
}
break;
}
case (POLY_DNA_DOUBLE):
{
angle = angle0;
dist = PAIR_DRATIO*(newr + oldr);
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
particle[n].type = 0;
particle[n].angle = angle0;
beta = atan(newr/(dist + 2.0*PAIR_DRATIO*newr));
beta2 = atan(1.5/((double)(NPARTNERS_DNA-8)*2.0*PAIR_DRATIO));
switch (k) {
case (0):
{
angle -= PID;
break;
}
case (1):
{
angle -= (PID + DNA_RIGIDITY*beta);
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 1;
// particle[n].type = 2;
break;
}
case (2):
{
angle -= (PID - DNA_RIGIDITY*beta);
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 1;
// particle[n].type = 2;
break;
}
case (3):
{
angle += PID;
break;
}
case (4):
{
angle += PID + DNA_RIGIDITY*beta;
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 2;
break;
}
case (5):
{
angle += PID - DNA_RIGIDITY*beta;
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 2;
break;
}
case (NPARTNERS_DNA-2):
{
dist += (double)(NPARTNERS_DNA-8)*2.0*PAIR_DRATIO*newr;
angle += 0.5*beta2;
particle[n].type = rtype;
break;
}
case (NPARTNERS_DNA-1):
{
dist += (double)(NPARTNERS_DNA-8)*2.0*PAIR_DRATIO*newr;
angle -= 0.5*beta2;
particle[n].type = rtype;
break;
}
default:
{
dist += (double)(k-6)*2.0*PAIR_DRATIO*newr;
}
}
break;
}
case (POLY_DNA_FLEX):
{
angle = angle0;
dist = PAIR_DRATIO*(newr + oldr);
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
particle[n].type = 0;
particle[n].angle = angle0;
beta = atan(newr/(dist + 2.0*PAIR_DRATIO*newr));
beta2 = atan(1.5/((double)(NPARTNERS_DNA-6)*2.0*PAIR_DRATIO));
switch (k) {
case (0):
{
angle -= PID;
break;
}
case (1):
{
angle -= PID;
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 1;
break;
}
case (2):
{
angle += PID;
break;
}
case (3):
{
angle += PID;
dist += 2.0*PAIR_DRATIO*newr;
particle[n].type = 2;
break;
}
case (NPARTNERS_DNA-2):
{
dist += (double)(NPARTNERS_DNA-6)*2.0*PAIR_DRATIO*newr;
angle += 0.5*beta2;
particle[n].type = rtype;
break;
}
case (NPARTNERS_DNA-1):
{
dist += (double)(NPARTNERS_DNA-6)*2.0*PAIR_DRATIO*newr;
angle -= 0.5*beta2;
particle[n].type = rtype;
break;
}
default:
{
dist += (double)(k-4)*2.0*PAIR_DRATIO*newr;
}
}
break;
}
default:
{
angle = angle0 + DPI*(double)k/(double)npartners;
dist = oldr + newr;
particle[n].radius = newr;
particle[n].mass_inv = 1.0/newmass;
// particle[n].partner_eqd[0] = (MU + MU_B)*PAIR_DRATIO;
// particle[i].partner_eqd[k] = (MU + MU_B)*PAIR_DRATIO;
}
}
particle[n].xc = particle[i].xc + dist*cos(angle);
particle[n].yc = particle[i].yc + dist*sin(angle);
// printf("Distance %.3lg, (xi,yi) = (%.3lg, %.3lg), (xn,yn) = (%.3lg, %.3lg)\n",
// dist, particle[i].xc, particle[i].yc, particle[n].xc, particle[n].yc);
/* adjust list of partners */
switch (pairing_type) {
case (POLY_STAR):
{
particle[n].npartners = 1;
particle[n].partner[0] = i;
break;
}
case (POLY_STAR_CHARGED):
{
particle[n].npartners = 3;
particle[n].partner[0] = i;
if (k == 0) particle[n].partner[1] = nlist[npartners-1];
else particle[n].partner[1] = nlist[k-1];
if (k == npartners-1) particle[n].partner[2] = nlist[0];
else particle[n].partner[2] = nlist[k+1];
break;
}
case (POLY_POLYGON):
{
particle[n].npartners = npartners;
particle[n].partner[0] = i;
p = 0;
p1 = 1;
while (p < npartners)
{
q = nlist[p];
if (q != n)
{
particle[n].partner[p1] = q;
p1++;
}
p++;
}
particle[n].npartners = p1;
break;
}
case (POLY_KITE):
{
particle[n].npartners = npartners;
particle[n].partner[0] = i;
p = 0;
p1 = 1;
while (p < npartners)
{
q = nlist[p];
if (q != n)
{
particle[n].partner[p1] = q;
p1++;
}
p++;
}
particle[n].npartners = p1;
break;
}
case (POLY_SEG_POLYGON):
{
particle[n].npartners = 3;
particle[n].partner[0] = i;
if (k == 0) particle[n].partner[1] = nlist[npartners-1];
else particle[n].partner[1] = nlist[k-1];
if (k == npartners-1) particle[n].partner[2] = nlist[0];
else particle[n].partner[2] = nlist[k+1];
break;
}
case (POLY_SOAP_B):
{
if (k==npartners-1)
{
particle[n].npartners = 1;
particle[n].partner[0] = nlist[npartners-2];
}
else if (k==0)
{
particle[n].npartners = 2;
particle[n].partner[0] = i;
particle[n].partner[1] = nlist[1];
}
else
{
particle[n].npartners = 2;
particle[n].partner[0] = nlist[k-1];
particle[n].partner[1] = nlist[k+1];
}
break;
}
case (POLY_SOAP_N):
{
if (k==npartners-1)
{
particle[n].npartners = 1;
particle[n].partner[0] = nlist[npartners-2];
}
else if (k==0)
{
particle[n].npartners = 2;
particle[n].partner[0] = i;
particle[n].partner[1] = nlist[1];
}
else
{
particle[n].npartners = 2;
particle[n].partner[0] = nlist[k-1];
particle[n].partner[1] = nlist[k+1];
}
break;
}
case (POLY_SOAP_NMIX):
{
if (k==npartners-1)
{
particle[n].npartners = 1;
particle[n].partner[0] = nlist[npartners-2];
}
else if (k==0)
{
particle[n].npartners = 2;
particle[n].partner[0] = i;
particle[n].partner[1] = nlist[1];
}
else
{
particle[n].npartners = 2;
particle[n].partner[0] = nlist[k-1];
particle[n].partner[1] = nlist[k+1];
}
break;
}
case (POLY_PLUSMINUS):
{
if (k==npartners-1)
{
particle[n].npartners = 1;
particle[n].partner[0] = nlist[npartners-2];
}
else if (k==0)
{
particle[n].npartners = 2;
particle[n].partner[0] = i;
particle[n].partner[1] = nlist[1];
}
else
{
particle[n].npartners = 2;
particle[n].partner[0] = nlist[k-1];
particle[n].partner[1] = nlist[k+1];
}
break;
}
case (POLY_HYDRA):
{
p = k%armlen;
if (p == armlen-1)
{
particle[n].npartners = 1;
particle[n].partner[0] = nlist[k-1];
}
else if (p == 0)
{
particle[n].npartners = 2;
particle[n].partner[0] = i;
particle[n].partner[1] = nlist[k+1];
}
else
{
particle[n].npartners = 2;
particle[n].partner[0] = nlist[k-1];
particle[n].partner[1] = nlist[k+1];
}
break;
}
case (POLY_HYDRA_RIGID):
{
p = k%armlen;
if (p == armlen-1)
{
particle[n].npartners = 4;
particle[n].partner[0] = nlist[k-1];
particle[n].partner[1] = i;
particle[n].partner[2] = nlist[(k+armlen)%NPARTNERS];
q = k-armlen;
if (q < 0) q += NPARTNERS;
particle[n].partner[3] = nlist[q];
}
else if (p == 0)
{
particle[n].npartners = 4;
particle[n].partner[0] = i;
particle[n].partner[1] = nlist[k+1];
particle[n].partner[2] = nlist[(k+armlen)%NPARTNERS];
q = k-armlen;
if (q < 0) q += NPARTNERS;
particle[n].partner[3] = nlist[q];
}
else
{
particle[n].npartners = 2;
particle[n].partner[0] = nlist[k-1];
particle[n].partner[1] = nlist[k+1];
}
break;
}
default:
{
particle[n].npartners = npartners;
particle[n].partner[0] = i;
counter = 1;
for (l=0; l<npartners; l++) if (l != k)
{
particle[n].partner[counter] = nlist[l];
counter++;
}
}
}
}
/* adjust equilibrium distances */
if ((pairing_type == POLY_ALL))
{
for (l=0; l<particle[i].npartners; l++)
particle[i].partner_eqd[l] = (oldr + newr)*PAIR_DRATIO;
for (k=0; k<npartners; k++)
{
n = nlist[k];
for (l=0; l<particle[n].npartners; l++)
particle[n].partner_eqd[l] = (oldr + newr)*PAIR_DRATIO;
}
}
else
{
for (l=0; l<particle[i].npartners; l++)
{
p = particle[i].partner[l];
dist = module2(particle[i].xc - particle[p].xc, particle[i].yc - particle[p].yc);
particle[i].partner_eqd[l] = dist;
particle[i].partner_eqa[l] = particle[p].angle - particle[i].angle;
}
for (k=0; k<npartners; k++)
{
n = nlist[k];
for (l=0; l<particle[n].npartners; l++)
{
p = particle[n].partner[l];
dist = module2(particle[n].xc - particle[p].xc, particle[n].yc - particle[p].yc);
particle[n].partner_eqd[l] = dist;
particle[n].partner_eqa[l] = particle[p].angle - particle[n].angle;
}
}
}
/* set molecule numbers */
particle[i].molecule = i;
for (k=0; k<npartners; k++)
{
n = particle[i].partner[k];
particle[n].molecule = i;
particle[n].added = particle[i].added;
particle[n].coulomb = particle[i].coulomb;
particle[n].reactive = particle[i].reactive;
// printf("i = %i, added = %i, coulomb = %i\t nb = %i, added = %i, coulomb = %i\n", i, particle[i].added, particle[i].coulomb, n, particle[n].added, particle[n].coulomb);
}
/* test for presence of other molecules that are too close */
if (DEACIVATE_CLOSE_PAIRS)
{
closeby = 0;
for (k=0; k<NMAXCIRCLES; k++) if (particle[k].active)
{
different = (k!=i);
for (l=0; l<npartners; l++) different *= (k!=nlist[l]);
if (different) for (l=0; l<npartners; l++)
{
n = nlist[l];
for (p=-1; p<2; p++) for (q=-1; q<2; q++)
{
x = particle[k].xc + p*(BCXMAX - BCXMIN);
y = particle[k].yc + q*(BCYMAX - BCYMIN);
dist = module2(x - particle[n].xc, y - particle[n].yc);
closeby += (dist < PAIR_SAFETY_FACTOR*(particle[l].radius + particle[k].radius));
}
}
}
if (closeby > 0)
{
printf("Deactivating particle cluster %i\n", i);
particle[i].active = 0;
for (l=0; l<npartners; l++) particle[nlist[l]].active = 0;
}
}
}
void update_single_molecule_data(int mol, t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES])
/* update connection data for a single molecule */
{
int np, p, pp, nq, q, qq, molq, npartners = 0, new, connection;
// molecule[mol].npartners = 0;
printf("Resetting partner list of molecule %i\n", mol);
fprintf(lj_log, "[update_single_molecule_data] Resetting partner list of molecule %i\n", mol);
fprintf(lj_log, "[update_single_molecule_data] Before reset, molecule %i has %i partners: ", mol, molecule[mol].npartners);
for (p=0; p<molecule[mol].npartners; p++)
{
printf("%i(%i) ", molecule[mol].partner[p], molecule[mol].connection_type[p]);
fprintf(lj_log, "%i(%i) ", molecule[mol].partner[p], molecule[mol].connection_type[p]);
}
printf("\n");
fprintf(lj_log, "\n");
np = molecule[mol].nparticles;
for (p=0; p<np; p++)
{
pp = molecule[mol].particle[p];
nq = particle[pp].npartners;
for (q=0; q<nq; q++)
{
qq = particle[pp].partner[q];
molq = particle[qq].molecule;
if (molq != mol)
{
new = 1;
fprintf(lj_log, "Testing molecule %i\n", molq);
/* test if connection has already been found */
for (connection = 0; connection < npartners; connection++)
if (molecule[mol].partner[connection] == molq) new = 0;
if (new)
{
molecule[mol].partner[npartners] = molq;
molecule[mol].connection_type[npartners] = particle[pp].type;
npartners++;
fprintf(lj_log, "Added molecule %i(%i) as partner %i\n", molq, particle[pp].type, npartners);
}
else fprintf(lj_log, "Molecule %i not added\n", molq);
}
}
}
molecule[mol].npartners = npartners;
printf("Molecule %i has %i partners: ", mol, npartners);
fprintf(lj_log, "[update_single_molecule_data] After reset, molecule %i has %i partners: ", mol, npartners);
for (p=0; p<npartners; p++)
{
printf("%i(%i) ", molecule[mol].partner[p], molecule[mol].connection_type[p]);
fprintf(lj_log, "%i(%i) ", molecule[mol].partner[p], molecule[mol].connection_type[p]);
}
printf("\n");
fprintf(lj_log, "\n");
}
void init_molecule_data(t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES])
/* initialize date in molecule structure */
{
int i, m, np;
printf("Initializing molecule structure\n");
#pragma omp parallel for private(m)
for (m=0; m<NMAXCIRCLES; m++)
{
molecule[m].nparticles = 0;
molecule[m].npartners = 0;
molecule[m].added = 0;
}
// printf("1\n");
for (i=0; i<ncircles; i++)
{
// printf("i = %i\n", i);
m = particle[i].molecule;
if (m + 1 > nmolecules) nmolecules = m + 1;
np = molecule[m].nparticles;
// printf("np = %i\n", np);
if (np < NPARTNERS+1)
{
molecule[m].particle[np] = i;
molecule[m].nparticles++;
}
molecule[m].added = particle[i].added;
}
printf("Found %i molecules\n", nmolecules);
fprintf(lj_log, "Found %i molecules\n", nmolecules);
/* for debugging */
for (m=0; m<nmolecules; m++)
{
printf("Molecule %i has %i particles: \n", m, molecule[m].nparticles);
fprintf(lj_log, "Molecule %i has %i particles: \n", m, molecule[m].nparticles);
for (i=0; i<molecule[m].nparticles; i++)
{
printf(" %i-%i |" , molecule[m].particle[i], particle[molecule[m].particle[i]].molecule);
fprintf(lj_log, " %i-%i |" , molecule[m].particle[i], particle[molecule[m].particle[i]].molecule);
}
printf("\n");
fprintf(lj_log, "\n");
printf("Molecule %i.added = %i\n", m, molecule[m].added);
}
}
void init_particle_pairs(t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES])
/* initialize data structure for paired particles */
{
int i, k, l, n, p, q, counter, nlist[NMAXPARTNERS], rtype = 0, newrtype;
double angle, dist;
if (ncircles*NMAXPARTNERS > NMAXCIRCLES)
{
printf("Error: NMAXCIRCLES is too small\n");
exit(1);
}
nmolecules = ncircles;
for (i=0; i<ncircles; i++)
{
if ((particle[i].active)&&(particle[i].type == 0))
{
if (RANDOMIZE_ANGLE) angle = DPI*(double)rand()/(double)RAND_MAX;
else if ((RD_INITIAL_COND == IC_DNA_POLYMERASE)||(RD_INITIAL_COND == IC_DNA_POLYMERASE_REC))
{
if (i%NGRIDY == NGRIDY-2)
{
angle = PID;
rtype = 3 + rand()%4;
}
else if (i%NGRIDY == NGRIDY-1)
{
angle = -PID;
// printf("old type = %i\n", rtype);
switch (rtype) {
case (3):
{
newrtype = 4;
break;
}
case (4):
{
newrtype = 3;
break;
}
case (5):
{
newrtype = 6;
break;
}
case (6):
{
newrtype = 5;
break;
}
}
// printf("new type = %i\n", newrtype);
rtype = newrtype;
}
printf("i = %i, type = %i\n", i, rtype);
}
else angle = 0.0;
for (k=0; k<NPARTNERS; k++) nlist[k] = ncircles*(k+1) + i;
if (PAIR_TYPEB_PARTICLES) add_particle_inpair(i, nlist, angle, PAIRING_TYPE, NPARTNERS, 2, rtype, MU_C, PARTICLE_MASS_C, CHARGE_C, NARMS, particle);
else add_particle_inpair(i, nlist, angle, PAIRING_TYPE, NPARTNERS, 2, rtype, MU_B, PARTICLE_MASS_B, CHARGE_B, NARMS, particle);
}
}
ncircles += ncircles*NPARTNERS;
if (PAIR_TYPEB_PARTICLES)
{
for (i=0; i<ncircles; i++)
{
if ((particle[i].active)&&(particle[i].type == 1))
{
if (RANDOMIZE_ANGLE) angle = DPI*(double)rand()/(double)RAND_MAX;
else angle = 0.0;
for (k=0; k<NPARTNERS_B; k++) nlist[k] = ncircles*(k+1) + i;
add_particle_inpair(i, nlist, angle, PAIRING_TYPE_B, NPARTNERS_B, 3, rtype, MU_D, PARTICLE_MASS_D, CHARGE_D, NARMS_B, particle);
}
}
ncircles += ncircles*NPARTNERS;
}
init_molecule_data(particle, molecule);
printf("Done\n");
/* find first two partner numbers, for P_MOL_ANGLE color scheme */
// for (i=0; i<ncircles; i++)
// {
// p = i;
// for (k=0; k<particle[i].npartners; k++)
// if (particle[i].partner[k] < p) p = particle[i].partner[k];
// particle[i].p0 = p;
// particle[i].p1 = particle[p].partner[0];
// for (k=0; k<particle[i].npartners; k++)
// {
// q = particle[i].partner[k];
// if (q > p)
// {
// particle[q].p0 = p;
// particle[q].p1 = particle[p].partner[0];
// }
// }
// }
printf("ncircles = %i\n", ncircles);
for (i=0; i<ncircles; i++) print_partners(i, particle);
}
int add_particle(double x, double y, double vx, double vy, double mass, short int type, t_particle particle[NMAXCIRCLES])
{
int i, k, l, n, closeby = 0, nlist[NMAXPARTNERS];
double dist, angle;
/* test distance to other particles */
for (i=0; i<ncircles; i++)
{
dist = module2(x - particle[i].xc, y - particle[i].yc);
if ((particle[i].active)&&(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;
// particle[i].radius = MU*sqrt(mass);
particle[i].active = 1;
particle[i].neighb = 0;
particle[i].diff_neighb = 0;
particle[i].thermostat = 1;
particle[i].charge = CHARGE;
particle[i].energy = 0.0;
particle[i].emean = 0.0;
particle[i].dirmean = 0.0;
if (RANDOM_RADIUS) particle[i].radius = particle[i].radius*(RANDOM_RADIUS_MIN + RANDOM_RADIUS_RANGE*((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;
if ((RANDOM_RADIUS)&&(ADAPT_MASS_TO_RADIUS > 0.0))
particle[i].mass_inv *= 1.0/(1.0 + pow(particle[i].radius/MU, ADAPT_MASS_TO_RADIUS));
if ((RANDOM_RADIUS)&&(ADAPT_DAMPING_TO_RADIUS > 0.0))
particle[i].damping = 1.0 + ADAPT_DAMPING_FACTOR*pow(particle[i].radius/MU, ADAPT_DAMPING_TO_RADIUS);
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 = OMEGA_INITIAL*gaussian();
// printf("Particle[%i].omega = %.4lg\n", i, particle[i].omega);
// 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;
// }
if ((PLOT == P_INITIAL_POS)||(PLOT_B == P_INITIAL_POS))
{
switch (INITIAL_POS_TYPE) {
case (IP_X):
{
particle[i].color_hue = 360.0*(particle[i].xc - INITXMIN)/(INITXMAX - INITXMIN);
break;
}
case (IP_Y):
{
particle[i].color_hue = 360.0*(particle[i].yc - INITYMIN)/(INITYMAX - INITYMIN);
break;
}
}
}
else if ((PLOT == P_NUMBER)||(PLOT_B == P_NUMBER))
particle[i].color_hue = 360.0*(double)(i%N_PARTICLE_COLORS)/(double)N_PARTICLE_COLORS;
if ((PAIR_PARTICLES)&&(type == 0))
{
angle = DPI*(double)rand()/(double)RAND_MAX;
for (k=0; k<NPARTNERS; k++) nlist[k] = ncircles + k + 1;
if (PAIR_TYPEB_PARTICLES) add_particle_inpair(i, nlist, angle, PAIRING_TYPE, NPARTNERS, 2, 0, MU_C, PARTICLE_MASS_C, CHARGE_C, NARMS, particle);
else add_particle_inpair(ncircles, nlist, angle, PAIRING_TYPE, NPARTNERS, 2, 0, MU_B, PARTICLE_MASS_B, CHARGE_B, NARMS, particle);
ncircles += NPARTNERS+1;
}
else ncircles++;
printf("Added particle at (%.3lg, %.3lg)\n", x, y);
printf("Number of particles: %i\n", ncircles);
return(1);
}
}
void compute_entropy(t_particle particle[NMAXCIRCLES], double entropy[2])
{
int i, nleft1 = 0, nleft2 = 0;
double p1, p2, x;
static int first = 1, ntot1 = 0, ntot2 = 0;
static double log2;
if (first)
{
log2 = log(2.0);
for (i=0; i<ncircles; i++) if (particle[i].type == 0) ntot1++;
else ntot2++;
first = 0;
}
for (i=0; i<ncircles; i++)
{
if (POSITION_Y_DEPENDENCE) x = particle[i].yc;
else x = particle[i].xc;
if (particle[i].type == 0)
{
if (x < 0.0) nleft1++;
}
else
{
if (x < 0.0) nleft2++;
}
}
p1 = (double)nleft1/(double)ntot1;
p2 = (double)nleft2/(double)ntot2;
printf("Type 1: nleft = %i, ntot = %i, p = %.3lg\n", nleft1, ntot1, p1);
printf("Type 2: nleft = %i, ntot = %i, p = %.3lg\n", nleft2, ntot2, p2);
if ((p1==0.0)||(p1==1.0)) entropy[0] = 0.0;
else entropy[0] = -(p1*log(p1) + (1.0-p1)*log(1.0-p1)/log2);
if ((p2==0.0)||(p2==1.0)) entropy[1] = 0.0;
else entropy[1] = -(p2*log(p2) + (1.0-p2)*log(1.0-p2)/log2);
}
void compute_particle_colors(t_particle particle, t_cluster cluster[NMAXCIRCLES], int plot, double rgb[3], double rgbx[3], double rgby[3], double *radius, int *width, t_particle other_particle[NMAXCIRCLES])
{
double ej, angle, hue, huex, huey, lum, x, y, ccluster;
int i, k, p, q, cl;
switch (plot) {
case (P_KINETIC):
{
ej = particle.energy;
if (ej > 0.0)
{
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*ej/PARTICLE_EMAX;
if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
}
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_NEIGHBOURS):
{
hue = neighbour_color(particle.neighb);
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_BONDS):
{
hue = neighbour_color(particle.neighb);
*radius = particle.radius;
*width = 1;
break;
}
case (P_ANGLE):
{
angle = particle.angle;
hue = angle*particle.spin_freq/DPI;
hue -= (double)((int)hue);
huex = (DPI - angle)*particle.spin_freq/DPI;
huex -= (double)((int)huex);
angle = PI - angle;
if (angle < 0.0) angle += DPI;
huey = angle*particle.spin_freq/DPI;
huey -= (double)((int)huey);
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(hue);
huex = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(huex);
huey = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(huey);
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_TYPE):
{
hue = type_hue(particle.type);
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_DIRECTION):
{
hue = argument(particle.vx, particle.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.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_DIRECT_ENERGY):
{
hue = argument(particle.vx, particle.vy);
if (hue > DPI) hue -= DPI;
if (hue < 0.0) hue += DPI;
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(hue)/DPI;
if (particle.energy < 0.1*PARTICLE_EMAX) lum = 10.0*particle.energy/PARTICLE_EMAX;
else lum = 1.0;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_ANGULAR_SPEED):
{
hue = 160.0*(1.0 + tanh(SLOPE*particle.omega));
// printf("omega = %.3lg, hue = %.3lg\n", particle[j].omega, hue);
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_DIFF_NEIGHB):
{
hue = (double)(particle.diff_neighb+1)/(double)(particle.neighb+1);
// hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*hue;
// hue = 180.0*(1.0 + hue);
hue = 20.0 + 320.0*hue;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_THERMOSTAT):
{
if (particle.thermostat) hue = 30.0;
else hue = 270.0;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_INITIAL_POS):
{
hue = particle.color_hue;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_NUMBER):
{
hue = particle.color_hue;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_EMEAN):
{
ej = particle.emean;
if (ej > 0.0)
{
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*ej/PARTICLE_EMAX;
if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
}
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_EMEAN_DENSITY):
{
ej = particle.emean;
cl = particle.cluster;
ej *= PARTICLE_MASS/cluster[cl].mass;
if (ej > 0.0)
{
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*ej/PARTICLE_EMAX;
if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
}
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_LOG_EMEAN):
{
ej = particle.emean;
if (ej > 0.0)
{
ej = log(ej/PARTICLE_EMIN)/log(PARTICLE_EMAX/PARTICLE_EMIN);
if (ej < 0.0) ej = 0.0;
else if (ej > 1.0) ej = 1.0;
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*ej;
// if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
// if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
}
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_DIRECT_EMEAN):
{
hue = particle.dirmean + COLOR_HUESHIFT*PI;
// printf("dirmean = %.3lg\n", particle.dirmean);
if (hue > DPI) hue -= DPI;
if (hue < 0.0) hue += DPI;
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(hue)/DPI;
ej = particle.emean;
if (ej < 0.5*PARTICLE_EMAX) lum = 2.0*ej/PARTICLE_EMAX;
else lum = 1.0;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_NOPARTICLE):
{
hue = 0.0;
lum = 1.0;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_NPARTNERS):
{
hue = partner_color(particle.npartners);
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_CHARGE):
{
hue = (-tanh(0.5*particle.charge)+1.0)*180.0;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_MOL_ANGLE):
{
p = particle.p0;
q = particle.p1;
x = other_particle[q].xc - other_particle[p].xc;
y = other_particle[q].yc - other_particle[p].yc;
/* deal with periodic boundary conditions */
if (x > 0.5*(XMAX - XMIN)) x -= (XMAX - XMIN);
else if (x < 0.5*(XMIN - XMAX)) x += (XMAX - XMIN);
if (y > 0.5*(YMAX - YMIN)) y -= (YMAX - YMIN);
else if (y < 0.5*(YMIN - YMAX)) y += (YMAX - YMIN);
angle = argument(x, y)*MOL_ANGLE_FACTOR;
// printf("Particle p = %i, mol_angle = %i\n", p, particle.mol_angle);
// angle = argument(x, y)*(double)particle.mol_angle;
// angle = argument(x, y)*(double)(other_particle[particle.p0].npartners);
while (angle > DPI) angle -= DPI;
while (angle < 0.0) angle += DPI;
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(angle)/DPI;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_CLUSTER):
{
// cluster = (double)(particle.cluster)/(double)(ncircles);
ccluster = (double)(particle.cluster_color)/(double)(ncircles);
ccluster -= (double)((int)ccluster);
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*ccluster;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_CLUSTER_SIZE):
{
// cluster = (double)(particle.cluster)/(double)(ncircles);
ccluster = 1.0 - 5.0/((double)particle.cluster_size + 4.0);
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*ccluster;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_CLUSTER_SELECTED):
{
cl = particle.cluster;
if (cluster[cl].selected) hue = COLOR_HUE_CLUSTER_SELECTED;
else hue = COLOR_HUE_CLUSTER_NOT_SELECTED;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_COLLISION):
{
hue = (double)particle.collision;
if (hue > 0.0) hue = atan(0.25*(0.03*hue + 1.0))/PID;
// {
// hue += 10.0;
// hue *= 1.0/(40.0 + hue);
// }
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*hue;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
case (P_RADIUS):
{
// hue = atan(5.0*(particle.radius/MU - 0.75))/PID;
hue = (particle.radius/MU - RANDOM_RADIUS_MIN)/RANDOM_RADIUS_RANGE;
// hue = 0.5*(hue + 1.0);
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*hue;
*radius = particle.radius;
*width = BOUNDARY_WIDTH;
break;
}
}
switch (plot) {
case (P_KINETIC):
{
// hsl_to_rgb_turbo(hue, 0.9, 0.5, rgb);
// hsl_to_rgb_turbo(hue, 0.9, 0.5, rgbx);
// hsl_to_rgb_turbo(hue, 0.9, 0.5, rgby);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_EKIN);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_EKIN);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_EKIN);
break;
}
case (P_BONDS):
{
hsl_to_rgb_turbo(hue, 0.9, 0.5, rgb);
hsl_to_rgb_turbo(hue, 0.9, 0.5, rgbx);
hsl_to_rgb_turbo(hue, 0.9, 0.5, rgby);
break;
}
case (P_DIRECTION):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_DIRECTION);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_DIRECTION);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_DIRECTION);
break;
}
case (P_ANGLE):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_ANGLE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_ANGLE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_ANGLE);
break;
}
case (P_DIRECT_ENERGY):
{
hsl_to_rgb_twilight(hue, 0.9, 0.5, rgb);
hsl_to_rgb_twilight(hue, 0.9, 0.5, rgbx);
hsl_to_rgb_twilight(hue, 0.9, 0.5, rgby);
for (i=0; i<3; i++)
{
rgb[i] *= lum;
rgbx[i] *= lum;
rgby[i] *= lum;
}
break;
}
case (P_DIFF_NEIGHB):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_DIFFNEIGH);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_DIFFNEIGH);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_DIFFNEIGH);
// hsl_to_rgb_twilight(hue, 0.9, 0.5, rgb);
// hsl_to_rgb_twilight(hue, 0.9, 0.5, rgbx);
// hsl_to_rgb_twilight(hue, 0.9, 0.5, rgby);
break;
}
case (P_INITIAL_POS):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_INITIAL_POS);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_INITIAL_POS);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_INITIAL_POS);
break;
}
case (P_EMEAN):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_EKIN);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_EKIN);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_EKIN);
break;
}
case (P_LOG_EMEAN):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_EKIN);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_EKIN);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_EKIN);
break;
}
case (P_DIRECT_EMEAN):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_DIRECTION);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_DIRECTION);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_DIRECTION);
for (i=0; i<3; i++)
{
rgb[i] *= lum;
rgbx[i] *= lum;
rgby[i] *= lum;
}
break;
}
case (P_CHARGE):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_CHARGE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_CHARGE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_CHARGE);
break;
}
case (P_MOL_ANGLE):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_ANGLE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_ANGLE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_ANGLE);
break;
}
case (P_CLUSTER):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_CLUSTER);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_CLUSTER);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_CLUSTER);
break;
}
case (P_CLUSTER_SIZE):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_CLUSTER_SIZE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_CLUSTER_SIZE);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_CLUSTER_SIZE);
break;
}
case (P_CLUSTER_SELECTED):
{
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_CLUSTER_SELECTED);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgbx, COLOR_PALETTE_CLUSTER_SELECTED);
hsl_to_rgb_palette(hue, 0.9, 0.5, rgby, COLOR_PALETTE_CLUSTER_SELECTED);
break;
}
default:
{
hsl_to_rgb(hue, 0.9, 0.5, rgb);
hsl_to_rgb(hue, 0.9, 0.5, rgbx);
hsl_to_rgb(hue, 0.9, 0.5, rgby);
}
}
}
void set_segment_group_color(int group, double lum, double rgb[3])
{
switch (group) {
case (1):
{
hsl_to_rgb_palette(270.0, 0.9, 0.5, rgb, COLOR_PALETTE);
break;
}
case (2):
{
hsl_to_rgb_palette(90.0, 0.9, 0.5, rgb, COLOR_PALETTE);
break;
}
default:
{
rgb[0] = 1.0;
rgb[1] = 1.0;
rgb[2] = 1.0;
}
}
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
}
void set_segment_pressure_color(double pressure, double lum, double rgb[3])
{
double hue;
// if (pressure < 0.0) pressure = 0.0;
hue = SEGMENT_HUE_MIN + (SEGMENT_HUE_MAX - SEGMENT_HUE_MIN)*pressure/SEGMENT_PMAX;
if (hue > SEGMENT_HUE_MIN) hue = SEGMENT_HUE_MIN;
else if (hue < SEGMENT_HUE_MAX) hue = SEGMENT_HUE_MAX;
// hsl_to_rgb_turbo(hue, 0.9, lum, rgb);
hsl_to_rgb_palette(hue, 0.9, lum, rgb, COLOR_PALETTE_PRESSURE);
glColor3f(rgb[0], rgb[1], rgb[2]);
}
void draw_altitude_lines()
{
int i, i1, i2;
double x, y;
glColor3f(0.5, 0.5, 0.5);
glLineWidth(1);
i1 = (int)(YMIN + ytrack) - 1.0;
i2 = (int)(YMAX + ytrack) + 1.0;
for (i = i1; i < i2; i++)
{
y = (double)i - ytrack;
draw_line(BCXMIN, y, XMAX - 1.8, y);
}
i1 = (int)(XMIN + xtrack) - 1.0;
i2 = (int)(XMAX - 1.8 + xtrack) + 1.0;
for (i = i1; i < i2; i++)
{
x = (double)i - xtrack;
if (x < XMAX - 1.8) draw_line(x, YMIN, x, YMAX);
}
}
void draw_one_triangle(t_particle particle, int same_table[9*HASHMAX], int p, int q, int nsame)
{
double x, y, dx, dy;
int k;
x = particle.xc + (double)p*(BCXMAX - BCXMIN);
y = particle.yc + (double)q*(BCYMAX - BCYMIN);
if (TRACK)
{
x -= xtrack;
y -= ytrack;
}
glBegin(GL_TRIANGLE_FAN);
glVertex2d(x, y);
for (k=0; k<nsame; k++)
{
dx = particle.deltax[same_table[k]];
dy = particle.deltay[same_table[k]];
if (module2(dx, dy) < particle.radius*NBH_DIST_FACTOR)
glVertex2d(x + dx, y + dy);
}
glEnd();
}
void draw_triangles(t_particle particle[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES], int plot)
/* fill triangles between neighboring particles */
{
int i, j, k, p, q, t0, tj, tmax, nsame = 0, same_table[9*HASHMAX], width;
double rgb[3], hue, dx, dy, x, y, radius, rgbx[3], rgby[3];
// printf("Number of nbs: ");
for (i=0; i<ncircles; i++) if (particle[i].active)
{
nsame = 0;
t0 = particle[i].type;
/* find neighbours of same type */
for (j=0; j<particle[i].hash_nneighb; j++)
{
k = particle[i].hashneighbour[j];
if ((k!=i)&&((plot != P_TYPE)||(particle[k].type == t0)))
{
same_table[nsame] = j;
nsame++;
}
}
/* draw the triangles */
if (nsame > 1)
{
if (plot == P_TYPE)
{
hue = type_hue(t0);
hsl_to_rgb(hue, 0.9, 0.3, rgb);
}
else
{
compute_particle_colors(particle[i], cluster, plot, rgb, rgbx, rgby, &radius, &width, particle);
}
glColor3f(rgb[0], rgb[1], rgb[2]);
if (PERIODIC_BC) for (p=-1; p<2; p++) for (q=-1; q<2; q++)
draw_one_triangle(particle[i], same_table, p, q, nsame);
else draw_one_triangle(particle[i], same_table, 0, 0, nsame);
}
}
printf("\n");
}
void draw_one_particle_links(t_particle particle)
/* draw links of one particle */
{
int i, j, k;
double x1, x2, y1, y2, length, linkcolor,periodx, periody, xt1, yt1, xt2, yt2;
glLineWidth(LINK_WIDTH);
// if (particle.active)
// {
// radius = particle[j].radius;
for (k = 0; k < particle.hash_nneighb; k++)
{
x1 = particle.xc;
if (CENTER_VIEW_ON_OBSTACLE) x1 -= xshift;
y1 = particle.yc;
x2 = x1 + particle.deltax[k];
y2 = y1 + particle.deltay[k];
length = module2(particle.deltax[k], particle.deltay[k])/particle.radius;
if (COLOR_BONDS)
{
if (length < 1.5) linkcolor = 1.0;
else linkcolor = 1.0 - 0.75*(length - 1.5)/(NBH_DIST_FACTOR - 1.5);
glColor3f(linkcolor, linkcolor, linkcolor);
}
if (length < 1.0*NBH_DIST_FACTOR) draw_line(x1, y1, x2, y2);
}
}
int draw_special_particle(t_particle particle, double xc1, double yc1, double radius, double angle, int nsides, double rgb[3], short int fill)
/* draw special particles shapes, for chemical reactions */
{
double x1, y1, x2, y2, omega, r;
int wsign, i, j;
switch(RD_REACTION)
{
case (CHEM_AAB):
{
if (particle.type == 2)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*0.7*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*0.7*radius*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, 0.7*radius, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 0.7*radius, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_ABC):
{
if (particle.type == 3)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*0.7*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*0.7*radius*sin(particle.angle);
if (wsign == 1)
{
if (fill) draw_colored_polygon(x1, y1, 1.2*MU_B, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU_B, nsides, angle + APOLY*PID);
}
else
{
if (fill) draw_colored_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID);
}
}
return(0);
}
break;
}
case (CHEM_A2BC):
{
if (particle.type == 3)
{
draw_colored_polygon(xc1, yc1, 1.2*MU_B, nsides, angle + APOLY*PID, rgb);
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + 1.5*radius*cos(particle.angle + 0.6*(double)wsign*PI);
y1 = yc1 + 1.5*radius*sin(particle.angle + 0.6*(double)wsign*PI);
if (fill) draw_colored_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_CATALYSIS):
{
if (particle.type == 3)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*0.7*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*0.7*radius*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_AUTOCATALYSIS):
{
if (particle.type == 2)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*MU*0.7*cos(particle.angle);
y1 = yc1 + (double)wsign*MU*0.7*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_BAA):
{
if (particle.type == 2)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*1.2*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*1.2*radius*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, 0.9*radius, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 0.9*radius, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_AABAA):
{
if (particle.type == 2)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*0.7*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*0.7*radius*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, 0.9*radius, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 0.9*radius, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_POLYMER):
{
if (particle.type >= 3)
{
omega = DPI/(double)(particle.type - 2);
draw_colored_polygon(xc1, yc1, 1.2*MU_B, nsides, angle + APOLY*PID, rgb);
for (i=0; i<particle.type-2; i++)
{
x1 = xc1 + 1.5*MU_B*cos(particle.angle + (double)i*omega);
y1 = yc1 + 1.5*MU_B*sin(particle.angle + (double)i*omega);
if (fill) draw_colored_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_POLYMER_DISS):
{
if (particle.type >= 3)
{
omega = DPI/(double)(particle.type - 2);
draw_colored_polygon(xc1, yc1, 1.2*MU_B, nsides, angle + APOLY*PID, rgb);
for (i=0; i<particle.type-2; i++)
{
x1 = xc1 + 1.5*MU_B*cos(particle.angle + (double)i*omega);
y1 = yc1 + 1.5*MU_B*sin(particle.angle + (double)i*omega);
if (fill) draw_colored_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_POLYMER_STEP):
{
if (particle.type >= 2)
{
omega = DPI/(double)(particle.type - 1);
draw_colored_polygon(xc1, yc1, MU, nsides, angle + APOLY*PID, rgb);
for (i=0; i<particle.type-1; i++)
{
x1 = xc1 + 1.5*MU*cos(particle.angle + (double)i*omega);
y1 = yc1 + 1.5*MU*sin(particle.angle + (double)i*omega);
if (fill) draw_colored_polygon(x1, y1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_CATALYTIC_A2D):
{
if (particle.type == 3)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*MU*0.7*cos(particle.angle);
y1 = yc1 + (double)wsign*MU*0.7*sin(particle.angle);
if (wsign == -1) r = MU;
else r = MU_B;
if (fill) draw_colored_polygon(x1, y1, r, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, r, nsides, angle + APOLY*PID);
}
return(0);
}
else if (particle.type == 4)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*MU*0.7*cos(particle.angle);
y1 = yc1 + (double)wsign*MU*0.7*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_ABCAB):
{
if (particle.type == 3)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*0.7*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*0.7*radius*sin(particle.angle);
if (wsign == 1)
{
if (fill) draw_colored_polygon(x1, y1, 1.2*MU_B, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU_B, nsides, angle + APOLY*PID);
}
else
{
if (fill) draw_colored_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID);
}
}
return(0);
}
break;
}
case (CHEM_ABCDABC):
{
if (particle.type == 3)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*0.7*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*0.7*radius*sin(particle.angle);
if (wsign == 1)
{
if (fill) draw_colored_polygon(x1, y1, MU_B, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU_B, nsides, angle + APOLY*PID);
}
else
{
if (fill) draw_colored_polygon(x1, y1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU, nsides, angle + APOLY*PID);
}
}
return(0);
}
else if (particle.type == 4)
{
draw_colored_polygon(xc1, yc1, 1.2*MU_B, nsides, angle + APOLY*PID, rgb);
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + 0.7*radius*cos(particle.angle + 0.6*(double)wsign*PI);
y1 = yc1 + 0.7*radius*sin(particle.angle + 0.6*(double)wsign*PI);
if (fill) draw_colored_polygon(x1, y1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU, nsides, angle + APOLY*PID);
}
return(0);
}
break;
}
case (CHEM_BZ):
{
if ((particle.type >= 2)&&(particle.type <= 4))
{
omega = DPI/(double)(particle.type - 1);
if (fill) draw_colored_polygon(xc1, yc1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(xc1, yc1, MU, nsides, angle + APOLY*PID);
for (i=0; i<particle.type-1; i++)
{
x1 = xc1 + 1.5*MU*cos(particle.angle + (double)i*omega);
y1 = yc1 + 1.5*MU*sin(particle.angle + (double)i*omega);
if (fill) draw_colored_polygon(x1, y1, MU_B, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU_B, nsides, angle + APOLY*PID);
}
return(0);
}
else if (particle.type == 5)
{
if (fill) draw_colored_polygon(xc1, yc1, MU_B, 4, angle + APOLY*PID, rgb);
else draw_polygon(xc1, yc1, MU_B, 4, angle + APOLY*PID);
return(0);
}
else if (particle.type == 6)
{
if (fill) draw_colored_polygon(xc1, yc1, 1.5*MU_B, 6, angle + APOLY*PID, rgb);
else draw_polygon(xc1, yc1, 1.5*MU_B, 6, angle + APOLY*PID);
return(0);
}
else if (particle.type == 7)
{
for (i=-1; i<2; i+=2)
{
x1 = xc1 + (double)i*1.5*MU*cos(particle.angle);
y1 = yc1 + (double)i*1.5*MU*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU, nsides, angle + APOLY*PID);
for (j=-1; j<2; j++)
{
x2 = x1 + 1.5*MU*cos(particle.angle + (double)j*PID);
y2 = y1 + 1.5*MU*sin(particle.angle + (double)j*PID);
if (fill) draw_colored_polygon(x2, y2, MU_B, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x2, y2, MU_B, nsides, angle + APOLY*PID);
}
}
return(0);
}
else if (particle.type == 8)
{
x1 = xc1 + 1.5*MU*cos(particle.angle);
y1 = yc1 + 1.5*MU*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, MU_B, 4, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU_B, 4, angle + APOLY*PID);
x1 = xc1 - 1.5*MU*cos(particle.angle);
y1 = yc1 - 1.5*MU*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, MU_B, 4, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU_B, 4, angle + APOLY*PID);
return(0);
}
break;
}
case (CHEM_BRUSSELATOR):
{
if (particle.type == 4)
{
if (fill) draw_colored_polygon(xc1, yc1, MU_B, nsides, angle + APOLY*PID, rgb);
else draw_polygon(xc1, yc1, MU_B, nsides, angle + APOLY*PID);
x1 = xc1 + 1.2*MU_B*cos(particle.angle);
y1 = yc1 + 1.2*MU_B*sin(particle.angle);
if (fill) draw_colored_polygon(x1, y1, MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, MU, nsides, angle + APOLY*PID);
return(0);
}
break;
}
case (CHEM_ABDACBE):
{
if (particle.type == 4)
{
for (wsign = -1; wsign <= 1; wsign+=2)
{
x1 = xc1 + (double)wsign*0.7*radius*cos(particle.angle);
y1 = yc1 + (double)wsign*0.7*radius*sin(particle.angle);
if (wsign == 1)
{
if (fill) draw_colored_polygon(x1, y1, 1.2*MU_B, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU_B, nsides, angle + APOLY*PID);
}
else
{
if (fill) draw_colored_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID, rgb);
else draw_polygon(x1, y1, 1.2*MU, nsides, angle + APOLY*PID);
}
}
return(0);
}
break;
}
// case (CHEM_DNA_ENZYME):
// {
// if (particle.type == 7)
// {
// /* TODO */
// }
// return(0);
// break;
// }
}
return(1);
}
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 x1, x2, y1, y2, xc1, yc1, wangle, newradius = radius, pradius;
int wsign, cont = 1, draw = 1;
static double ca, sa;
static int first = 1;
if (first)
{
angle = APOLY*PID;
ca = cos(angle);
sa = sin(angle);
first = 0;
}
if (CENTER_VIEW_ON_OBSTACLE) xc1 = xc - xshift;
else xc1 = xc;
if (TRACK)
{
xc1 -= xtrack;
yc1 = yc - ytrack;
}
else yc1 = yc;
glColor3f(rgb[0], rgb[1], rgb[2]);
/* specific shapes for chemical reactions */
if (REACTION_DIFFUSION) cont = draw_special_particle(particle, xc1, yc1, radius, angle, nsides, rgb, 1);
if ((particle.interaction == I_LJ_QUADRUPOLE)||(particle.interaction == I_LJ_DIPOLE)||(particle.interaction == I_VICSEK)||(particle.interaction == I_VICSEK_REPULSIVE)||(particle.interaction == I_VICSEK_SPEED)||(particle.interaction == I_VICSEK_SHARK))
draw_colored_rhombus(xc1, yc1, radius, angle + APOLY*PID, rgb);
else if (particle.interaction == I_SEGMENT)
draw_colored_rotated_rect(xc1, yc1, particle.radius, 0.1*particle.radius, angle + APOLY*PID, rgb);
else if (particle.interaction == I_SEGMENT_CHARGED)
{
draw_colored_rotated_rect(xc1, yc1, particle.radius, 0.1*particle.radius, angle + APOLY*PID, rgb);
/* TODO: draw ends */
}
else if (cont)
{
if (nsides == NSEG) draw_colored_circle_precomp(xc1, yc1, radius, rgb);
else draw_colored_polygon(xc1, yc1, radius, nsides, angle + APOLY*PID, rgb);
}
/* draw crosses/bars on charged particles */
if (TWO_TYPES)
{
if ((DRAW_CROSS)&&(particle.charge > 0.0))
{
pradius = particle.radius;
if (ROTATION)
{
angle = angle + APOLY*PID;
ca = cos(angle);
sa = sin(angle);
}
glLineWidth(2);
glColor3f(1.0, 1.0, 1.0);
x1 = xc1 - pradius*ca;
y1 = yc1 - pradius*sa;
x2 = xc1 + pradius*ca;
y2 = yc1 + pradius*sa;
draw_line(x1, y1, x2, y2);
x1 = xc1 - pradius*sa;
y1 = yc1 + pradius*ca;
x2 = xc1 + pradius*sa;
y2 = yc1 - pradius*ca;
draw_line(x1, y1, x2, y2);
}
if ((DRAW_MINUS)&&(particle.charge < 0.0))
{
pradius = particle.radius;
if (ROTATION)
{
angle = angle + APOLY*PID;
ca = cos(angle);
sa = sin(angle);
}
glLineWidth(2);
glColor3f(1.0, 1.0, 1.0);
x1 = xc1 - pradius*ca;
y1 = yc1 - pradius*sa;
x2 = xc1 + pradius*ca;
y2 = yc1 + pradius*sa;
draw_line(x1, y1, x2, y2);
}
}
glLineWidth(width);
glColor3f(1.0, 1.0, 1.0);
if (REACTION_DIFFUSION) cont = draw_special_particle(particle, xc1, yc1, radius, angle, nsides, rgb, 0);
if ((particle.interaction == I_LJ_QUADRUPOLE)||(particle.interaction == I_LJ_DIPOLE)||(particle.interaction == I_VICSEK)||(particle.interaction == I_VICSEK_REPULSIVE)||(particle.interaction == I_VICSEK_SPEED)||(particle.interaction == I_VICSEK_SHARK))
draw_rhombus(xc1, yc1, radius, angle + APOLY*PID);
else if (particle.interaction == I_SEGMENT)
draw_rotated_rect(xc1, yc1, particle.radius, 0.1*particle.radius, angle + APOLY*PID);
else if (particle.interaction == I_SEGMENT_CHARGED)
{
draw_rotated_rect(xc1, yc1, particle.radius, 0.1*particle.radius, angle + APOLY*PID);
/* TODO: draw ends */
}
else if (cont)
{
if (nsides == NSEG) draw_circle_precomp(xc1, yc1, radius);
else draw_polygon(xc1, yc1, radius, nsides, angle + APOLY*PID);
}
if (particle.interaction == I_LJ_WATER) for (wsign = -1; wsign <= 1; wsign+=2)
{
wangle = particle.angle + (double)wsign*DPI/3.0;
x1 = xc1 + particle.radius*cos(wangle);
y1 = yc1 + particle.radius*sin(wangle);
draw_colored_polygon(x1, y1, 0.5*radius, nsides, angle + APOLY*PID, rgb);
glColor3f(1.0, 1.0, 1.0);
draw_polygon(x1, y1, 0.5*radius, nsides, angle + APOLY*PID);
}
// printf("Particle radius %.3f, pradius %.3f, mass %.3f\n", radius, particle.radius, 1.0/particle.mass_inv);
// sleep(1);
}
void draw_collisions(t_collision *collisions, int ncollisions)
/* draw discs where collisions happen */
{
int i, j;
double rgb[3], lum, x, y, x1, y1;
for (i=0; i<ncollisions; i++) if (collisions[i].time > 0)
{
lum = (double)collisions[i].time/(double)COLLISION_TIME;
if (collisions[i].color == 0.0) for (j=0; j<3; j++) rgb[j] = lum;
else hsl_to_rgb_palette(collisions[i].color, 0.9, lum, rgb, COLOR_PALETTE);
x = collisions[i].x;
y = collisions[i].y;
if (CENTER_VIEW_ON_OBSTACLE) x1 = x - xshift;
else x1 = x;
if (TRACK)
{
x1 -= xtrack;
y1 = y - ytrack;
}
else y1 = y;
draw_colored_polygon(x1, y1, COLLISION_RADIUS*MU, NSEG, 0.0, rgb);
collisions[i].time--;
}
}
void draw_trajectory(t_tracer trajectory[TRAJECTORY_LENGTH*N_TRACER_PARTICLES], int traj_position, int traj_length, t_particle *particle, t_cluster *cluster, int *tracer_n, int plot)
/* draw tracer particle trajectory */
{
int i, j, time, p, width;
double x1, x2, y1, y2, rgb[3], rgbx[3], rgby[3], radius, lum;
// blank();
glLineWidth(TRAJECTORY_WIDTH);
printf("drawing trajectory\n");
if (traj_length < TRAJECTORY_LENGTH*TRACER_STEPS)
for (i=0; i < traj_length-1; i++)
// for (i=traj_length-2; i >= 0; i--)
for (j=0; j<n_tracers; j++)
{
compute_particle_colors(particle[tracer_n[j]], cluster, plot, rgb, rgbx, rgby, &radius, &width, particle);
glColor3f(rgb[0], rgb[1], rgb[2]);
x1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].xc;
x2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].xc;
y1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].yc;
y2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].yc;
time = traj_length - i;
lum = 0.9 - TRACER_LUM_FACTOR*(double)time/(double)(TRAJECTORY_LENGTH*TRACER_STEPS);
if (lum < 0.0) lum = 0.0;
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
if ((lum > 0.1)&&(module2(x2 - x1, y2 - y1) < 0.25*(YMAX - YMIN)))
draw_line(x1, y1, x2, y2);
}
else
{
// for (i = traj_length-2; i >= traj_position + 1; i--)
for (i = traj_position + 1; i < traj_length-1; i++)
for (j=0; j<n_tracers; j++)
{
compute_particle_colors(particle[tracer_n[j]], cluster, plot, rgb, rgbx, rgby, &radius, &width, particle);
glColor3f(rgb[0], rgb[1], rgb[2]);
x1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].xc;
x2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].xc;
y1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].yc;
y2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].yc;
time = traj_position + traj_length - i;
lum = 0.9 - TRACER_LUM_FACTOR*(double)time/(double)(TRAJECTORY_LENGTH*TRACER_STEPS);
if (lum < 0.0) lum = 0.0;
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
if ((lum > 0.1)&&(module2(x2 - x1, y2 - y1) < 0.25*(YMAX - YMIN)))
draw_line(x1, y1, x2, y2);
}
for (i=0; i < traj_position-1; i++)
// for (i = traj_position-2; i >= 0; i--)
for (j=0; j<n_tracers; j++)
{
compute_particle_colors(particle[tracer_n[j]], cluster, plot, rgb, rgbx, rgby, &radius, &width, particle);
glColor3f(rgb[0], rgb[1], rgb[2]);
x1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].xc;
x2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].xc;
y1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].yc;
y2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].yc;
time = traj_position - i;
lum = 0.9 - TRACER_LUM_FACTOR*(double)time/(double)(TRAJECTORY_LENGTH*TRACER_STEPS);
if (lum < 0.0) lum = 0.0;
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
if ((lum > 0.1)&&(module2(x2 - x1, y2 - y1) < 0.25*(YMAX - YMIN)))
draw_line(x1, y1, x2, y2);
}
}
}
void old_draw_trajectory(t_tracer trajectory[TRAJECTORY_LENGTH*N_TRACER_PARTICLES], int traj_position, int traj_length, t_particle *particle, t_cluster *cluster, int *tracer_n, int plot)
/* draw tracer particle trajectory */
{
int i, j, time, p, width;
double x1, x2, y1, y2, rgb[3], rgbx[3], rgby[3], radius, lum;
// blank();
glLineWidth(TRAJECTORY_WIDTH);
printf("drawing trajectory\n");
for (j=0; j<n_tracers; j++)
{
compute_particle_colors(particle[tracer_n[j]], cluster, plot, rgb, rgbx, rgby, &radius, &width, particle);
glColor3f(rgb[0], rgb[1], rgb[2]);
if (traj_length < TRAJECTORY_LENGTH*TRACER_STEPS)
// for (i=0; i < traj_length-1; i++)
for (i=traj_length-2; i >= 0; i--)
{
x1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].xc;
x2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].xc;
y1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].yc;
y2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].yc;
time = traj_length - i;
lum = 0.9 - TRACER_LUM_FACTOR*(double)time/(double)(TRAJECTORY_LENGTH*TRACER_STEPS);
if (lum < 0.0) lum = 0.0;
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
if ((lum > 0.1)&&(module2(x2 - x1, y2 - y1) < 0.25*(YMAX - YMIN)))
draw_line(x1, y1, x2, y2);
// printf("tracer = %i, (x1, y1) = (%.3lg, %.3lg), (x2, y2) = (%.3lg, %.3lg)\n", j, x1, y1, x2, y2);
}
else
{
// for (i = traj_position + 1; i < traj_length-1; i++)
for (i = traj_length-2; i >= traj_position + 1; i--)
{
x1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].xc;
x2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].xc;
y1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].yc;
y2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].yc;
time = traj_position + traj_length - i;
lum = 0.9 - TRACER_LUM_FACTOR*(double)time/(double)(TRAJECTORY_LENGTH*TRACER_STEPS);
if (lum < 0.0) lum = 0.0;
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
if ((lum > 0.1)&&(module2(x2 - x1, y2 - y1) < 0.25*(YMAX - YMIN)))
draw_line(x1, y1, x2, y2);
}
// for (i=0; i < traj_position-1; i++)
for (i = traj_position-2; i >= 0; i--)
{
x1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].xc;
x2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].xc;
y1 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i].yc;
y2 = trajectory[j*TRAJECTORY_LENGTH*TRACER_STEPS + i+1].yc;
time = traj_position - i;
lum = 0.9 - TRACER_LUM_FACTOR*(double)time/(double)(TRAJECTORY_LENGTH*TRACER_STEPS);
if (lum < 0.0) lum = 0.0;
glColor3f(lum*rgb[0], lum*rgb[1], lum*rgb[2]);
if ((lum > 0.1)&&(module2(x2 - x1, y2 - y1) < 0.25*(YMAX - YMIN)))
draw_line(x1, y1, x2, y2);
}
}
}
}
void color_background(t_particle particle[NMAXCIRCLES], t_obstacle obstacle[NMAXOBSTACLES], int bg_color, t_hashgrid hashgrid[HASHX*HASHY])
/* color background according to particle properties */
{
int i, j, k, n, p, q, m, nnb, number, avrg_fact, obs;
double rgb[3], hue, value, p1, p2, pp1, pp2, oldhue, valx, valy, lum;
static int first = 1, counter = 0;
p1 = 0.75;
p2 = 1.0 - p1;
pp1 = 0.95;
pp2 = 1.0 - pp1;
glBegin(GL_QUADS);
for (i=0; i<HASHX; i++)
for (j=0; j<HASHY; j++)
{
n = mhash(i, j);
if (first)
{
hashgrid[n].hue1 = 180.0;
hashgrid[n].hue2 = 180.0;
}
/* set two old values for option DOUBLE_MOVIE */
if (DOUBLE_MOVIE)
{
if (counter) oldhue = hashgrid[n].hue1;
else oldhue = hashgrid[n].hue2;
}
else oldhue = hashgrid[n].hue1;
switch (bg_color) {
case (BG_DENSITY):
{
nnb = hashgrid[n].nneighb;
number = 0;
for (q=0; q<nnb; q++)
{
m = hashgrid[n].neighbour[q];
number += hashgrid[m].number;
}
number += hashgrid[n].number;
// hue = 50.0*(double)hashgrid[n].number;
hue = 75.0*(double)number/(double)(nnb + 1);
hue = p1*oldhue + p2*hue;
rgb[0] = hue/360.0;
rgb[1] = hue/360.0;
rgb[2] = hue/360.0;
break;
}
case (BG_CHARGE):
{
avrg_fact = 3; /* weight of central cell in hashgrid average */
value = 0.0;
nnb = hashgrid[n].nneighb;
for (q=0; q<nnb; q++)
{
m = hashgrid[n].neighbour[q];
for (k=0; k<hashgrid[m].number; k++)
{
p = hashgrid[m].particles[k];
value += particle[p].charge;
}
}
/* hashcell n counts double */
for (k=0; k<hashgrid[n].number; k++)
{
p = hashgrid[n].particles[k];
value += (double)(avrg_fact-1)*particle[p].charge;
}
value *= 1.0/(double)(nnb + avrg_fact);
if (CHARGE_OBSTACLES) value += hashgrid[n].charge;
hue = (-tanh(0.5*value)+1.0)*180.0;
hue = p1*oldhue + p2*hue;
hsl_to_rgb_twilight(hue, 0.9, 0.5, rgb);
break;
}
case (BG_EKIN):
{
value = 0.0;
nnb = hashgrid[n].nneighb;
for (q=0; q<nnb; q++)
{
m = hashgrid[n].neighbour[q];
for (k=0; k<hashgrid[m].number; k++)
{
p = hashgrid[m].particles[k];
value += particle[p].energy;
}
}
/* hashcell n counts double */
for (k=0; k<hashgrid[n].number; k++)
{
p = hashgrid[n].particles[k];
value += particle[p].energy;
}
value *= 1.0/(double)(nnb + 1);
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*value/PARTICLE_EMAX;
if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
hue = p1*oldhue + p2*hue;
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_EKIN);
break;
}
case (BG_EOBSTACLES):
{
value = 0.0;
nnb = hashgrid[n].nneighb;
for (q=0; q<nnb; q++)
{
m = hashgrid[n].neighbour[q];
if (hashgrid[m].nobs) value += obstacle[hashgrid[m].obstacle].energy;
}
/* hashcell n counts four times */
if (hashgrid[n].nobs) value += 3.0*obstacle[hashgrid[n].obstacle].energy;
value *= 1.0/(double)(nnb + 4);
// if (hashgrid[n].nobs) value += obstacle[hashgrid[n].obstacle].energy;
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*value/OBSTACLE_EMAX;
if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
hue = p1*oldhue + p2*hue;
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_EKIN);
break;
}
case (BG_EKIN_OBSTACLES):
{
value = 0.0;
nnb = hashgrid[n].nneighb;
for (q=0; q<nnb; q++)
{
m = hashgrid[n].neighbour[q];
for (k=0; k<hashgrid[m].number; k++)
{
p = hashgrid[m].particles[k];
value += particle[p].energy;
}
}
/* hashcell n counts double */
for (k=0; k<hashgrid[n].number; k++)
{
p = hashgrid[n].particles[k];
value += particle[p].energy;
}
value *= 1.0/(double)(nnb + 1);
if (hashgrid[n].nobs) value += obstacle[hashgrid[n].obstacle].energy;
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*value/OBSTACLE_EMAX;
if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
hue = p1*oldhue + p2*hue;
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_EKIN);
break;
}
case (BG_DIR_OBSTACLES):
{
valx = 0.0;
valy = 0.0;
nnb = hashgrid[n].nneighb;
for (q=0; q<nnb; q++)
{
m = hashgrid[n].neighbour[q];
if (hashgrid[m].nobs)
{
valx += obstacle[hashgrid[m].obstacle].vx;
valy += obstacle[hashgrid[m].obstacle].vy;
}
}
/* hashcell n counts more */
if (hashgrid[n].nobs)
{
valx += 4.0*obstacle[hashgrid[n].obstacle].vx;
valy += 4.0*obstacle[hashgrid[n].obstacle].vy;
}
valx *= 1.0/(double)(nnb + 4);
valy *= 1.0/(double)(nnb + 4);
hue = argument(valx, valy);
if (hue > DPI) hue -= DPI;
if (hue < 0.0) hue += DPI;
hue = pp1*oldhue + pp2*hue;
lum = module2(valx, valy)/OBSTACLE_VMAX;
if (lum > 1.0) lum = 1.0;
hsl_to_rgb_palette(hue*360.0/DPI, 0.9, 0.5*lum, rgb, COLOR_PALETTE_DIRECTION);
break;
}
case (BG_POS_OBSTACLES):
{
valx = 0.0;
valy = 0.0;
nnb = hashgrid[n].nneighb;
// for (q=0; q<nnb; q++)
// {
// m = hashgrid[n].neighbour[q];
// if (hashgrid[m].nobs)
// {
// valx += obstacle[hashgrid[m].obstacle].xc;
// valy += obstacle[hashgrid[m].obstacle].yc;
// }
// }
/* hashcell n counts double */
if (hashgrid[n].nobs)
{
obs = hashgrid[n].obstacle;
valx += obstacle[obs].xc - obstacle[obs].xc0;
valy += obstacle[obs].yc - obstacle[obs].yc0;
}
// valx *= 1.0/(double)(nnb + 1);
// valy *= 1.0/(double)(nnb + 1);
hue = argument(valx, valy);
if (hue > DPI) hue -= DPI;
if (hue < 0.0) hue += DPI;
hue = pp1*oldhue + pp2*hue;
lum = module2(valx, valy);
if (lum > 1.0) lum = 1.0;
// lum = 1.0;
hsl_to_rgb_palette(hue*360.0/DPI, 0.9, 0.5*lum, rgb, COLOR_PALETTE_DIRECTION);
break;
}
case (BG_FORCE):
{
value = 0.0;
nnb = hashgrid[n].nneighb;
for (q=0; q<nnb; q++)
{
m = hashgrid[n].neighbour[q];
for (k=0; k<hashgrid[m].number; k++)
{
p = hashgrid[m].particles[k];
value += module2(particle[p].fx, particle[p].fy);
}
}
/* hashcell n counts double */
for (k=0; k<hashgrid[n].number; k++)
{
p = hashgrid[n].particles[k];
value += module2(particle[p].fx, particle[p].fy);
}
value *= BG_FORCE_SLOPE/(double)(nnb + 1);
hue = (1.0 - tanh(value))*360.0;
hue = pp1*oldhue + pp2*hue;
hsl_to_rgb_turbo(hue, 0.9, 0.5, rgb);
break;
}
}
if (DOUBLE_MOVIE)
{
if (counter) hashgrid[n].hue1 = hue;
else hashgrid[n].hue2 = hue;
}
else hashgrid[n].hue1 = hue;
glColor3f(rgb[0], rgb[1], rgb[2]);
glVertex2d(hashgrid[n].x1, hashgrid[n].y1);
glVertex2d(hashgrid[n].x2, hashgrid[n].y1);
glVertex2d(hashgrid[n].x2, hashgrid[n].y2);
glVertex2d(hashgrid[n].x1, hashgrid[n].y2);
}
glEnd();
first = 0;
counter = 1 - counter;
}
void draw_particles(t_particle particle[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES], int plot, double beta, t_collision *collisions, int ncollisions, int bg_color, t_hashgrid hashgrid[HASHX*HASHY], t_lj_parameters params)
{
int i, j, k, m, width, nnbg, nsides, cl, p, q;
double ej, hue, huex, huey, rgb[3], rgbx[3], rgby[3], radius, x1, y1, x2, y2, angle, ca, sa, length, linkcolor, sign = 1.0, angle1, signy = 1.0, periodx, periody, x, y, lum, logratio;
char message[100];
printf("Drawing particles\n");
// if (!TRACER_PARTICLE) blank();
if (plot == P_NOPARTICLE) blank();
// if ((COLOR_BACKGROUND)&&(bg_color > 0)) color_background(particle, bg_color, hashgrid);
// else
if ((!TRACER_PARTICLE)&&(!COLOR_BACKGROUND)&&(!DRAW_OBSTACLE_LINKS)&&(!FILL_OBSTACLE_TRIANGLES)) blank();
// printf("Drawing particles 1\n");
glColor3f(1.0, 1.0, 1.0);
/* show region of partial thermostat */
if (PARTIAL_THERMO_COUPLING)
{
switch (PARTIAL_THERMO_REGION) {
case (TH_INBOX):
{
if (INCREASE_BETA)
{
logratio = log(beta/BETA)/log(0.5*BETA_FACTOR);
if (logratio > 1.0) logratio = 1.0;
else if (logratio < 0.0) logratio = 0.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;
}
else hue = 0.25*PARTICLE_HUE_MIN + 0.75*PARTICLE_HUE_MAX;
erase_area_hsl_turbo(0.0, YMIN, 2.0*PARTIAL_THERMO_WIDTH, PARTIAL_THERMO_HEIGHT*(YMAX - YMIN), hue, 0.9, 0.15);
break;
}
case(TH_LAYER):
{
hue = 0.75*PARTICLE_HUE_MIN + 0.25*PARTICLE_HUE_MAX;
erase_area_hsl_turbo(0.0, YMIN, XMAX, PARTIAL_THERMO_HEIGHT - YMIN, hue, 0.9, 0.15);
break;
}
case (TH_RING):
{
hue = 0.75*PARTICLE_HUE_MIN + 0.25*PARTICLE_HUE_MAX;
hsl_to_rgb_turbo(hue, 0.9, 0.15, rgb);
draw_colored_circle(0.0, 0.0, PARTIAL_THERMO_WIDTH, 180, rgb);
break;
}
case (TH_RING_EXPAND):
{
hue = 0.75*PARTICLE_HUE_MIN + 0.25*PARTICLE_HUE_MAX;
hsl_to_rgb_turbo(hue, 0.9, 0.15, rgb);
draw_colored_circle(0.0, 0.0, params.thermo_radius, 180, rgb);
break;
}
case(TH_INIT):
{
hue = 0.75*PARTICLE_HUE_MIN + 0.25*PARTICLE_HUE_MAX;
erase_rectangle_hsl_turbo(INITXMIN, INITYMIN, INITXMAX, INITYMAX, hue, 0.9, 0.15);
break;
}
case(TH_THERMO):
{
if (INCREASE_BETA)
{
logratio = log(beta/BETA)/log(0.5*BETA_FACTOR);
if (logratio > 1.0) logratio = 1.0;
else if (logratio < 0.0) logratio = 0.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;
}
else hue = 0.25*PARTICLE_HUE_MIN + 0.75*PARTICLE_HUE_MAX;
erase_rectangle_hsl_turbo(THERMOXMIN, THERMOYMIN, THERMOXMAX, THERMOYMAX, hue, 0.9, 0.15);
break;
}
case (TH_CONE):
{
if (INCREASE_BETA)
{
logratio = log(beta/BETA)/log(0.5*BETA_FACTOR);
if (logratio > 1.0) logratio = 1.0;
else if (logratio < 0.0) logratio = 0.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;
}
else hue = 0.25*PARTICLE_HUE_MIN + 0.75*PARTICLE_HUE_MAX;
draw_colored_triangle_turbo(0.05, -0.2, LAMBDA, LAMBDA, -LAMBDA, LAMBDA, hue, 0.9, 0.15);
draw_colored_triangle_turbo(0.05, -0.2, -LAMBDA, LAMBDA, -0.04, -0.2, hue, 0.9, 0.15);
break;
}
}
}
// printf("Drawing particles 2\n");
/* draw "altitude lines" */
if (ALTITUDE_LINES) draw_altitude_lines();
/* draw the bonds first */
if ((DRAW_BONDS)||(plot == P_BONDS))
{
glLineWidth(LINK_WIDTH);
for (j=0; j<ncircles; j++) if (particle[j].active) draw_one_particle_links(particle[j]);
}
/* fill triangles between particles */
if (FILL_TRIANGLES) draw_triangles(particle, cluster, plot);
/* draw collision discs */
if ((REACTION_DIFFUSION)&&(ncollisions > 0)) draw_collisions(collisions, ncollisions);
/* determine particle color and size */
for (j=0; j<ncircles; j++) if (particle[j].active)
{
compute_particle_colors(particle[j], cluster, plot, rgb, rgbx, rgby, &radius, &width, particle);
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;
}
case (I_LJ_WATER):
{
nsides = NSEG;
radius *= 0.75;
break;
}
case (I_COULOMB_PENTA):
{
nsides = 5;
break;
}
case (I_DNA_CHARGED):
{
if (particle[j].type == 7) nsides = 3;
else nsides = NSEG;
break;
}
case (I_SEGMENT):
{
nsides = 2;
break;
}
case (I_SEGMENT_CHARGED):
{
nsides = 2;
break;
}
case (I_POLYGON):
{
nsides = NPOLY;
break;
}
case (I_POLYGON_ALIGN):
{
nsides = NPOLY;
break;
}
default: nsides = NSEG;
}
angle = particle[j].angle + APOLY*DPI;
/* in case of periodic b.c., draw translates of particles */
if ((PERIODIC_BC)&&(plot != P_NOPARTICLE))
{
x1 = particle[j].xc;
y1 = particle[j].yc;
for (i=-1; i<2; i++)
for (k=-1; k<2; k++)
draw_one_particle(particle[j], x1 + (double)i*(BCXMAX - BCXMIN), y1 + (double)k*(BCYMAX - BCYMIN), radius, angle, nsides, width, rgb);
}
else if ((BOUNDARY_COND == BC_KLEIN)&&(plot != P_NOPARTICLE))
{
x1 = particle[j].xc;
y1 = particle[j].yc;
for (i=-2; i<3; i++)
{
if (vabs(i) == 1) sign = -1.0;
else sign = 1.0;
angle1 = angle*sign;
for (k=-1; k<2; k++)
draw_one_particle(particle[j], x1 + (double)i*(BCXMAX - BCXMIN), sign*(y1 + (double)k*(BCYMAX - BCYMIN)),
radius, angle1, nsides, width, rgb);
}
}
else if ((BOUNDARY_COND == BC_BOY)&&(plot != P_NOPARTICLE))
{
x1 = particle[j].xc;
y1 = particle[j].yc;
for (i=-1; i<2; i++) for (k=-1; k<2; k++)
{
if (vabs(i) == 1) sign = -1.0;
else sign = 1.0;
if (vabs(k) == 1) signy = -1.0;
else signy = 1.0;
if (signy == 1.0) angle1 = angle*sign;
else angle1 = PI - angle;
if (sign == -1.0) draw_one_particle(particle[j], signy*(x1 + (double)i*(BCXMAX - BCXMIN)),
sign*(y1 + (double)k*(BCYMAX - BCYMIN)), radius, angle1, nsides, width, rgbx);
else if (signy == -1.0) draw_one_particle(particle[j], signy*(x1 + (double)i*(BCXMAX - BCXMIN)),
sign*(y1 + (double)k*(BCYMAX - BCYMIN)), radius, angle1, nsides, width, rgby);
else draw_one_particle(particle[j], signy*(x1 + (double)i*(BCXMAX - BCXMIN)),
sign*(y1 + (double)k*(BCYMAX - BCYMIN)), radius, angle1, nsides, width, rgb);
}
}
else if ((BOUNDARY_COND == BC_GENUS_TWO)&&(plot != P_NOPARTICLE))
{
x1 = particle[j].xc;
y1 = particle[j].yc;
if (x1 < 0.0) periody = BCYMAX - BCYMIN;
else periody = 0.5*(BCYMAX - BCYMIN);
if (y1 < 0.0) periodx = BCXMAX - BCXMIN;
else periodx = 0.5*(BCXMAX - BCXMIN);
if ((x1 < 0.0)&&(y1 < 0.0))
for (i=-1; i<2; i++)
for (k=-1; k<2; k++)
{
x = x1 + (double)i*periodx;
y = y1 + (double)k*periody;
draw_one_particle(particle[j], x, y, radius, angle, nsides, width, rgb);
}
else if ((x1 < 0.0)&&(y1 >= 0.0))
for (i=-1; i<2; i++)
for (k=-1; k<2; k++)
{
x = x1 + (double)i*periodx;
y = y1 + (double)k*periody;
if (x < 1.2*particle[j].radius)
draw_one_particle(particle[j], x, y, radius, angle, nsides, width, rgb);
}
else if ((x1 >= 0.0)&&(y1 < 0.0))
for (i=-1; i<2; i++)
for (k=-1; k<2; k++)
{
x = x1 + (double)i*periodx;
y = y1 + (double)k*periody;
if (y < 1.2*particle[j].radius)
draw_one_particle(particle[j], x, y, radius, angle, nsides, width, rgb);
}
}
else if (plot != P_NOPARTICLE)
draw_one_particle(particle[j], particle[j].xc, particle[j].yc, 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);
}
}
/* draw links between particles in cluster */
if (DRAW_CLUSTER_LINKS)
{
glLineWidth(LINK_WIDTH);
for (j=0; j<ncircles; j++) if (particle[j].active)
{
cl = particle[j].cluster;
x1 = particle[j].xc;
y1 = particle[j].yc;
for (p=0; p<cluster[cl].nparticles; p++)
{
q = cluster[cl].particle[p];
x2 = particle[q].xc;
y2 = particle[q].yc;
draw_line(x1, y1, x2, y2);
}
}
}
}
void obstacle_hue(t_obstacle obstacle, double rgb[3])
/* compute obstacle color hue */
{
int k;
double etot, angle, hue, lum;
switch (OBSTACLE_COLOR) {
case (OC_ENERGY):
{
etot = obstacle.energy;
hue = ENERGY_HUE_MIN + (ENERGY_HUE_MAX - ENERGY_HUE_MIN)*etot/OBSTACLE_EMAX;
if (hue > ENERGY_HUE_MIN) hue = ENERGY_HUE_MIN;
if (hue < ENERGY_HUE_MAX) hue = ENERGY_HUE_MAX;
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_EKIN);
break;
}
case (OC_DIRECTION):
{
hue = argument(obstacle.vx, obstacle.vy);
if (hue < 0.0) hue += DPI;
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(hue)/DPI;
hsl_to_rgb_palette(hue, 0.9, 0.5, rgb, COLOR_PALETTE_DIRECTION);
break;
}
case (OC_DIRECTION_ENERGY):
{
hue = argument(obstacle.vx, obstacle.vy);
etot = obstacle.energy;
if (hue < 0.0) hue += DPI;
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*(hue)/DPI;
lum = etot/OBSTACLE_EMAX;
if (lum > 0.5) lum = 0.5;
hsl_to_rgb_palette(hue, 0.9, lum, rgb, COLOR_PALETTE_DIRECTION);
break;
}
}
}
int area_to_rgb(t_obstacle obstacle[NMAXOBSTACLES], int i, int n1, int n2, double rgb[3])
/* computes rgb code of triangle according to area */
/* returns 1 if angle of triangle is less than PI/2 */
/* OLD VERSION */
{
int p, q, k, n, counter;
double area, angle1, angle2;
static int first;
static double mean_area;
if (first)
{
/* compute mean area of triangles */
if (FILL_OBSTACLE_TRIANGLES)
{
counter = 0;
mean_area = 0.0;
for (p = 0; p < nobstacles; p++)
{
n = obstacle[p].nneighb;
for (q = 0; q < n - 1; q++)
{
n1 = obstacle[p].neighb[q];
n2 = obstacle[p].neighb[q+1];
mean_area += vabs(triangle_area(obstacle[p].xc, obstacle[p].yc, obstacle[n1].xc, obstacle[n1].yc, obstacle[n2].xc, obstacle[n2].yc));
counter++;
}
n1 = obstacle[p].neighb[n-1];
n2 = obstacle[p].neighb[0];
mean_area += vabs(triangle_area(obstacle[p].xc, obstacle[p].yc, obstacle[n1].xc, obstacle[n1].yc, obstacle[n2].xc, obstacle[n2].yc));
counter++;
}
mean_area *= 1.0/(double)counter;
}
first = 0;
}
area = vabs(triangle_area(obstacle[i].xc, obstacle[i].yc, obstacle[n1].xc, obstacle[n1].yc, obstacle[n2].xc, obstacle[n2].yc));
area = OBSTACLE_AREA_SHADE_FACTOR*(area - mean_area);
if (area > 1.0) area = 1.0;
if (area < 0.0) area = 0.0;
for (k=0; k<3; k++) rgb[k] = area;
angle1 = argument(obstacle[n1].xc - obstacle[i].xc, obstacle[n1].yc - obstacle[i].yc);
angle2 = argument(obstacle[n2].xc - obstacle[i].xc, obstacle[n2].yc - obstacle[i].yc);
if (angle2 < angle1) angle2 += DPI;
return(angle2 - angle1 < 1.5*PID);
}
void old_draw_obstacle_triangles(t_obstacle obstacle[NMAXOBSTACLES])
/* draw the triangles between obstacles, for option FILL_OBSTACLE_TRIANGLES */
{
int i, j, k, n, n1, n2, neigh;
short int acute;
double rgb[3], angle1, angle2;
if (FILL_OBSTACLE_TRIANGLES) for (i = 0; i < nobstacles; i++)
{
n = obstacle[i].nneighb;
for (j = 0; j < n - 1; j++)
{
n1 = obstacle[i].neighb[j];
n2 = obstacle[i].neighb[j+1];
acute = area_to_rgb(obstacle, i, n1, n2, rgb);
if (acute) draw_colored_triangle(obstacle[i].xc, obstacle[i].yc, obstacle[n1].xc, obstacle[n1].yc, obstacle[n2].xc, obstacle[n2].yc, rgb);
}
n1 = obstacle[i].neighb[n-1];
n2 = obstacle[i].neighb[0];
acute = area_to_rgb(obstacle, i, n1, n2, rgb);
if (acute) draw_colored_triangle(obstacle[i].xc, obstacle[i].yc, obstacle[n1].xc, obstacle[n1].yc, obstacle[n2].xc, obstacle[n2].yc, rgb);
}
glColor3f(1.0, 1.0, 1.0);
for (i = 0; i < nobstacles; i++)
for (j = 0; j < obstacle[i].nneighb; j++)
{
neigh = obstacle[i].neighb[j];
draw_line(obstacle[i].xc, obstacle[i].yc, obstacle[neigh].xc, obstacle[neigh].yc);
}
}
void facet_area_to_rgb(t_obstacle obstacle[NMAXOBSTACLES], t_otriangle triangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES], t_ofacet facet[NMAXOBSTACLES], int nfacet, double rgb[3])
/* computes rgb code of triangle according to area */
/* returns 1 if angle of triangle is less than PI/2 */
{
int p, q, k, n, counter;
double area, area0;
static int first;
static double mean_area;
if (first)
{
/* compute mean area of triangles */
if (FILL_OBSTACLE_TRIANGLES)
{
counter = 0;
mean_area = 0.0;
for (p = 0; p < n_otriangles; p++)
{
mean_area += otriangle_area(obstacle, triangle[p]);
counter++;
}
mean_area *= 1.0/(double)counter;
for (p = 0; p < n_ofacets; p++)
{
area0 = 0.0;
for (k = 0; k < facet[p].ntriangles; k++)
area0 += triangle[facet[p].triangle[k]].area0;
facet[p].area0 = area0/(double)facet[p].ntriangles;
}
}
first = 0;
}
area = 0.0;
for (k = 0; k < facet[nfacet].ntriangles; k++)
{
p = facet[nfacet].triangle[k];
area += otriangle_area(obstacle, triangle[p]);
}
area *= 1.0/(double)facet[nfacet].ntriangles;
area = OBSTACLE_AREA_SHADE_FACTOR*(area - facet[nfacet].area0);
if (area > 1.0) area = 1.0;
if (area < 0.0) area = 0.0;
for (k=0; k<3; k++) rgb[k] = area;
}
double triangle_area_to_rgb(t_obstacle obstacle[NMAXOBSTACLES], t_otriangle otriangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES], int triangle, double rgb[3])
{
double area;
int k, p;
area = otriangle_area(obstacle, otriangle[triangle]);
area = 0.6 + OBSTACLE_AREA_SHADE_FACTOR*(area - otriangle[triangle].area0);
if (area > 0.9) area = 0.9;
if (area < 0.1) area = 0.1;
for (k=0; k<3; k++) rgb[k] = area;
return(area);
}
void draw_obstacle_triangles(t_obstacle obstacle[NMAXOBSTACLES],
t_otriangle otriangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES],
t_ofacet ofacet[NMAXOBSTACLES])
/* draw the triangles between obstacles, for option FILL_OBSTACLE_TRIANGLES */
{
int i, j, k, n, n1, n2, neigh, nf, f, t, s1, s2, s3;
short int acute;
double rgb[3], pvect;
printf("drawing %i facets with %i triangles\n", n_ofacets, n_otriangles);
if (FILL_OBSTACLE_TRIANGLES)
{
/* determine acute angles */
for (t = 0; t < n_otriangles; t++)
{
s1 = otriangle[t].i[0];
s2 = otriangle[t].i[1];
s3 = otriangle[t].i[2];
pvect = (obstacle[s2].xc - obstacle[s1].xc)*(obstacle[s3].yc - obstacle[s1].yc);
pvect -= (obstacle[s2].yc - obstacle[s1].yc)*(obstacle[s3].xc - obstacle[s1].xc);
otriangle[t].acute = (pvect > 0.0);
}
if (SHADE_OBSTACLE_FACETS) for (i = 0; i < n_ofacets; i++)
{
facet_area_to_rgb(obstacle, otriangle, ofacet, i, rgb);
nf = ofacet[i].ntriangles;
for (f = 0; f < nf; f++)
{
t = ofacet[i].triangle[f];
if (otriangle[t].acute)
{
s1 = otriangle[t].i[0];
s2 = otriangle[t].i[1];
s3 = otriangle[t].i[2];
draw_colored_triangle(obstacle[s1].xc, obstacle[s1].yc, obstacle[s2].xc, obstacle[s2].yc, obstacle[s3].xc, obstacle[s3].yc, rgb);
}
}
}
else for (t = 0; t < n_otriangles; t++) if (otriangle[t].acute)
{
triangle_area_to_rgb(obstacle, otriangle, t, rgb);
s1 = otriangle[t].i[0];
s2 = otriangle[t].i[1];
s3 = otriangle[t].i[2];
draw_colored_triangle(obstacle[s1].xc, obstacle[s1].yc, obstacle[s2].xc, obstacle[s2].yc, obstacle[s3].xc, obstacle[s3].yc, rgb);
}
}
glColor3f(1.0, 1.0, 1.0);
for (i = 0; i < nobstacles; i++)
for (j = 0; j < obstacle[i].nneighb; j++)
{
neigh = obstacle[i].neighb[j];
draw_line(obstacle[i].xc, obstacle[i].yc, obstacle[neigh].xc, obstacle[neigh].yc);
}
}
void draw_container(double xmin, double xmax, t_obstacle obstacle[NMAXOBSTACLES], t_segment segment[NMAXSEGMENTS], t_belt *conveyor_belt, int wall)
/* draw the container, for certain boundary conditions */
{
int i, j, k, n, n1, n2, neigh;
double rgb[3], x, y, r, phi, angle, dx, dy, ybin, x1, x2, y1, y2, h, bangle, blength, pos0, pos, bsegment, tx, ty, bwidth, ca, sa, ekin, epot, etot, hue, area;
char message[100];
/* draw fixed obstacles */
if (ADD_FIXED_OBSTACLES)
{
glLineWidth(CONTAINER_WIDTH);
if (CHARGE_OBSTACLES) hsl_to_rgb(30.0, 0.1, 0.5, rgb);
else if (!OSCILLATE_OBSTACLES) hsl_to_rgb(300.0, 0.1, 0.5, rgb);
for (i = 0; i < nobstacles; i++)
{
if (OSCILLATE_OBSTACLES)
{
obstacle_hue(obstacle[i], rgb);
}
draw_colored_circle_precomp(obstacle[i].xc - xtrack, obstacle[i].yc - ytrack, obstacle[i].radius, rgb);
}
glColor3f(1.0, 1.0, 1.0);
for (i = 0; i < nobstacles; i++)
{
x = obstacle[i].xc - xtrack;
y = obstacle[i].yc - ytrack;
r = obstacle[i].radius;
draw_circle_precomp(x, y, r);
if (CHARGE_OBSTACLES)
{
draw_line(x - r, y, x + r, y);
draw_line(x, y - r, x, y + r);
}
if (ROTATE_OBSTACLES)
{
ca = cos(obstacle[i].angle);
sa = sin(obstacle[i].angle);
// printf("Obstacle %i angle = %.5lg\n", i, obstacle[i].angle);
draw_line(x - r*ca, y - r*sa, x + r*ca, y + r*sa);
draw_line(x + r*sa, y - r*ca, x - r*sa, y + r*ca);
}
}
}
if (ADD_FIXED_SEGMENTS)
{
glLineWidth(CONTAINER_WIDTH);
glColor3f(1.0, 1.0, 1.0);
for (i = 0; i < nsegments; i++) if (segment[i].active)
{
if (COLOR_SEG_GROUPS) set_segment_group_color(segment[i].group, 1.0, rgb);
else if (SHOW_SEGMENTS_PRESSURE) set_segment_pressure_color(segment[i].avrg_pressure, 1.0, rgb);
draw_line(segment[i].x1 - xtrack, segment[i].y1 - ytrack, segment[i].x2 - xtrack, segment[i].y2 - ytrack);
}
/* draw conveyor belt */
if (ADD_CONVEYOR_FORCE) for (i=0; i<nbelts;i++)
{
bwidth = conveyor_belt[i].width;
r = 0.7*bwidth;
tx = conveyor_belt[i].tx;
ty = conveyor_belt[i].ty;
bangle = -conveyor_belt[i].position/(bwidth);
blength = 2.0*conveyor_belt[i].length + DPI*conveyor_belt[i].width;
dx = r*cos(bangle);
dy = r*sin(bangle);
x1 = conveyor_belt[i].x1 - xtrack;
y1 = conveyor_belt[i].y1 - ytrack;
draw_line(x1-dx, y1-dy, x1+dx, y1+dy);
draw_line(x1+dy, y1-dx, x1-dy, y1+dx);
draw_circle_precomp(x1, y1, r);
x2 = conveyor_belt[i].x2 - xtrack;
y2 = conveyor_belt[i].y2 - ytrack;
draw_line(x2-dx, y2-dy, x2+dx, y2+dy);
draw_line(x2+dy, y2-dx, x2-dy, y2+dx);
draw_circle_precomp(x2, y2, r);
draw_line(x1-r*ty, y1+r*tx, x2-r*ty, y2+r*tx);
draw_line(x1+r*ty, y1-r*tx, x2+r*ty, y2-r*tx);
bsegment = 10.0*blength;
bsegment = blength/(double)((int)bsegment);
if (conveyor_belt[i].position > 0.0)
pos0 = conveyor_belt[i].position - bsegment*(double)((int)(conveyor_belt[i].position/bsegment));
else
{
pos = -conveyor_belt[i].position;
pos0 = pos - bsegment*(double)((int)(pos/bsegment));
pos0 = bsegment - pos0;
}
while (pos0 < conveyor_belt[i].length)
{
x = x1 + tx*pos0;
y = y1 + ty*pos0;
draw_line(x - r*ty, y + r*tx, x - bwidth*ty, y + bwidth*tx);
pos0 += bsegment;
}
while (pos0 < conveyor_belt[i].length + PI*bwidth)
{
pos = (pos0 - conveyor_belt[i].length)/bwidth;
angle = PID + conveyor_belt[i].angle - pos;
ca = cos(angle);
sa = sin(angle);
draw_line(x2 + r*ca, y2 + r*sa, x2 + bwidth*ca, y2 + bwidth*sa);
pos0 += bsegment;
}
while (pos0 < 2.0*conveyor_belt[i].length + PI*bwidth)
{
pos = pos0 - conveyor_belt[i].length - PI*bwidth;
x = x2 - tx*pos;
y = y2 - ty*pos;
draw_line(x + r*ty, y - r*tx, x + bwidth*ty, y - bwidth*tx);
pos0 += bsegment;
}
while (pos0 < 2.0*conveyor_belt[i].length + DPI*bwidth)
{
pos = (pos0 - 2.0*conveyor_belt[i].length - PI*bwidth)/bwidth;
angle = -PID + conveyor_belt[i].angle - pos;
ca = cos(angle);
sa = sin(angle);
draw_line(x1 + r*ca, y1 + r*sa, x1 + bwidth*ca, y1 + bwidth*sa);
pos0 += bsegment;
}
}
}
switch (BOUNDARY_COND) {
case (BC_SCREEN):
{
/* do nothing */
break;
}
case (BC_RECTANGLE):
{
glColor3f(1.0, 1.0, 1.0);
glLineWidth(CONTAINER_WIDTH);
draw_line(BCXMIN, BCYMIN, BCXMAX, BCYMIN);
draw_line(BCXMIN, BCYMAX, BCXMAX, BCYMAX);
if (!SYMMETRIC_DECREASE) draw_line(BCXMAX, BCYMIN, BCXMAX, BCYMAX);
draw_line(xmin, BCYMIN, xmin, BCYMAX);
// draw_line(XMIN, 0.5*(BCYMIN + BCYMAX), xmin, 0.5*(BCYMIN + BCYMAX));
if (SYMMETRIC_DECREASE)
{
draw_line(xmax, BCYMIN, xmax, BCYMAX);
draw_line(XMAX, 0.5*(BCYMIN + BCYMAX), xmax, 0.5*(BCYMIN + BCYMAX));
}
break;
}
case (BC_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) x = 0.0;
else x = xmin + (double)i*(OBSXMAX - OBSXMIN);
draw_colored_circle_precomp(x, 0.0, OBSTACLE_RADIUS, rgb);
glColor3f(1.0, 1.0, 1.0);
draw_circle_precomp(x, 0.0, OBSTACLE_RADIUS);
glColor3f(0.0, 0.0, 0.0);
sprintf(message, "Mach %.3f", xspeed/20.0);
// sprintf(message, "Speed %.2f", xspeed);
write_text(x-0.17, -0.025, message);
}
break;
}
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) x = 0.0;
else x = xmin + (double)i*(OBSXMAX - OBSXMIN);
draw_colored_circle_precomp(x, 0.0, OBSTACLE_RADIUS, rgb);
glColor3f(1.0, 1.0, 1.0);
draw_circle_precomp(x, 0.0, OBSTACLE_RADIUS);
glColor3f(0.0, 0.0, 0.0);
sprintf(message, "Mach %.2f", xspeed/20.0);
// sprintf(message, "Speed %.2f", xspeed);
write_text(x-0.17, -0.025, message);
}
break;
}
case (BC_PERIODIC_TRIANGLE):
{
glLineWidth(CONTAINER_WIDTH);
hsl_to_rgb(300.0, 0.1, 0.5, rgb);
for (i=-1; i<2; i++)
{
if (CENTER_VIEW_ON_OBSTACLE) x = 0.0;
else x = xmin + (double)i*(OBSXMAX - OBSXMIN);
x1 = x + OBSTACLE_RADIUS;
x2 = x - OBSTACLE_RADIUS;
h = 2.0*OBSTACLE_RADIUS*tan(APOLY*PID);
draw_colored_triangle(x1, 0.0, x2, h, x2, -h, rgb);
glColor3f(1.0, 1.0, 1.0);
draw_triangle(x1, 0.0, x2, h, x2, -h);
glColor3f(0.0, 0.0, 0.0);
sprintf(message, "Mach %.2f", xspeed*3.0/40.0);
write_text(x-0.25, -0.025, message);
}
break;
}
case (BC_PERIODIC_FUNNEL):
{
glLineWidth(CONTAINER_WIDTH);
hsl_to_rgb(300.0, 0.1, 0.5, rgb);
for (i=-1; i<2; i++)
{
if (CENTER_VIEW_ON_OBSTACLE) x = 0.0;
else x = xmin + (double)i*(OBSXMAX - OBSXMIN);
for (j=-1; j<2; j+=2)
{
draw_colored_circle_precomp(x, (double)j*(FUNNEL_WIDTH + OBSTACLE_RADIUS), OBSTACLE_RADIUS, rgb);
glColor3f(1.0, 1.0, 1.0);
draw_circle_precomp(x, (double)j*(FUNNEL_WIDTH + OBSTACLE_RADIUS), OBSTACLE_RADIUS);
}
glColor3f(0.0, 0.0, 0.0);
sprintf(message, "Mach %.2f", xspeed/20.0);
write_text(x-0.17, 0.75, message);
}
break;
}
case (BC_RECTANGLE_LID):
{
glColor3f(1.0, 1.0, 1.0);
glLineWidth(CONTAINER_WIDTH);
draw_line(BCXMIN, BCYMIN, BCXMAX, BCYMIN);
draw_line(BCXMIN, BCYMIN, BCXMIN, BCYMAX);
draw_line(BCXMAX, BCYMIN, BCXMAX, BCYMAX);
hsl_to_rgb(300.0, 0.1, 0.5, rgb);
draw_colored_rectangle(BCXMIN + 0.05, ylid, BCXMAX - 0.05, ylid + LID_WIDTH, rgb);
glColor3f(1.0, 1.0, 1.0);
draw_rectangle(BCXMIN + 0.05, ylid, BCXMAX - 0.05, ylid + LID_WIDTH);
break;
}
case (BC_RECTANGLE_WALL):
{
glColor3f(1.0, 1.0, 1.0);
glLineWidth(CONTAINER_WIDTH);
draw_rectangle(BCXMIN, BCYMIN, BCXMAX, BCYMAX);
draw_line(0.5*(BCXMIN+BCXMAX), BCYMAX, 0.5*(BCXMIN+BCXMAX), BCYMAX + 0.5*WALL_WIDTH);
draw_line(0.5*(BCXMIN+BCXMAX), BCYMIN, 0.5*(BCXMIN+BCXMAX), BCYMIN - 0.5*WALL_WIDTH);
if (wall)
{
hsl_to_rgb(300.0, 0.1, 0.5, rgb);
draw_colored_rectangle(xwall - 0.5*WALL_WIDTH, BCYMIN + 0.025, xwall + 0.5*WALL_WIDTH, BCYMAX - 0.025, rgb);
glColor3f(1.0, 1.0, 1.0);
draw_rectangle(xwall - 0.5*WALL_WIDTH, BCYMIN + 0.025, xwall + 0.5*WALL_WIDTH, BCYMAX - 0.025);
}
break;
}
case (BC_EHRENFEST):
{
glLineWidth(CONTAINER_WIDTH);
glColor3f(1.0, 1.0, 1.0);
phi = asin(EHRENFEST_WIDTH/EHRENFEST_RADIUS);
glBegin(GL_LINE_LOOP);
for (i=0; i<=NSEG; i++)
{
angle = -PI + phi + (double)i*2.0*(PI - phi)/(double)NSEG;
glVertex2d(1.0 + EHRENFEST_RADIUS*cos(angle), EHRENFEST_RADIUS*sin(angle));
}
for (i=0; i<=NSEG; i++)
{
angle = phi + (double)i*2.0*(PI - phi)/(double)NSEG;
glVertex2d(-1.0 + EHRENFEST_RADIUS*cos(angle), EHRENFEST_RADIUS*sin(angle));
}
glEnd();
break;
}
case (BC_SCREEN_BINS):
{
glLineWidth(CONTAINER_WIDTH);
glColor3f(1.0, 1.0, 1.0);
dy = (YMAX - YMIN)/((double)NGRIDX + 3);
dx = dy/cos(PI/6.0);
ybin = 2.75*dy;
for (i=-1; i<=NGRIDX; i++)
{
x = ((double)i - 0.5*(double)NGRIDX + 0.5)*dx;
draw_line(x, YMIN, x, YMIN + ybin);
}
break;
}
case (BC_GENUS_TWO):
{
hsl_to_rgb(300.0, 0.1, 0.5, rgb);
draw_colored_rectangle(0.0, 0.0, BCXMAX, BCYMAX, rgb);
break;
}
default:
{
/* do nothing */
}
}
}
void print_omega(double angle, double angular_speed, double fx, double fy)
{
char message[100];
double rgb[3], y1, frac, absa;
static double xleftbox, xlefttext, xrightbox, xrighttext, y = YMAX - 0.1, ymin = YMIN + 0.05;
static int first = 1;
if (first)
{
xrightbox = XMAX - 0.54;
xrighttext = xrightbox - 0.48;
first = 0;
}
y1 = y;
if (PRINT_ANGLE)
{
erase_area_hsl(xrightbox, y + 0.025, 0.42, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
angle = angle*360.0/DPI;
absa = vabs(angle);
frac = absa - (double)((int)absa);
sprintf(message, "Angle = %3d.%d degrees", (int)absa, (int)(10.0*frac));
write_text(xrighttext + 0.1, y, message);
y1 -= 0.1;
}
if (PRINT_OMEGA)
{
erase_area_hsl(xrightbox, y1 + 0.025, 0.42, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Angular speed = %.4f", angular_speed);
write_text(xrighttext + 0.1, y1, message);
y1 -= 0.1;
}
if (PRINT_SEGMENTS_FORCE)
{
erase_area_hsl(xrightbox, y1 + 0.025, 0.42, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Fx = %.4f", fx);
write_text(xrighttext + 0.1, y1, message);
y1 -= 0.1;
erase_area_hsl(xrightbox, y1 + 0.025, 0.42, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Fy = %.4f", fy);
write_text(xrighttext + 0.1, y1, message);
y1 -= 0.1;
erase_area_hsl(xrightbox, y1 + 0.025, 0.42, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Fy/Fx = %.4f", fy/fx);
write_text(xrighttext + 0.1, y1, message);
}
}
void compute_segments_force(t_lj_parameters *params, t_segment segment[NMAXSEGMENTS])
{
int i;
double fx = 0.0, fy = 0.0;
for (i=0; i<nsegments; i++) if (segment[i].active)
{
fx += segment[i].fx;
fy += segment[i].fy;
}
params->bdry_fx = fx;
params->bdry_fy = fy;
}
void print_parameters(t_lj_parameters params, short int left, double pressure[N_PRESSURES], short int refresh)
{
char message[100];
int i, j, k;
double density, hue, rgb[3], logratio, x, y, meanpress[N_PRESSURES], phi, sphi, dphi, pprint, mean_temp, lengthcontainer, boundary_force, fx, fy, r1, r2;
static double xbox, xtext, xmid, xmidtext, xxbox, xxtext, pressures[N_P_AVERAGE], meanpressure = 0.0, maxpressure = 0.0, mean_fx, mean_fy;
static double press[N_PRESSURES][N_P_AVERAGE], temp[N_T_AVERAGE], scale;
static int first = 1, i_pressure, i_temp;
if (first)
{
// scale = (XMAX - XMIN)/4.0;
scale = (YMAX - YMIN)/2.5;
if (left)
{
xbox = XMIN + 0.45*scale;
xtext = XMIN + 0.12*scale;
xxbox = XMAX - 0.39*scale;
xxtext = XMAX - 0.73*scale;
}
else
{
xbox = XMAX - 0.41*scale;
xtext = XMAX - 0.73*scale;
xxbox = XMIN + 0.4*scale;
xxtext = XMIN + 0.08*scale;
}
xmid = 0.5*(XMIN + XMAX) - 0.1*scale;
// xmid = 0.5*(XMIN + XMAX) + 0.05*scale;
xmidtext = xmid - 0.2*scale;
for (i=0; i<N_P_AVERAGE; i++) pressures[i] = 0.0;
if (RECORD_PRESSURES) for (j=0; j<N_PRESSURES; j++)
{
meanpress[j] = 0.0;
for (i=0; i<N_P_AVERAGE; i++) press[j][i] = 0.0;
}
i_pressure = 0;
i_temp = 0;
for (i=0; i<N_T_AVERAGE; i++) temp[i] = 0.0;
mean_fx = 0.0;
mean_fy = 0.0;
r1 = 0.005;
r2 = 1.0 - r1;
first = 0;
}
lengthcontainer = params.xmaxcontainer - params.xmincontainer;
boundary_force = params.fboundary/(double)(ncircles*NVID);
/* table of pressures */
pressures[i_pressure] = boundary_force/(lengthcontainer + INITYMAX - INITYMIN);
if (RECORD_PRESSURES)
{
for (j=0; j<N_PRESSURES; j++) press[j][i_pressure] = pressure[j];
}
i_pressure++;
if (i_pressure == N_P_AVERAGE) i_pressure = 0;
for (i=0; i<N_P_AVERAGE; i++) meanpressure += pressures[i];
meanpressure = meanpressure/(double)N_P_AVERAGE;
if (RECORD_PRESSURES) for (j=0; j<N_PRESSURES; j++)
{
meanpress[j] = 0.0;
for (i=0; i<N_P_AVERAGE; i++) meanpress[j] += press[j][i];
meanpress[j] = meanpress[j]/(double)N_P_AVERAGE;
}
// if (RECORD_PRESSURES)
// for (j=0; j<N_PRESSURES; j++) meanpress[j] =
//
// for (j=0; j<N_PRESSURES; j++) printf("Mean pressure[%i] = %.5lg\n", j, meanpress[j]);
if (RECORD_PRESSURES)
{
for (j=0; j<N_PRESSURES; j++) if (meanpress[j] > maxpressure) maxpressure = meanpress[j];
printf("Max pressure = %.5lg\n\n", maxpressure);
}
y = YMAX - 0.1*scale;
if ((INCREASE_BETA)||(PRINT_TEMPERATURE)) /* print temperature */
{
logratio = log(params.beta/BETA)/log(0.5*BETA_FACTOR);
if (logratio > 1.0) logratio = 1.0;
else if (logratio < 0.0) logratio = 0.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;
if (PRINT_LEFT) erase_area_hsl_turbo(xbox, y + 0.025*scale, 0.5*scale, 0.05*scale, hue, 0.9, 0.5);
else erase_area_hsl_turbo(xmid + 0.1, y + 0.025*scale, 0.45*scale, 0.05*scale, 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 %.2f", 1.0/params.beta);
if (PRINT_LEFT) write_text(xtext, y, message);
else write_text(xmidtext, y, message);
// y -= 0.1;
// 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);
}
if (DECREASE_CONTAINER_SIZE) /* print density */
{
density = (double)ncircles/((lengthcontainer)*(INITYMAX - INITYMIN));
erase_area_hsl(xbox, y + 0.025*scale, 0.37*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Density %.3f", density);
write_text(xtext + 0.1, y, message);
erase_area_hsl(xmid, y + 0.025*scale, 0.37*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Temperature %.2f", params.mean_energy);
write_text(xmidtext - 0.1, y, message);
erase_area_hsl(xxbox, y + 0.025*scale, 0.37*scale, 0.05*scale, 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*scale, 0.35*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Force constant %.2f", params.krepel);
write_text(xtext + 0.03, y, message);
}
else if (INCREASE_E) /* print electric field */
{
erase_area_hsl(xbox, y + 0.025*scale, 0.27*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "E field = %.2f", 25.0*NVID*DT_PARTICLE*params.efield);
write_text(xtext + 0.08, y, message);
}
else if (INCREASE_B) /* print magnetic field */
{
erase_area_hsl(xbox, y + 0.025*scale, 0.27*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "B field = %.2f", 25.0*NVID*DT_PARTICLE*params.bfield);
write_text(xtext + 0.08, y, message);
}
if (PRINT_NPARTICLES) /* print number of particles */
{
erase_area_hsl(xbox, y + 0.025*scale, 0.27*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "%i electrons", params.nactive);
write_text(xtext + 0.08, y, message);
}
if (PRINT_TYPE_PROP) /* print proportion of types */
{
erase_area_hsl(xbox, y + 0.025*scale, 0.27*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "%.1f %% anions", 100.0*params.prop);
write_text(xtext + 0.08, y, message);
}
if (RECORD_PRESSURES)
{
y = FUNNEL_WIDTH + OBSTACLE_RADIUS;
for (i=0; i<N_PRESSURES; i++)
{
phi = DPI*(double)i/(double)N_PRESSURES;
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*meanpress[i]/MAX_PRESSURE;
if (hue > PARTICLE_HUE_MIN) hue = PARTICLE_HUE_MIN;
if (hue < PARTICLE_HUE_MAX) hue = PARTICLE_HUE_MAX;
hsl_to_rgb_turbo(hue, 0.9, 0.5, rgb);
dphi = DPI/(double)N_PRESSURES;
// x = 0.95*OBSTACLE_RADIUS*cos(phi);
sphi = sin(phi);
if (sphi < 0.0) draw_colored_sector(0.0, y, 0.95*OBSTACLE_RADIUS, OBSTACLE_RADIUS, phi, phi + dphi, rgb, 10);
else draw_colored_sector(0.0, -y, 0.95*OBSTACLE_RADIUS, OBSTACLE_RADIUS, phi, phi + dphi, rgb, 10);
}
glColor3f(1.0, 1.0, 1.0);
for (i=-1; i<2; i++)
{
k = N_PRESSURES/4 + i*N_PRESSURES/9;
phi = DPI*(double)k/(double)N_PRESSURES;
pprint = 0.0;
for (j=-2; j<3; j++) pprint += meanpress[k + j];
sprintf(message, "p = %.0f", pprint*200.0/MAX_PRESSURE);
write_text(0.85*OBSTACLE_RADIUS*cos(phi) - 0.1, -y + 0.85*OBSTACLE_RADIUS*sin(phi), message);
}
}
if ((PARTIAL_THERMO_COUPLING)&&(!INCREASE_BETA)&&(!EXOTHERMIC))
{
printf("Temperature %i in average: %.3lg\n", i_temp, params.mean_energy);
temp[i_temp] = params.mean_energy;
i_temp++;
if (i_temp >= N_T_AVERAGE) i_temp = 0;
mean_temp = 0.0;
for (i=0; i<N_T_AVERAGE; i++) mean_temp += temp[i];
mean_temp = mean_temp/N_T_AVERAGE;
hue = PARTICLE_HUE_MIN + 0.5*(PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*mean_temp/PARTICLE_EMAX;
if (hue < PARTICLE_HUE_MAX) hue = PARTICLE_HUE_MAX;
erase_area_hsl_turbo(xbox, y + 0.025*scale, 0.37*scale, 0.05*scale, 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 %.2f", mean_temp);
write_text(xtext, y, message);
}
if (INCREASE_GRAVITY)
{
erase_area_hsl(xmid, y + 0.025*scale, 0.22*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Gravity %.2f", params.gravity/GRAVITY);
// write_text(xmidtext + 0.1, y, message);
write_text(xmidtext, y, message);
}
if (CHANGE_RADIUS)
{
erase_area_hsl(xmid, y + 0.025*scale, 0.3*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Radius %.4f", params.radius);
write_text(xmidtext + 0.05, y, message);
}
if (PRINT_SEGMENTS_FORCE)
{
glColor3f(1.0, 1.0, 1.0);
if (refresh)
{
fx = 0.01*params.bdry_fx/(double)params.nactive;
fy = 0.01*params.bdry_fy/(double)params.nactive;
/* average boundary force */
mean_fx = r2*mean_fx + r1*fx;
mean_fy = r2*mean_fy + r1*fy;
}
if ((PRINT_ANGLE)||(PRINT_OMEGA))
draw_arrow(0.0, 0.0, FORCE_FACTOR*mean_fx, FORCE_FACTOR*mean_fy, 15.0, 0.1);
else
{
erase_area_hsl(xmid, 0.0, 0.2*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Fy = %.2f", mean_fy);
write_text(xmidtext + 0.15*scale, -0.02*scale, message);
if (mean_fx*mean_fx + mean_fy*mean_fy > 5.0*FORCE_FACTOR)
draw_arrow(0.0, 0.0, FORCE_FACTOR*mean_fx, FORCE_FACTOR*mean_fy, 15.0, 0.1);
}
}
if ((PRINT_ANGLE)||(PRINT_OMEGA)) print_omega(params.angle, params.omega, mean_fx, mean_fy);
}
void print_ehrenfest_parameters(t_particle particle[NMAXCIRCLES], double pleft, double pright)
{
char message[100];
int i, j, nleft1 = 0, nleft2 = 0, nright1 = 0, nright2 = 0;
double density, hue, rgb[3], logratio, y, shiftx = 0.3, xmidplus, xmidminus;
static double xleftbox, xlefttext, xmidbox, xmidtext, xrightbox, xrighttext, pressures[500][2], meanpressure[2];
static int first = 1, i_pressure, naverage = 500, n_pressure;
if (first)
{
xleftbox = -0.85;
xlefttext = xleftbox - 0.5;
xrightbox = 1.0;
xrighttext = xrightbox - 0.45;
// xmid = 0.5*(XMIN + XMAX) - 0.1;
// xmidtext = xmid - 0.24;
meanpressure[0] = 0.0;
meanpressure[1] = 0.0;
for (i=0; i<naverage; i++)
{
pressures[i][0] = 0.0;
pressures[i][1] = 0.0;
}
i_pressure = 0;
n_pressure = 0;
first = 0;
}
if (BOUNDARY_COND == BC_EHRENFEST)
{
xmidplus = 1.0 - EHRENFEST_RADIUS;
xmidminus = -1.0 + EHRENFEST_RADIUS;
}
else
{
xmidplus = xwall;
xmidminus = xwall;
}
/* table of pressures */
pressures[i_pressure][0] = pleft;
pressures[i_pressure][1] = pright;
i_pressure++;
if (i_pressure == naverage) i_pressure = 0;
if (n_pressure < naverage - 1) n_pressure++;
for (i=0; i<n_pressure; i++)
for (j=0; j<2; j++)
meanpressure[j] += pressures[i][j];
for (j=0; j<2; j++) meanpressure[j] = meanpressure[j]/(double)n_pressure;
for (i = 0; i < ncircles; i++) if (particle[i].active)
{
if (particle[i].xc < xmidminus)
{
if (particle[i].type == 0) nleft1++;
else nleft2++;
}
else if (particle[i].xc > xmidplus)
{
if (particle[i].type == 0) nright1++;
else nright2++;
}
}
y = YMIN + 0.05;
erase_area_hsl(xleftbox - shiftx, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb(HUE_TYPE0, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "%i particles", nleft1);
write_text(xlefttext + 0.28 - shiftx, y, message);
erase_area_hsl(xleftbox + shiftx, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb(HUE_TYPE1, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "%i particles", nleft2);
write_text(xlefttext + 0.28 + shiftx, y, message);
erase_area_hsl(xrightbox - shiftx, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb(HUE_TYPE0, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "%i particles", nright1);
write_text(xrighttext + 0.28 - shiftx, y, message);
erase_area_hsl(xrightbox + shiftx, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb(HUE_TYPE1, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "%i particles", nright2);
write_text(xrighttext + 0.28 + shiftx, y, message);
y = YMAX - 0.1;
erase_area_hsl(xleftbox - 0.1, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb_turbo(HUE_TYPE1, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "Pressure %.2f", 0.001*meanpressure[0]/(double)ncircles);
write_text(xlefttext + 0.25, y, message);
erase_area_hsl(xrightbox - 0.1, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb_turbo(HUE_TYPE0, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "Pressure %.2f", 0.001*meanpressure[1]/(double)ncircles);
write_text(xrighttext + 0.2, y, message);
}
void count_particle_number(t_particle *particle, int *particle_numbers, int time)
{
int type, i, n;
n = time*(RD_TYPES+1);
for (type = 0; type < RD_TYPES+1; type++)
particle_numbers[n + type] = 0;
if (COUNT_PARTNER_TYPE)
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].type == 0)
particle_numbers[n + particle[i].npartners + 1]++;
else if ((particle[i].type == 2)&&(particle[i].npartners == 0))
particle_numbers[n + 1]++;
}
// for (type = 0; type < RD_TYPES+1; type++) printf("type = %i, %i particles\n", type, particle_numbers[n + type]);
}
else for (i=0; i<ncircles; i++)
if (particle[i].active)
particle_numbers[n + particle[i].type]++;
}
void print_particle_number(int npart)
{
char message[100];
double y = YMAX - 0.1;
static double xleftbox, xlefttext;
static int first = 1;
if (first)
{
xleftbox = XMIN + 0.5;
xlefttext = xleftbox - 0.5;
first = 0;
}
erase_area_hsl(xleftbox, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
if (npart > 1) sprintf(message, "%i particles", npart);
else sprintf(message, "%i particle", npart);
write_text(xlefttext + 0.28, y, message);
}
void print_particle_types_number(t_particle *particle, int ntypes)
{
int i, ntype[10];
char message[100];
double y = YMAX - 0.1, rgb[3];
static double xleftbox, xlefttext;
static int first = 1;
if (first)
{
xleftbox = XMAX - 0.5;
xlefttext = xleftbox - 0.45;
first = 0;
}
for (i=0; i<10; i++) ntype[i] = 0;
for (i=0; i<ncircles; i++)
if (particle[i].active) ntype[particle[i].type]++;
for (i=1; i<ntypes+1; i++)
{
erase_area_hsl(xleftbox, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
set_type_color(i, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "%i particles", ntype[i]);
write_text(xlefttext + 0.28, y, message);
y -= 0.12;
}
}
void print_entropy(double entropy[2])
{
char message[100];
double rgb[3];
static double xleftbox, xlefttext, xrightbox, xrighttext, y = YMAX - 0.1, ymin = YMIN + 0.05;
static int first = 1;
if (first)
{
xleftbox = XMIN + 0.4;
xlefttext = xleftbox - 0.55;
xrightbox = XMAX - 0.39;
xrighttext = xrightbox - 0.55;
first = 0;
}
if (POSITION_Y_DEPENDENCE)
{
erase_area_hsl(xrightbox, ymin + 0.025, 0.35, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb_turbo(HUE_TYPE1, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "Entropy = %.4f", entropy[1]);
write_text(xrighttext + 0.28, ymin, message);
}
else
{
erase_area_hsl(xleftbox, y + 0.025, 0.35, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb_turbo(HUE_TYPE1, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "Entropy = %.4f", entropy[1]);
write_text(xlefttext + 0.28, y, message);
}
erase_area_hsl(xrightbox, y + 0.025, 0.35, 0.05, 0.0, 0.9, 0.0);
hsl_to_rgb_turbo(HUE_TYPE0, 0.9, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
sprintf(message, "Entropy = %.4f", entropy[0]);
write_text(xrighttext + 0.28, y, message);
}
void print_segments_speeds(double vx[2], double vy[2])
{
char message[100];
double rgb[3], y;
static double xleftbox, xlefttext, xrightbox, xrighttext, ymin = YMIN + 0.05, scale;
static int first = 1;
if (first)
{
scale = (XMAX - XMIN)/4.0;
xleftbox = XMIN + 0.3*scale;
xlefttext = xleftbox - 0.22*scale;
xrightbox = XMAX - 0.39*scale;
xrighttext = xrightbox - 0.3*scale;
first = 0;
}
y = YMAX - 0.1*scale;
erase_area_hsl(xrightbox, y + 0.025*scale, 0.25*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Vx = %.2f", vx[0]);
write_text(xrighttext + 0.1, y, message);
y -= 0.1*scale;
erase_area_hsl(xrightbox, y + 0.025*scale, 0.25*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Vy = %.2f", vy[0]);
write_text(xrighttext + 0.1, y, message);
if (TWO_OBSTACLES)
{
y = YMAX - 0.1*scale;
erase_area_hsl(xleftbox, y + 0.025*scale, 0.2*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Vx = %.2f", vx[1]);
write_text(xlefttext + 0.1, y, message);
y -= 0.1*scale;
erase_area_hsl(xleftbox, y + 0.025*scale, 0.2*scale, 0.05*scale, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Vy = %.2f", vy[1]);
write_text(xlefttext + 0.1, y, message);
}
}
void print_segment_group_speeds(t_group_segments *segment_group)
{
char message[100];
double rgb[3], y, av_vx = 0.0, av_vy = 0.0, av_omega = 0.0, xbox, xtext;
int i, group, groupshift;
static double xleftbox, xlefttext, xrightbox, xrighttext, ymin = YMIN + 0.05, scale;
static double vx[100*NMAXGROUPS], vy[100*NMAXGROUPS], omega[100*NMAXGROUPS], inv_t_window, speed_ratio;
static int first = 1, position[NMAXGROUPS], t_window = 50;
if (first)
{
scale = (XMAX - XMIN)/4.0;
xleftbox = XMIN + 0.3*scale;
xlefttext = xleftbox - 0.22*scale;
xrightbox = XMAX - 0.39*scale;
xrighttext = xrightbox - 0.3*scale;
speed_ratio = (double)(25*NVID)*DT_PARTICLE;
inv_t_window = 1.0/(double)t_window;
for (group=1; group<NMAXGROUPS; group++) position[group] = 0;
first = 0;
}
/* compute time averages */
for (group = 1; group < ngroups; group++)
{
groupshift = (group-1)*100;
vx[groupshift + position[group]] = segment_group[group].vx*speed_ratio;
vy[groupshift + position[group]] = segment_group[group].vy*speed_ratio;
omega[groupshift + position[group]] = segment_group[group].omega*speed_ratio;
position[group]++;
if (position[group] >=t_window) position[group] = 0;
for (i=0; i<t_window; i++)
{
av_vx += vx[groupshift + i];
av_vy += vy[groupshift + i];
av_omega += omega[groupshift + i];
}
av_vx *= inv_t_window;
av_vy *= inv_t_window;
av_omega *= inv_t_window;
if (ngroups > 2)
{
xbox = xleftbox + (xrightbox - xleftbox)*(group-1)/(ngroups-2);
xtext = xlefttext + (xrighttext - xlefttext)*(group-1)/(ngroups-2);
}
else
{
xbox = xrightbox - 0.2;
xtext = xrighttext - 0.2;
}
// printf("xbox = %.2f, xtext = %.2f, av_vy = %.2f\n", xbox, xtext, av_vy);
y = YMAX - 0.1*scale;
erase_area_hsl(xbox, y + 0.025*scale, 0.25*scale, 0.05*scale, 0.0, 0.9, 0.0);
set_segment_group_color(group, 1.0, rgb);
sprintf(message, "Vx = %.4f", av_vx);
write_text(xtext + 0.1, y, message);
y -= 0.1*scale;
erase_area_hsl(xbox, y + 0.025*scale, 0.25*scale, 0.05*scale, 0.0, 0.9, 0.0);
set_segment_group_color(group, 1.0, rgb);
sprintf(message, "Vy = %.4f", av_vy);
write_text(xtext + 0.1, y, message);
y -= 0.1*scale;
erase_area_hsl(xbox, y + 0.025*scale, 0.3*scale, 0.05*scale, 0.0, 0.9, 0.0);
set_segment_group_color(group, 1.0, rgb);
sprintf(message, "V = %.4f", module2(av_vx, av_vy));
write_text(xtext + 0.1, y, message);
y -= 0.1*scale;
erase_area_hsl(xbox, y + 0.025*scale, 0.3*scale, 0.05*scale, 0.0, 0.9, 0.0);
set_segment_group_color(group, 1.0, rgb);
sprintf(message, "Omega = %.4f", av_omega);
write_text(xtext + 0.1, y, message);
}
}
void print_particles_speeds(t_particle particle[NMAXCIRCLES])
{
char message[100];
double y = YMAX - 0.1, vx = 0.0, vy = 0.0;
int i, nactive = 0;
static double xleftbox, xlefttext, xrightbox, xrighttext, ymin = YMIN + 0.05;
static int first = 1;
if (first)
{
xrightbox = XMAX - 0.39;
xrighttext = xrightbox - 0.55;
first = 0;
}
for (i=0; i<NMAXCIRCLES; i++) if (particle[i].active)
{
nactive++;
vx += particle[i].vx;
vy += particle[i].vy;
}
vx = vx/(double)nactive;
vy = vy/(double)nactive;
erase_area_hsl(xrightbox, y + 0.025, 0.35, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Average vx = %.4f", vx);
write_text(xrighttext + 0.1, y, message);
y -= 0.1;
erase_area_hsl(xrightbox, y + 0.025, 0.35, 0.05, 0.0, 0.9, 0.0);
glColor3f(1.0, 1.0, 1.0);
sprintf(message, "Average vy = %.4f", vy);
write_text(xrighttext + 0.1, y, message);
}
double compute_boundary_force(int j, t_particle particle[NMAXCIRCLES], t_obstacle obstacle[NMAXOBSTACLES], t_segment segment[NMAXSEGMENTS], double xleft, double xright, double *pleft, double *pright, double pressure[N_PRESSURES], int wall, double krepel, int reset)
{
int i, s, k, corner, group, ngroups;
double xmin, xmax, ymin, ymax, padding, r, rp, r2, cphi, sphi, f, fperp = 0.0, x, y, xtube, distance, dx, dy, width, ybin, angle, x1, y1, x2, h, ytop, norm, dleft, dplus, dminus, tmp_pleft = 0.0, tmp_pright = 0.0, proj, pscal, pvect, pvmin, charge, d2, speed, pangle, distance1, sangle;
double group_pressure[NMAXGROUPS];
int segments_per_group[NMAXGROUPS];
short int inactivate_group[NMAXGROUPS], inactivate_segment;
static int first = 1;
static double seqangle;
if (first)
{
seqangle = PI/(double)NPOLY;
first = 0;
}
if ((OSCILLATE_OBSTACLES)&&(reset)) for (i=0; i<nobstacles; i++)
{
obstacle[i].fx = 0.0;
obstacle[i].fy = 0.0;
}
/* compute force from fixed circular obstacles */
if (ADD_FIXED_OBSTACLES) for (i=0; i<nobstacles; i++)
{
x = particle[j].xc - obstacle[i].xc;
y = particle[j].yc - obstacle[i].yc;
distance = module2(x, y);
if (distance < 1.0e-7) distance = 1.0e-7;
cphi = x/distance;
sphi = y/distance;
r2 = obstacle[i].radius + particle[j].radius;
if (distance < r2)
{
f = KSPRING_OBSTACLE*(r2 - distance);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
if (OSCILLATE_OBSTACLES)
{
obstacle[i].fx -= f*cphi;
obstacle[i].fy -= f*sphi;
}
if (ROTATE_OBSTACLES)
{
speed = particle[j].vx*sphi - particle[j].vy*cphi;
f = KSPRING_BELT*(obstacle[i].omega*obstacle[i].radius/particle[j].mass_inv - speed);
particle[j].fx += f*sphi;
particle[j].fy -= f*cphi;
}
}
if (CHARGE_OBSTACLES)
{
charge = obstacle[i].charge*particle[k].charge;
d2 = distance*distance;
f = KCOULOMB_OBSTACLE*charge/(1.0e-12 + d2);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
}
}
/* compute force from fixed linear obstacles */
particle[j].close_to_boundary = 0;
if (ADD_FIXED_SEGMENTS) for (i=0; i<nsegments; i++) if (segment[i].active)
{
x = particle[j].xc;
y = particle[j].yc;
proj = (segment[i].ny*(x - segment[i].x1) - segment[i].nx*(y - segment[i].y1))/segment[i].length;
if ((proj > 0.0)&&(proj < 1.0))
{
// distance = segment[i].nx*x + segment[i].ny*y - segment[i].c;
distance = segment[i].nx*x + segment[i].ny*y - segment[i].c;
/* case of interacting polygons */
// if (POLYGON_INTERACTION)
// {
// // distance = segment[i].nx*x + segment[i].ny*y - segment[i].c;
// pangle = DPI/(double)NPOLY;
// dx = 0.0;
// dy = 0.0;
// for (corner=0; corner<NPOLY; corner++)
// {
// angle = particle[j].angle + pangle*(double)corner;
// x1 = x + particle[j].radius*cos(angle);
// y1 = y + particle[j].radius*sin(angle);
// distance1 = segment[i].nx*x1 + segment[i].ny*y1 - segment[i].c;
// if (distance1 < distance)
// {
// x = x1;
// y = y1;
// distance = distance1;
// dx = x1 - particle[j].xc;
// dy = y1 - particle[j].yc;
// }
// }
// // printf("(dx, dy) = (%.3lg, %.3lg)\n", dx, dy);
// }
r = SEGMENT_FORCE_EQR*particle[j].radius;
if (vabs(distance) < r)
{
particle[j].close_to_boundary = 1;
f = KSPRING_OBSTACLE*(r - distance);
particle[j].fx += f*segment[i].nx;
particle[j].fy += f*segment[i].ny;
if ((MOVE_BOUNDARY)||(MOVE_SEGMENT_GROUPS)||(PRINT_SEGMENTS_FORCE))
{
segment[i].fx -= f*segment[i].nx;
segment[i].fy -= f*segment[i].ny;
segment[i].torque -= (x - segment[i].xc)*f*segment[i].ny - (y - segment[i].yc)*f*segment[i].nx;
}
if (SHOW_SEGMENTS_PRESSURE)
{
segment[i].pressure = f/segment[i].length;
segment[i].avrg_pressure *= (1.0 - P_AVRG_FACTOR);
segment[i].avrg_pressure += P_AVRG_FACTOR*segment[i].pressure;
// printf("Segment %i: pressure = %.3lg\n", i, segment[i].avrg_pressure);
}
if (ADD_CONVEYOR_FORCE)
{
speed = particle[j].vx*segment[i].ny - particle[j].vy*segment[i].nx;
f = KSPRING_BELT*(segment[i].conveyor_speed/particle[j].mass_inv - speed);
particle[j].fx += f*segment[i].ny;
particle[j].fy -= f*segment[i].nx;
// printf("Belt force (%.5lg, %.5lg)\n", f*segment[i].ny, -f*segment[i].nx);
}
if ((POLYGON_INTERACTION)&&(segment[i].align_torque))
{
sangle = segment[i].nangle;
particle[j].torque -= KTORQUE_BOUNDARY*sin(particle[j].spin_freq*(particle[j].angle - sangle) - seqangle);
// particle[j].torque -= KTORQUE_BOUNDARY*(dx*f*segment[i].ny - dy*f*segment[i].nx);
// printf("Torque on particle %i = %.3lg\n", j, particle[j].torque);
}
}
if ((VICSEK_INT)&&(vabs(distance) < SEGMENT_FORCE_EQR*r))
{
pvmin = 2.0;
pvect = cos(particle[j].angle)*segment[i].ny - sin(particle[j].angle)*segment[i].nx;
if ((pvect > 0.0)&&(pvect < pvmin)) pvect = pvmin;
else if ((pvect < 0.0)&&(pvect > -pvmin)) pvect = -pvmin;
// particle[j].torque += KTORQUE_BOUNDARY*pvect;
particle[j].torque += KTORQUE_BOUNDARY*pvect*(SEGMENT_FORCE_EQR*r - vabs(distance));
}
}
/* compute force from concave corners */
if (segment[i].concave)
{
distance = module2(x - segment[i].x1, y - segment[i].y1);
angle = argument(x - segment[i].x1, y - segment[i].y1);
if (angle < segment[i].angle1) angle += DPI;
/* added 24/9/22 */
else if (angle > segment[i].angle2) angle -= DPI;
r = SEGMENT_FORCE_EQR*particle[j].radius;
if ((distance < r)&&(angle > segment[i].angle1)&&(angle < segment[i].angle2))
{
f = KSPRING_OBSTACLE*(r - distance);
particle[j].fx += f*cos(angle);
particle[j].fy += f*sin(angle);
if ((MOVE_BOUNDARY)||(MOVE_SEGMENT_GROUPS))
{
segment[i].fx -= f*cos(angle);
segment[i].fy -= f*sin(angle);
segment[i].torque -= (x - segment[i].xc)*f*sin(angle) - (y - segment[i].yc)*f*cos(angle);
}
}
}
}
if (INACTIVATE_SEGMENTS_UNDER_PRESSURE)
{
ngroups = 0;
for (group=0; group<NMAXGROUPS; group++)
{
segments_per_group[group] = 0;
group_pressure[group] = 0.0;
}
for (i=0; i<nsegments; i++)
{
group = segment[i].group;
segments_per_group[group]++;
group_pressure[group] += segment[i].avrg_pressure;
if (group > ngroups) ngroups = group;
}
for (group=1; group<ngroups; group++) if (segments_per_group[group] > 0)
{
// group_pressure[group] *= 1.0/(double)segments_per_group[group];
if (group_pressure[group] > SEGMENT_P_INACTIVATE*(double)segments_per_group[group]) inactivate_group[group] = 1;
else inactivate_group[group] = 0;
}
for (i=0; i<nsegments; i++)
{
group = segment[i].group;
if ((group > 0)&&(inactivate_group[group])) segment[i].active = 0;
}
}
switch(BOUNDARY_COND){
case (BC_SCREEN):
{
/* add harmonic force outside screen */
if (particle[j].xc > XMAX) particle[j].fx -= KSPRING_BOUNDARY*(particle[j].xc - XMAX);
else if (particle[j].xc < XMIN) particle[j].fx += KSPRING_BOUNDARY*(XMIN - particle[j].xc);
if (particle[j].yc > YMAX) particle[j].fy -= KSPRING_BOUNDARY*(particle[j].yc - YMAX);
else if (particle[j].yc < YMIN) particle[j].fy += KSPRING_BOUNDARY*(YMIN - particle[j].yc);
// if (particle[j].xc > BCXMAX) particle[j].fx -= KSPRING_BOUNDARY*(particle[j].xc - BCXMAX);
// else if (particle[j].xc < BCXMIN) particle[j].fx += KSPRING_BOUNDARY*(BCXMIN - particle[j].xc);
// if (particle[j].yc > BCYMAX) particle[j].fy -= KSPRING_BOUNDARY*(particle[j].yc - BCYMAX);
// else if (particle[j].yc < BCYMIN) particle[j].fy += KSPRING_BOUNDARY*(BCYMIN - particle[j].yc);
return(fperp);
}
case (BC_RECTANGLE):
{
/* add harmonic force outside rectangular box */
padding = particle[j].radius + 0.01;
xmin = xleft + padding;
xmax = xright - padding;
ymin = BCYMIN + padding;
ymax = BCYMAX - padding;
if (particle[j].xc > xmax)
{
fperp = KSPRING_BOUNDARY*(particle[j].xc - xmax);
particle[j].fx -= fperp;
}
else if (particle[j].xc < xmin)
{
fperp = KSPRING_BOUNDARY*(xmin - particle[j].xc);
particle[j].fx += fperp;
}
if (particle[j].yc > ymax)
{
fperp = KSPRING_BOUNDARY*(particle[j].yc - ymax);
particle[j].fy -= fperp;
}
else if (particle[j].yc < ymin)
{
fperp = KSPRING_BOUNDARY*(ymin - particle[j].yc);
particle[j].fy += fperp;
}
// if (particle[j].xc > xmax) particle[j].fx -= KSPRING_BOUNDARY*(particle[j].xc - xmax);
// else if (particle[j].xc < xmin) particle[j].fx += KSPRING_BOUNDARY*(xmin - particle[j].xc);
// if (particle[j].yc > ymax) particle[j].fy -= KSPRING_BOUNDARY*(particle[j].yc - ymax);
// else if (particle[j].yc < ymin) particle[j].fy += KSPRING_BOUNDARY*(ymin - particle[j].yc);
return(fperp);
}
case (BC_CIRCLE):
{
/* add harmonic force outside screen */
if (particle[j].xc > BCXMAX) particle[j].fx -= KSPRING_BOUNDARY*(particle[j].xc - BCXMAX);
else if (particle[j].xc < BCXMIN) particle[j].fx += KSPRING_BOUNDARY*(BCXMIN - particle[j].xc);
if (particle[j].yc > BCYMAX) particle[j].fy -= KSPRING_BOUNDARY*(particle[j].yc - BCYMAX);
else if (particle[j].yc < BCYMIN) particle[j].fy += KSPRING_BOUNDARY*(BCYMIN - particle[j].yc);
/* add harmonic force from obstacle */
for (i=-2; i<2; i++)
{
x = xleft + (double)i*(OBSXMAX - OBSXMIN);
if (vabs(particle[j].xc - x) < 1.1*OBSTACLE_RADIUS)
{
r = module2(particle[j].xc - x, particle[j].yc);
if (r < 1.0e-5) r = 1.0e-05;
cphi = (particle[j].xc - x)/r;
sphi = particle[j].yc/r;
padding = MU + 0.03;
if (r < OBSTACLE_RADIUS + padding)
{
f = KSPRING_OBSTACLE*(OBSTACLE_RADIUS + padding - r);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
}
}
}
return(fperp);
}
case (BC_PERIODIC_CIRCLE):
{
x = xleft;
if (vabs(particle[j].xc - x) < 1.1*OBSTACLE_RADIUS)
{
r = module2(particle[j].xc - x, particle[j].yc);
if (r < 1.0e-5) r = 1.0e-05;
cphi = (particle[j].xc - x)/r;
sphi = particle[j].yc/r;
padding = MU + 0.03;
if (r < OBSTACLE_RADIUS + padding)
{
f = KSPRING_OBSTACLE*(OBSTACLE_RADIUS + padding - r);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
}
}
return(f);
}
case (BC_PERIODIC_TRIANGLE):
{
x = xleft;
x1 = x + OBSTACLE_RADIUS;
x2 = x - OBSTACLE_RADIUS;
h = 2.0*OBSTACLE_RADIUS*tanh(APOLY*PID);
padding = MU + 0.03;
// ytop = 0.5*h*(1.0 - (particle[j].xc - x)/OBSTACLE_RADIUS);
if ((vabs(particle[j].xc - x) < OBSTACLE_RADIUS + padding)&&(vabs(particle[j].yc < h + padding)))
{
/* signed distances to side of triangle */
dleft = x2 - particle[j].xc;
norm = module2(h, 2.0*OBSTACLE_RADIUS);
if (particle[j].yc >= 0.0)
{
dplus = (h*particle[j].xc + 2.0*OBSTACLE_RADIUS*particle[j].yc - h*(x+OBSTACLE_RADIUS))/norm;
if ((dleft < padding)&&(dleft > dplus)) /* left side is closer */
{
f = KSPRING_OBSTACLE*(padding - dleft);
particle[j].fx -= f;
}
else if (dplus < padding) /* top side is closer */
{
f = KSPRING_OBSTACLE*(padding - dplus);
particle[j].fx += f*h/norm;
particle[j].fy += 2.0*f*OBSTACLE_RADIUS/norm;
}
}
else
{
dminus = (h*particle[j].xc - 2.0*OBSTACLE_RADIUS*particle[j].yc - h*(x+OBSTACLE_RADIUS))/norm;
if ((dleft < padding)&&(dleft > dminus)) /* left side is closer */
{
f = KSPRING_OBSTACLE*(padding - dleft);
particle[j].fx -= f;
}
else if (dminus < padding) /* bottom side is closer */
{
f = KSPRING_OBSTACLE*(padding - dminus);
particle[j].fx += f*h/norm;
particle[j].fy += -2.0*f*OBSTACLE_RADIUS/norm;
}
}
/* force from tip of triangle */
r = module2(particle[j].xc - x1, particle[j].yc);
if (r < 0.5*padding)
{
if (r < 1.0e-5) r = 1.0e-05;
cphi = (particle[j].xc - x1)/r;
sphi = particle[j].yc/r;
f = KSPRING_OBSTACLE*(0.5*padding - r);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
}
}
return(f);
}
case (BC_PERIODIC_FUNNEL):
{
x = xleft;
padding = MU + 0.02;
if (vabs(particle[j].yc) > FUNNEL_WIDTH - padding) for (i=-1; i<2; i+=2)
{
r = module2(particle[j].xc - x, particle[j].yc - (double)i*(FUNNEL_WIDTH + OBSTACLE_RADIUS));
if (r < 1.0e-5) r = 1.0e-05;
cphi = (particle[j].xc - x)/r;
sphi = (particle[j].yc - (double)i*(FUNNEL_WIDTH + OBSTACLE_RADIUS))/r;
if (r < OBSTACLE_RADIUS + padding)
{
f = KSPRING_OBSTACLE*(OBSTACLE_RADIUS + padding - r);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
if (RECORD_PRESSURES)
{
angle = argument(cphi, sphi);
if (angle < 0.0) angle += DPI;
k = (int)((double)N_PRESSURES*angle/DPI);
if (k >= N_PRESSURES) k = N_PRESSURES - 1;
pressure[k] += f;
}
}
}
return(f);
}
case (BC_RECTANGLE_LID):
{
r = particle[j].radius;
if (particle[j].yc < BCYMIN + r) particle[j].fy += KSPRING_BOUNDARY*(BCYMIN + r - particle[j].yc);
else if (particle[j].yc > ylid - r)
{
fperp = KSPRING_BOUNDARY*(particle[j].yc - ylid + r);
particle[j].fy -= fperp;
}
if (particle[j].yc < BCYMAX + r)
{
if (particle[j].xc > BCXMAX - r) particle[j].fx -= KSPRING_BOUNDARY*(particle[j].xc - BCXMAX + r);
else if (particle[j].xc < BCXMIN + r) particle[j].fx += KSPRING_BOUNDARY*(BCXMIN + r - particle[j].xc);
}
return(fperp);
}
case (BC_RECTANGLE_WALL):
{
padding = particle[j].radius + 0.01;
xmin = BCXMIN + padding;
xmax = BCXMAX - padding;
ymin = BCYMIN + padding;
ymax = BCYMAX - padding;
if (particle[j].xc > xmax)
{
fperp = KSPRING_BOUNDARY*(particle[j].xc - xmax);
particle[j].fx -= fperp;
tmp_pright += fperp;
}
else if (particle[j].xc < xmin)
{
fperp = KSPRING_BOUNDARY*(xmin - particle[j].xc);
particle[j].fx += fperp;
tmp_pleft += fperp;
}
if (particle[j].yc > ymax)
{
fperp = KSPRING_BOUNDARY*(particle[j].yc - ymax);
particle[j].fy -= fperp;
if (particle[j].xc > xwall) tmp_pright += fperp;
else tmp_pleft += fperp;
}
else if (particle[j].yc < ymin)
{
fperp = KSPRING_BOUNDARY*(ymin - particle[j].yc);
particle[j].fy += fperp;
if (particle[j].xc > xwall) tmp_pright += fperp;
else tmp_pleft += fperp;
}
if (wall)
{
*pleft += tmp_pleft/(2.0*(BCYMAX - BCYMIN) + 2.0*(xwall - BCXMIN));
*pright += tmp_pright/(2.0*(BCYMAX - BCYMIN) + 2.0*(BCXMAX - xwall));
}
else
{
*pleft += tmp_pleft/(2.0*(BCYMAX - BCYMIN + BCXMAX - BCXMIN));
*pright += tmp_pright/(2.0*(BCYMAX - BCYMIN + BCXMAX - BCXMIN));
}
if ((wall)&&(vabs(particle[j].xc - xwall) < 0.5*WALL_WIDTH + padding))
{
if (particle[j].xc > xwall)
{
fperp = -KSPRING_BOUNDARY*(xwall + 0.5*WALL_WIDTH + padding - particle[j].xc);
particle[j].fx -= fperp;
*pright -= fperp/(BCYMAX - BCYMIN);
}
else
{
fperp = KSPRING_BOUNDARY*(particle[j].xc - xwall + 0.5*WALL_WIDTH + padding);
particle[j].fx -= fperp;
*pleft += fperp/(BCYMAX - BCYMIN);
}
return(fperp);
}
return(0.0);
}
case (BC_EHRENFEST):
{
rp = particle[j].radius;
xtube = 1.0 - sqrt(EHRENFEST_RADIUS*EHRENFEST_RADIUS - EHRENFEST_WIDTH*EHRENFEST_WIDTH);
distance = 0.0;
/* middle tube */
if (vabs(particle[j].xc) <= xtube)
{
if (particle[j].yc > EHRENFEST_WIDTH - rp)
{
distance = particle[j].yc - EHRENFEST_WIDTH;
particle[j].fy -= KSPRING_BOUNDARY*(distance + rp);
}
else if (particle[j].yc < -EHRENFEST_WIDTH + rp)
{
distance = - EHRENFEST_WIDTH - particle[j].yc;
particle[j].fy += KSPRING_BOUNDARY*(distance + rp);
}
}
/* right container */
else if (particle[j].xc > 0.0)
{
r = module2(particle[j].xc - 1.0, particle[j].yc);
if ((r > EHRENFEST_RADIUS - rp)&&((particle[j].xc > 1.0)||(vabs(particle[j].yc) > EHRENFEST_WIDTH)))
{
cphi = (particle[j].xc - 1.0)/r;
sphi = particle[j].yc/r;
f = KSPRING_BOUNDARY*(EHRENFEST_RADIUS - r - rp);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
*pright -= f;
}
}
/* left container */
else
{
r = module2(particle[j].xc + 1.0, particle[j].yc);
if ((r > EHRENFEST_RADIUS - rp)&&((particle[j].xc < -1.0)||(vabs(particle[j].yc) > EHRENFEST_WIDTH)))
{
cphi = (particle[j].xc + 1.0)/r;
sphi = particle[j].yc/r;
f = KSPRING_BOUNDARY*(EHRENFEST_RADIUS - r - rp);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
*pleft -= f;
}
}
/* add force from "corners" */
if ((vabs(particle[j].xc) - xtube < rp)&&(vabs(particle[j].yc) - EHRENFEST_WIDTH < rp))
{
for (i=-1; i<=1; i+=2)
for (k=-1; k<=1; k+=2)
{
distance = module2(particle[j].xc - (double)i*xtube, particle[j].yc - (double)k*EHRENFEST_WIDTH);
if (distance < rp)
{
cphi = (particle[j].xc - (double)i*xtube)/distance;
sphi = (particle[j].yc - (double)k*EHRENFEST_WIDTH)/distance;
f = KSPRING_BOUNDARY*(rp - distance);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
}
}
}
return(fperp);
}
case (BC_SCREEN_BINS):
{
/* add harmonic force outside screen */
if (particle[j].xc > XMAX) particle[j].fx -= KSPRING_BOUNDARY*(particle[j].xc - XMAX);
else if (particle[j].xc < XMIN) particle[j].fx += KSPRING_BOUNDARY*(XMIN - particle[j].xc);
if (particle[j].yc > YMAX + 10.0*MU) particle[j].fy -= KSPRING_BOUNDARY*(particle[j].yc - YMAX - 10.0*MU);
else if (particle[j].yc < YMIN) particle[j].fy += KSPRING_BOUNDARY*(YMIN - particle[j].yc);
/* force from the bins */
dy = (YMAX - YMIN)/((double)NGRIDX + 3);
dx = dy/cos(PI/6.0);
rp = particle[j].radius;
width = rp + 0.05*dx;
ybin = 2.75*dy;
if (particle[j].yc < YMIN + ybin) for (i=-1; i<=NGRIDX; i++)
{
x = ((double)i - 0.5*(double)NGRIDX + 0.5)*dx;
distance = vabs(particle[j].xc - x);
if (distance < width)
{
if (particle[j].xc > x) particle[j].fx += KSPRING_BOUNDARY*(width - distance);
else particle[j].fx -= KSPRING_BOUNDARY*(width - distance);
}
}
else if (particle[j].yc < YMIN + ybin + particle[j].radius) for (i=-1; i<=NGRIDX; i++)
{
x = ((double)i - 0.5*(double)NGRIDX + 0.5)*dx;
distance = module2(particle[j].xc - x, particle[j].yc - YMIN - ybin);
if (distance < rp)
{
if (distance < 1.0e-8) distance = 1.0e-8;
cphi = (particle[j].xc - x)/distance;
sphi = (particle[j].yc - YMIN - ybin)/distance;
f = KSPRING_BOUNDARY*(rp - distance);
particle[j].fx += f*cphi;
particle[j].fy += f*sphi;
}
}
return(fperp);
}
case (BC_ABSORBING):
{
/* add harmonic force outside screen */
padding = 0.1;
x = particle[j].xc;
y = particle[j].yc;
if ((x > BCXMAX + padding)||(x < BCXMIN - padding)||(y > BCYMAX + padding)||(y < BCYMIN - padding))
{
particle[j].active = 0;
particle[j].vx = 0.0;
particle[j].vy = 0.0;
particle[j].xc = BCXMAX + 2.0*padding;
particle[j].yc = BCYMAX + 2.0*padding;
}
return(fperp);
}
case (BC_REFLECT_ABS):
{
/* add harmonic force outside screen */
padding = 0.1;
x = particle[j].xc;
y = particle[j].yc;
if ((x > BCXMAX + padding)||(y > BCYMAX + padding)||(y < BCYMIN - padding))
{
particle[j].active = 0;
particle[j].vx = 0.0;
particle[j].vy = 0.0;
particle[j].xc = BCXMAX + 2.0*padding;
particle[j].yc = BCYMAX + 2.0*padding;
}
else if (particle[j].yc < BCYMIN) particle[j].fy += KSPRING_BOUNDARY*(BCYMIN - particle[j].yc);
return(fperp);
}
case (BC_REFLECT_ABS_BOTTOM):
{
/* add harmonic force outside screen */
padding = MU;
x = particle[j].xc;
y = particle[j].yc;
if (y < BCYMIN)
{
particle[j].active = 0;
particle[j].vx = 0.0;
particle[j].vy = 0.0;
particle[j].xc = BCXMAX + 2.0*padding;
particle[j].yc = BCYMIN - 2.0*padding;
}
else
{
if (y > BCYMAX + padding) particle[j].fy -= KSPRING_BOUNDARY*(y - BCYMAX - padding);
if (x > BCXMAX - padding) particle[j].fx -= KSPRING_BOUNDARY*(x - BCXMAX + padding);
else if (x < BCXMIN + padding) particle[j].fx += KSPRING_BOUNDARY*(BCXMIN - x + padding);
}
return(0.0);
}
case (BC_REFLECT_ABS_RIGHT):
{
/* add harmonic force outside screen */
padding = MU;
x = particle[j].xc;
y = particle[j].yc;
if (x > BCXMAX + padding)
{
particle[j].active = 0;
particle[j].vx = 0.0;
particle[j].vy = 0.0;
particle[j].xc = BCXMAX + 2.0*padding;
particle[j].yc = BCYMIN - 2.0*padding;
}
else
{
if (y > BCYMAX - padding) particle[j].fy -= KSPRING_BOUNDARY*(y - BCYMAX + padding);
else if (y < BCYMIN + padding) particle[j].fy += KSPRING_BOUNDARY*(BCYMIN - y + padding);
if (x < BCXMIN + padding) particle[j].fx += KSPRING_BOUNDARY*(BCXMIN - x + padding);
}
return(0.0);
}
}
}
void dissociate_particles_findp(int i, int j, t_particle particle[NMAXCIRCLES])
/* dissociate partnered particles i and j */
{
int np, nq, r, p, q;
np = particle[i].npartners;
nq = particle[j].npartners;
/* find which partner of j is particle i */
p = 0;
while ((particle[i].partner[p] != j)&&(p < np)) p++;
/* remove partner p from partner list */
for (q=p; q<np-1; q++)
particle[i].partner[q] = particle[i].partner[q+1];
particle[i].npartners--;
/* find which partner of j is particle i */
q = 0;
while ((particle[j].partner[q] != i)&&(q < nq)) q++;
/* remove partner q from partner list */
for (r=q; r<nq-1; r++)
particle[j].partner[r] = particle[j].partner[r+1];
particle[j].npartners--;
}
void dissociate_particles(int i, int j, int p, t_particle particle[NMAXCIRCLES])
/* dissociate partnered particles i and j */
/* assuming j is pth partner of i */
{
int np, nq, r, q;
np = particle[i].npartners;
nq = particle[j].npartners;
/* remove partner p from partner list */
for (q=p; q<np-1; q++)
particle[i].partner[q] = particle[i].partner[q+1];
particle[i].npartners--;
/* find which partner of j is particle i */
q = 0;
while ((particle[j].partner[q] != i)&&(q < nq)) q++;
/* remove partner q from partner list */
for (r=q; r<nq-1; r++)
particle[j].partner[r] = particle[j].partner[r+1];
particle[j].npartners--;
}
void dissociate_molecule(int i, int j, t_particle particle[NMAXCIRCLES])
/* dissociate all particles in molecule containing particles i and j */
{
int np, nq, p, q, n;
np = particle[i].npartners;
nq = particle[j].npartners;
particle[i].npartners = 0;
particle[j].npartners = 0;
particle[i].vx = 0.0;
particle[i].vy = 0.0;
particle[i].thermostat = 1;
particle[j].vx = 0.0;
particle[j].vy = 0.0;
particle[j].thermostat = 1;
for (p=0; p<np; p++)
{
n = particle[i].partner[p];
particle[n].npartners = 0;
particle[n].vx = 0.0;
particle[n].vy = 0.0;
particle[n].thermostat = 1;
}
for (q=0; q<nq; q++)
{
n = particle[j].partner[q];
particle[n].npartners = 0;
particle[n].vx = 0.0;
particle[n].vy = 0.0;
particle[n].thermostat = 1;
}
}
void compute_partner_force(int j, int n, double eq_distance, double eq_angle, double f[2], double *torque, t_particle particle[NMAXCIRCLES])
/* compute force of partner particle n on particle j */
{
double dx, dy, r, ca, sa, force, sangle, torque2;
double x1, x2, y1, y2, rmax, alpha, cosa, rj, rn, phi;
int p, q;
dx = particle[n].xc - particle[j].xc;
dy = particle[n].yc - particle[j].yc;
if (dx > 0.5*(BCXMAX - BCXMIN)) dx -= (BCXMAX-BCXMIN);
else if (dx < -0.5*(BCXMAX - BCXMIN)) dx += (BCXMAX-BCXMIN);
if (dy > 0.5*(BCYMAX - BCYMIN)) dy -= (BCYMAX-BCYMIN);
else if (dy < -0.5*(BCYMAX - BCYMIN)) dy += (BCYMAX-BCYMIN);
if ((PAIR_PARTICLES)&&(PAIRING_TYPE == POLY_SEG_POLYGON))
{
rmax = 1.0*particle[j].radius;
for (p=-1; p<=1; p+=2)
for (q=-1; q<=1; q+=2)
{
x1 = particle[n].xc + (double)p*particle[n].radius*cos(particle[n].angle);
y1 = particle[n].yc + (double)p*particle[n].radius*sin(particle[n].angle);
x2 = particle[j].xc + (double)q*particle[n].radius*cos(particle[j].angle);
y2 = particle[j].yc + (double)q*particle[n].radius*sin(particle[j].angle);
r = module2(x2-x1, y2-y1);
if ((r>0.0)&&(r < rmax))
{
// f[0] += KSPRING_PAIRS*(x1-x2)/r;
// f[1] += KSPRING_PAIRS*(y1-y2)/r;
f[0] = 1.0e8*(x1-x2)/r;
f[1] = 1.0e8*(y1-y2)/r;
}
}
}
// else
{
r = module2(dx, dy);
if (r < 1.0e-10) r = 1.0e-10;
if (r > 2.0*eq_distance) r = 2.0*eq_distance;
// if (r > 1.5*eq_distance) r = 1.5*eq_distance;
ca = dx/r;
sa = dy/r;
/* TODO: adjust max distance */
if (r < 1.5*eq_distance) force = KSPRING_PAIRS*(r - eq_distance);
else
{
// printf("Dissociating partners %i and %i because max distance exceeded\n", j, n);
force = 0.0;
// dissociate_particles_findp(j, n, particle);
// dissociate_molecule(j, n, particle);
}
f[0] = force*ca;
f[1] = force*sa;
/* TEST */
f[0] -= particle[j].vx*DAMPING;
f[1] -= particle[j].vy*DAMPING;
}
if (ROTATION)
{
*torque = 0.0;
if ((REACTION_DIFFUSION)&&(RD_REACTION == CHEM_POLYGON_AGGREGATION))
{
// r = module2(dx, dy);
// if (r < 2.0*MU)
// {
// if (NPOLY%2 == 0)
// sangle = sin((double)NPOLY*(particle[n].angle - particle[j].angle));
// else sangle = sin((double)NPOLY*(particle[n].angle - particle[j].angle) - PI);
// sangle = sin((double)NPOLY*(particle[n].angle - particle[j].angle - eq_angle));
// *torque += KTORQUE_PAIRS*sangle;
/* stabilize relative angle */
q = particle[j].partner[0];
if ((particle[j].npartners == 1)&&(particle[q].npartners == 1))
{
sangle = sin((double)NPOLY*(particle[n].angle - particle[j].angle - eq_angle));
*torque += KTORQUE_PAIRS*sangle;
phi = argument(dx, dy);
sangle = sin((double)NPOLY*(phi - particle[j].angle) - PI);
*torque += KTORQUE_PAIRS*sangle;
}
/* TEST: add some damping to rotation */
*torque -= particle[j].omega*DAMPING_ROT;
// sangle = sin((double)NPOLY*(phi - particle[j].angle) - PI);
// *torque += KTORQUE_PAIRS*sangle;
// }
}
else if ((PAIR_PARTICLES)&&(PAIRING_TYPE == POLY_SEG_POLYGON))
{
/* TODO */
sangle = sin(particle[n].angle - particle[j].angle - eq_angle);
// if (sangle > 0.0) *torque = KTORQUE_PAIRS*(1.0 + sangle);
// else *torque = KTORQUE_PAIRS*(-1.0 + sangle);
*torque = KTORQUE_PAIRS*sangle;
// *torque = 0.0;
// if (COMPUTE_PAIR_TORQUE)
// {
// torque2 = KTORQUE_PAIR_ANGLE*sangle;
// f[0] -= torque2*sa;
// f[1] += torque2*ca;
// }
}
else
{
sangle = sin(SPIN_INTER_FREQUENCY*(particle[n].angle - particle[j].angle));
if (sangle > 0.0) *torque = KTORQUE_PAIRS*(1.0 + sangle);
else *torque = KTORQUE_PAIRS*(-1.0 + sangle);
if (COMPUTE_PAIR_TORQUE)
{
torque2 = KTORQUE_PAIR_ANGLE*sangle;
f[0] -= torque2*sa;
f[1] += torque2*ca;
}
}
}
}
void compute_particle_force(int j, double krepel, t_particle particle[NMAXCIRCLES], t_hashgrid hashgrid[HASHX*HASHY])
/* compute force from other particles on particle j */
{
int i0, j0, m0, k, l, m, q, close, n, test, different_cluster = 1;
double fx = 0.0, fy = 0.0, force[2], torque = 0.0, torque_ij, x, y;
particle[j].neighb = 0;
if (REACTION_DIFFUSION) particle[j].diff_neighb = 0;
for (k=0; k<particle[j].hash_nneighb; k++)
{
n = particle[j].hashneighbour[k];
if (CLUSTER_PARTICLES) /* test whether n is not in the same cluster */
different_cluster = (particle[n].cluster != particle[j].cluster);
else different_cluster = 1;
if ((particle[n].active)&&(different_cluster))
{
if (PAIR_FORCE)
{
/* test whether n is not a partner particle */
test = 1;
for (l=0; l<particle[n].npartners; l++)
if (particle[n].partner[l] == j) test = 0;
if (test)
{
close = compute_repelling_force(j, k, force, &torque_ij, particle, krepel);
fx += force[0];
fy += force[1];
torque += torque_ij;
if (close)
{
particle[j].neighb++;
if (REACTION_DIFFUSION&&(particle[j].type != particle[particle[j].hashneighbour[k]].type))
particle[j].diff_neighb++;
}
}
}
else
{
close = compute_repelling_force(j, k, force, &torque_ij, particle, krepel);
fx += force[0];
fy += force[1];
torque += torque_ij;
if (close)
{
particle[j].neighb++;
if (REACTION_DIFFUSION&&(particle[j].type != particle[particle[j].hashneighbour[k]].type))
particle[j].diff_neighb++;
}
}
}
}
if (PAIR_FORCE) for (l=0; l<particle[j].npartners; l++)
{
n = particle[j].partner[l];
if ((!CLUSTER_PARTICLES)||(particle[n].cluster != particle[j].cluster))
{
compute_partner_force(j, n, particle[j].partner_eqd[l], particle[j].partner_eqa[l], force, &torque_ij, particle);
fx += force[0];
fy += force[1];
torque += torque_ij;
}
}
particle[j].fx += fx;
particle[j].fy += fy;
particle[j].torque += torque;
}
int reorder_particles(t_particle particle[NMAXCIRCLES], double py[NMAXCIRCLES], double pangle[NMAXCIRCLES])
/* keep only active particles, beta */
{
int i, k, new = 0, nactive = 0;
for (i=0; i<ncircles; i++) if (particle[i].active)
{
particle[new].xc = particle[i].xc;
particle[new].yc = particle[i].yc;
particle[new].radius = particle[i].radius;
particle[new].angle = particle[i].angle;
particle[new].active = particle[i].active;
particle[new].energy = particle[i].energy;
particle[new].vx = particle[i].vx;
particle[new].vy = particle[i].vy;
particle[new].omega = particle[i].omega;
particle[new].mass_inv = particle[i].mass_inv;
particle[new].inertia_moment_inv = particle[i].inertia_moment_inv;
particle[new].fx = particle[i].fx;
particle[new].fy = particle[i].fy;
particle[new].thermostat = particle[i].thermostat;
particle[new].hashcell = particle[i].hashcell;
particle[new].neighb = particle[i].neighb;
particle[new].diff_neighb = particle[i].diff_neighb;
particle[new].hash_nneighb = particle[i].hash_nneighb;
particle[new].type = particle[i].type;
particle[new].interaction = particle[i].interaction;
particle[new].eq_dist = particle[i].eq_dist;
particle[new].spin_range = particle[i].spin_range;
particle[new].spin_freq = particle[i].spin_freq;
py[new] = py[i];
pangle[new] = pangle[i];
for (k=0; k<9*HASHMAX; k++)
{
particle[new].hashneighbour[k] = particle[i].hashneighbour[k];
particle[new].deltax[k] = particle[i].deltax[k];
particle[new].deltay[k] = particle[i].deltay[k];
}
new++;
nactive++;
}
return(nactive);
}
int twotype_config(int i, t_particle particle[NMAXCIRCLES])
/* assign different particle types */
{
switch (TWOTYPE_CONFIG) {
case (TTC_RANDOM): return((double)rand()/RAND_MAX > TYPE_PROPORTION);
case (TTC_CHESSBOARD):
{
switch (CIRCLE_PATTERN) {
case (C_SQUARE): return((i%NGRIDY + i/NGRIDY)%2 == 0);
case (C_HEX): return(i%2 == 0);
default: return(i%2 == 0);
}
}
case (TTC_COANDA):
{
if (vabs(particle[i].yc) < LAMBDA) return(1);
if (vabs(particle[i].yc) < LAMBDA + MU) return(-1);
return(0);
}
default: return(0);
}
}
int initialize_configuration(t_particle particle[NMAXCIRCLES], t_hashgrid hashgrid[HASHX*HASHY], t_obstacle obstacle[NMAXOBSTACLES], double px[NMAXCIRCLES], double py[NMAXCIRCLES], double pangle[NMAXCIRCLES], int tracer_n[N_TRACER_PARTICLES],
t_segment segment[NMAXSEGMENTS], t_molecule molecule[NMAXCIRCLES])
/* initialize all particles, obstacles, and the hashgrid */
{
int i, j, k, n, type, nactive = 0, hashcell;
double x, y, h, xx, yy, rnd, angle;
for (i=0; i < ncircles; i++)
{
/* set particle type */
particle[i].type = 0;
// if ((TWO_TYPES)&&((double)rand()/RAND_MAX > TYPE_PROPORTION))
// if ((TWO_TYPES)&&(twotype_config(i, particle)))
if (TWO_TYPES)
{
type = twotype_config(i, particle);
if (type == 1)
{
particle[i].type = 1;
// particle[i].type = 2;
particle[i].radius = MU_B;
}
else if (type == -1) particle[i].active = 0;
}
if ((INTERACTION == I_VICSEK_SHARK)&&(i==1))
{
particle[i].type = 2;
particle[i].radius = MU_B;
}
particle[i].neighb = 0;
particle[i].diff_neighb = 0;
particle[i].thermostat = 1;
particle[i].close_to_boundary = 0;
particle[i].emean = 0.0;
particle[i].damping = 1.0;
particle[i].dirmean = 0.0;
particle[i].paired = 0;
particle[i].collision = 0;
// particle[i].added = 0;
// particle[i].coulomb = 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*(RANDOM_RADIUS_MIN + RANDOM_RADIUS_RANGE*((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;
if ((RANDOM_RADIUS)&&(ADAPT_MASS_TO_RADIUS > 0.0))
particle[i].mass_inv *= 1.0/(1.0 + pow(particle[i].radius/MU, ADAPT_MASS_TO_RADIUS));
if ((RANDOM_RADIUS)&&(ADAPT_DAMPING_TO_RADIUS > 0.0))
particle[i].damping = 1.0 + ADAPT_DAMPING_FACTOR*pow(particle[i].radius/MU, ADAPT_DAMPING_TO_RADIUS);
particle[i].inertia_moment_inv = 1.0/PARTICLE_INERTIA_MOMENT;
particle[i].charge = CHARGE;
}
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].charge = CHARGE_B;
}
switch (V_INITIAL_TYPE)
{
case (VI_RANDOM):
{
particle[i].vx = V_INITIAL*gaussian();
particle[i].vy = V_INITIAL*gaussian();
break;
}
case (VI_COANDA):
{
if (vabs(particle[i].yc) < LAMBDA) particle[i].vx = V_INITIAL;
else particle[i].vx = 0.0;
particle[i].vy = 0.0;
break;
}
}
if ((INTERACTION == I_VICSEK_SHARK)&&(i==1))
{
particle[i].vx *= 1000.0;
particle[i].vy *= 1000.0;
}
particle[i].energy = (particle[i].vx*particle[i].vx + particle[i].vy*particle[i].vy)*particle[i].mass_inv;
particle[i].emean = particle[i].energy;
particle[i].dirmean = 0.0;
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;
if ((PLOT == P_INITIAL_POS)||(PLOT_B == P_INITIAL_POS))
{
switch (INITIAL_POS_TYPE) {
case (IP_X):
{
particle[i].color_hue = 360.0*(particle[i].xc - INITXMIN)/(INITXMAX - INITXMIN);
break;
}
case (IP_Y):
{
particle[i].color_hue = 360.0*(particle[i].yc - INITYMIN)/(INITYMAX - INITYMIN);
break;
}
}
}
if ((PLOT == P_NUMBER)||(PLOT_B == P_NUMBER))
particle[i].color_hue = 360.0*(double)(i%N_PARTICLE_COLORS)/(double)N_PARTICLE_COLORS;
// if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
{
if (PAIR_PARTICLES) particle[i].cluster = rand()%(ncircles*CLUSTER_COLOR_FACTOR);
else particle[i].cluster = rand()%ncircles;
}
}
/* 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].emean = 0.0;
particle[i].damping = 1.0;
particle[i].dirmean = 0.0;
particle[i].mass_inv = 1.0/PARTICLE_MASS;
particle[i].inertia_moment_inv = 1.0/PARTICLE_INERTIA_MOMENT;
particle[i].charge = 0.0;
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;
particle[i].close_to_boundary = 0;
particle[i].coulomb = 1;
particle[i].added = 1;
particle[i].reactive = 1;
particle[i].paired = 0;
particle[i].collision = 0;
}
/* 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);
}
/* add larger copies of particles (for Ehrenfest model)*/
if (EHRENFEST_COPY)
{
for (i=0; i < ncircles; i++)
{
n = ncircles + i;
particle[n].xc = -particle[i].xc;
particle[n].yc = particle[i].yc;
particle[n].vx = -0.5*particle[i].vx;
particle[n].vy = 0.5*particle[i].vy;
px[n] = -0.5*px[i];
py[n] = 0.5*py[i];
particle[n].energy = particle[i].energy;
particle[n].radius = 2.0*particle[i].radius;
particle[n].type = 2;
particle[n].mass_inv = 1.25*particle[i].mass_inv;
particle[n].thermostat = 1;
particle[n].interaction = particle[i].interaction;
particle[n].eq_dist = 0.45*particle[i].eq_dist;
if ((double)rand()/RAND_MAX > 0.6) particle[n].active = 1;
}
ncircles *= 2;
}
/* change type of tracer particle */
if (TRACER_PARTICLE) for (j=0; j<n_tracers; j++)
{
i = 0;
if (j%2==0) xx = 1.0;
else xx = -1.0;
if (j/2 == 0) yy = -0.5;
else yy = 0.5;
// if (j%2 == 1) yy = -yy;
// while ((!particle[i].active)||(module2(particle[i].xc, particle[i].yc) > 0.5)) i++;
while ((!particle[i].active)||(module2(particle[i].xc + xx, particle[i].yc - yy) > 0.4)) i++;
tracer_n[j] = i;
particle[i].type = 2 + j;
particle[i].radius *= 1.5;
particle[i].mass_inv *= 1.0/TRACER_PARTICLE_MASS;
particle[i].vx *= 0.1;
particle[i].vy *= 0.1;
particle[i].thermostat = 0;
px[i] *= 0.1;
py[i] *= 0.1;
n_tracers++;
}
/* position-dependent particle type */
if (POSITION_DEPENDENT_TYPE) for (i=0; i<ncircles; i++)
if (((!POSITION_Y_DEPENDENCE)&&((particle[i].xc - POSITION_DEP_X)*POSITION_DEP_SIGN < 0.0))||((POSITION_Y_DEPENDENCE)&&(particle[i].yc*POSITION_DEP_SIGN < 0.0)))
{
particle[i].type = 2;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
particle[i].radius = MU_B;
}
/* add copies in case of particle pairing */
if (PAIR_PARTICLES) init_particle_pairs(particle, molecule);
/* inactivate particles in obstacle */
printf("Inactivating particles inside obstacles\n");
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;
}
else if (BOUNDARY_COND == BC_PERIODIC_FUNNEL)
{
for (i=0; i< ncircles; i++)
for (k=-1; k<2; k+=2)
if ((module2(particle[i].xc, particle[i].yc - (double)k*(FUNNEL_WIDTH + OBSTACLE_RADIUS)) < OBSTACLE_RADIUS + 2.0*MU))
{
printf("Inactivating particle at (%.3lg, %.3lg)\n", particle[i].xc, particle[i].yc);
particle[i].active = 0;
}
}
else if (BOUNDARY_COND == BC_PERIODIC_TRIANGLE)
{
h = 2.0*OBSTACLE_RADIUS*tan(APOLY*PID);
for (i=0; i< ncircles; i++)
if ((vabs(particle[i].xc) < 1.1*OBSTACLE_RADIUS + 2.0*MU)
&&(2.0*OBSTACLE_RADIUS*vabs(particle[i].yc) < h*(OBSTACLE_RADIUS + 2.0*MU - particle[i].xc)))
{
printf("Inactivating particle at (%.3lg, %.3lg)\n", particle[i].xc, particle[i].yc);
particle[i].active = 0;
}
}
else if (BOUNDARY_COND == BC_EHRENFEST)
{
for (i=0; i< ncircles; i++)
if (module2(vabs(particle[i].xc) -1.0, particle[i].yc) > EHRENFEST_RADIUS)
particle[i].active = 0;
}
else if (BOUNDARY_COND == BC_RECTANGLE_WALL)
{
for (i=0; i< ncircles; i++)
if (vabs(particle[i].xc - xwall) < WALL_WIDTH)
particle[i].active = 0;
}
else if (BOUNDARY_COND == BC_GENUS_TWO)
{
for (i=0; i< ncircles; i++)
if ((particle[i].xc > 0.0)&&(particle[i].yc > 0.0))
particle[i].active = 0;
}
if (ADD_FIXED_OBSTACLES)
{
for (i=0; i< ncircles; i++) for (j=0; j < nobstacles; j++)
{
if (module2(particle[i].xc - obstacle[j].xc, particle[i].yc - obstacle[j].yc) < OBSTACLE_RADIUS + particle[i].radius)
particle[i].active = 0;
hashcell = hash_cell(obstacle[j].xc, obstacle[j].yc);
hashgrid[hashcell].charge = OBSTACLE_CHARGE;
}
}
/* case of segment obstacles */
if (ADD_FIXED_SEGMENTS) for (i=0; i< ncircles; i++)
if (!in_segment_region(particle[i].xc, particle[i].yc, segment))
particle[i].active = 0;
/* case of reaction-diffusion equation/chemical reactions */
if (REACTION_DIFFUSION) for (i=0; i< ncircles; i++)
{
switch (RD_INITIAL_COND) {
case (IC_UNIFORM):
{
particle[i].type = 1;
break;
}
case (IC_UNIFORM2):
{
particle[i].type = 2;
particle[i].radius = MU_B;
particle[i].omega = OMEGA_INITIAL*gaussian();
break;
}
case (IC_RANDOM_UNIF):
{
particle[i].type = 1 + (int)(RD_TYPES*(double)rand()/(double)RAND_MAX);
break;
}
case (IC_RANDOM_TWO):
{
if ((double)rand()/(double)RAND_MAX < TYPE_PROPORTION) particle[i].type = 1;
else
{
particle[i].type = 2;
particle[i].radius = MU_B;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
}
break;
}
case (IC_CIRCLE):
{
if (module2(particle[i].xc,particle[i].yc) < LAMBDA) particle[i].type = 1;
else
{
particle[i].type = 2;
particle[i].radius = MU_B;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
}
break;
}
case (IC_CATALYSIS):
{
if ((particle[i].xc > 0.0)||((double)rand()/(double)RAND_MAX < TYPE_PROPORTION))
{
particle[i].type = 1;
particle[i].radius = MU;
particle[i].mass_inv = 1.0/PARTICLE_MASS;
}
else
{
particle[i].type = 2;
particle[i].radius = MU_B;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
}
break;
}
case (IC_LAYERS):
{
if (particle[i].yc > 0.0)
{
particle[i].type = 1;
particle[i].radius = MU;
particle[i].eq_dist = EQUILIBRIUM_DIST;
particle[i].mass_inv = 1.0/PARTICLE_MASS;
}
else if (particle[i].yc < -LAMBDA)
{
particle[i].type = 2;
particle[i].radius = MU_B;
particle[i].eq_dist = EQUILIBRIUM_DIST_B;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
}
else particle[i].active = 0;
break;
}
case (IC_BZ):
{
rnd = (double)rand()/(double)RAND_MAX;
if (rnd < 60.0/180.0)
{
particle[i].type = 4;
particle[i].radius = MU;
particle[i].eq_dist = EQUILIBRIUM_DIST;
particle[i].mass_inv = 1.0/(PARTICLE_MASS + 3.0*PARTICLE_MASS_B);
}
else if (rnd < 100.0/180.0)
{
particle[i].type = 5;
particle[i].radius = MU_B;
particle[i].eq_dist = EQUILIBRIUM_DIST;
particle[i].mass_inv = 1.0/(3.5*PARTICLE_MASS);
}
else
{
particle[i].type = 6;
particle[i].radius = 1.5*MU_B;
particle[i].eq_dist = EQUILIBRIUM_DIST;
particle[i].mass_inv = 0.2/(PARTICLE_MASS);
}
break;
}
case (IC_SIGNX):
{
if (particle[i].xc < 0.0) particle[i].type = 1;
else
{
particle[i].type = 2;
particle[i].radius = MU_B;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
}
break;
}
case (IC_TWOROCKETS):
{
if (vabs(particle[i].xc) < SEGMENTS_X0) particle[i].type = 1;
else
{
particle[i].type = 2;
particle[i].radius = MU_B;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
}
break;
}
case (IC_TWOROCKETS_TWOFUELS):
{
if (vabs(particle[i].xc) < SEGMENTS_X0) particle[i].type = 1;
else
{
if (particle[i].xc < 0) particle[i].type = 2;
else particle[i].type = 3;
particle[i].radius = MU_B;
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
}
break;
}
case (IC_DNA_POLYMERASE):
{
/* do nothing? */
break;
}
case (IC_DNA_POLYMERASE_REC):
{
/* do nothing? */
break;
}
default:
{
/* do nothing */
}
}
}
/* keep only active particles */
// ncircles = reorder_particles(particle, py, pangle);
/* count number of active particles */
for (i=0; i< ncircles; i++) nactive += particle[i].active;
printf("%i active particles\n", nactive);
// for (i=0; i<ncircles; i++) printf("particle %i of type %i\n", i, particle[i].type);
return(nactive);
}
int add_particles(t_particle particle[NMAXCIRCLES], double px[NMAXCIRCLES], double py[NMAXCIRCLES], int nadd_particle, int type, t_molecule molecule[NMAXCIRCLES],
int tracer_n[N_TRACER_PARTICLES])
/* add several particles to the system */
{
static int i = 0;
double x, y, r, phi;
printf("Adding a particle\n\n");
switch (ADD_REGION) {
case (ADD_RECTANGLE) :
{
x = ADDXMIN + (ADDXMAX - ADDXMIN)*(double)rand()/(double)RAND_MAX;
y = ADDYMIN + (ADDYMAX - ADDYMIN)*(double)rand()/(double)RAND_MAX;
break;
}
case (ADD_RING) :
{
r = ADDRMIN + (ADDRMAX - ADDRMIN)*(double)rand()/(double)RAND_MAX;
phi = DPI*(double)rand()/(double)RAND_MAX;
x = r*cos(phi);
y = r*sin(phi);
break;
}
}
printf("Added a particle at (%.5lg, %.5lg)\n\n\n", x, y);
fprintf(lj_log, "Added a particle at (%.5lg, %.5lg)\n\n\n", x, y);
add_particle(x, y, V_INITIAL, 0.05*V_INITIAL*(double)rand()/RAND_MAX, PARTICLE_MASS, type, particle);
printf("Particle number %i\n", ncircles-1);
// add_particle(x, y, V_INITIAL, 0.25*V_INITIAL*(double)rand()/RAND_MAX, PARTICLE_MASS, type, particle);
// add_particle(x, y, V_INITIAL*(double)rand()/RAND_MAX, 0.0, PARTICLE_MASS, type, particle);
// if (y > 0.0)
// add_particle(x, y, 5.0*V_INITIAL*(double)rand()/RAND_MAX, -10.0*V_INITIAL, PARTICLE_MASS, type, particle);
// else
// add_particle(x, y, 5.0*V_INITIAL*(double)rand()/RAND_MAX, 10.0*V_INITIAL, PARTICLE_MASS, type, particle);
particle[ncircles - 1].eq_dist = EQUILIBRIUM_DIST;
particle[ncircles - 1].thermostat = 1;
px[ncircles - 1] = particle[ncircles - 1].vx;
py[ncircles - 1] = particle[ncircles - 1].vy;
if ((TRACER_PARTICLE)&&(n_tracers < N_TRACER_PARTICLES))
{
tracer_n[n_tracers] = ncircles - 1;
n_tracers++;
printf("%i tracer particles\n", n_tracers);
}
// init_molecule_data(particle, molecule);
return (ncircles);
// 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;
// add_particle(MU*(2.0*rand()/RAND_MAX - 1.0), YMAX + 2.0*MU, 0.0, 0.0, PARTICLE_MASS, 0, particle);
// add_particle(XMIN - 0.5*MU, 0.0, 50.0 + 5.0*(double)i, 0.0, 2.0*PARTICLE_MASS, 0, particle);
// x = INITXMIN + (INITXMAX - INITXMIN)*(double)rand()/(double)RAND_MAX;
// y = INITYMIN + (INITYMAX - INITYMIN)*(double)rand()/(double)RAND_MAX;
// x = BCXMIN + (BCXMAX - BCXMIN)*(double)rand()/(double)RAND_MAX;
// y = YMAX + 0.5*(BCYMAX - YMAX)*(double)rand()/(double)RAND_MAX;
}
void center_momentum(double p[NMAXCIRCLES])
{
int i;
double ptot = 0.0, pmean;
for (i=0; i<ncircles; i++) ptot += p[i];
pmean = ptot/(double)ncircles;
for (i=0; i<ncircles; i++) p[i] -= pmean;
}
int floor_momentum(double p[NMAXCIRCLES])
{
int i, floor = 0;
double ptot = 0.0, pmean;
for (i=0; i<ncircles; i++)
{
if (p[i] > PMAX)
{
p[i] = PMAX;
floor = 1;
}
else if (p[i] < -PMAX)
{
p[i] = -PMAX;
floor = 1;
}
}
if (floor) printf("Flooring momentum\n");
return (floor);
}
int partial_thermostat_coupling(t_particle particle[NMAXCIRCLES], double xmin, t_segment segment[NMAXSEGMENTS], t_lj_parameters params)
/* only couple particles satisfying condition PARTIAL_THERMO_REGION to thermostat */
{
int condition, i, nthermo = 0;
double x, y, height, a, b;
static double maxheight;
static int first = 1;
if (first)
{
maxheight = YMIN + PARTIAL_THERMO_HEIGHT*(YMAX - YMIN);
first = 0;
}
if (PARTIAL_THERMO_REGION == TH_LAYER_TYPE2)
{
height = YMIN;
for (i=0; i<ncircles; i++)
{
y = particle[i].yc;
if ((particle[i].active)&&(particle[i].type == 2)&&(y > height)&&(y <= maxheight))
height = y;
}
if (height > maxheight) height = maxheight;
printf("Thermostat region y > %.3lg, max height = %.3lg\n", height, maxheight);
}
for (i=0; i<ncircles; i++)
{
switch (PARTIAL_THERMO_REGION) {
case (TH_VERTICAL):
{
condition = (particle[i].xc > xmin);
break;
}
case (TH_INSEGMENT):
{
condition = (in_segment_region(particle[i].xc, particle[i].yc, segment));
// condition = (in_segment_region(particle[i].xc - xsegments[0], particle[i].yc - ysegments[0]));
// // condition = (in_segment_region(particle[i].xc - xsegments[0], particle[i].yc - ysegments[0]));
// if (TWO_OBSTACLES)
// condition = condition||(in_segment_region(particle[i].xc - xsegments[1], particle[i].yc - ysegments[1]));
break;
}
case (TH_INBOX):
{
x = particle[i].xc;
y = particle[i].yc;
condition = ((y < YMIN + PARTIAL_THERMO_HEIGHT*(YMAX - YMIN))&&(vabs(x) < PARTIAL_THERMO_WIDTH*XMAX));
break;
}
case (TH_LAYER):
{
y = particle[i].yc;
condition = (y > PARTIAL_THERMO_HEIGHT);
break;
}
case (TH_LAYER_TYPE2):
{
y = particle[i].yc;
condition = (y > height);
break;
}
case (TH_RING):
{
x = particle[i].xc;
y = particle[i].yc;
condition = x*x + y*y > PARTIAL_THERMO_WIDTH*PARTIAL_THERMO_WIDTH;
break;
}
case (TH_RING_EXPAND):
{
x = particle[i].xc;
y = particle[i].yc;
condition = x*x + y*y > params.thermo_radius*params.thermo_radius;
break;
}
case (TH_INIT):
{
x = particle[i].xc;
y = particle[i].yc;
condition = ((x > INITXMIN)&&(x < INITXMAX)&&(y > INITYMIN)&&(y < INITYMAX));
break;
}
case (TH_THERMO):
{
x = particle[i].xc;
y = particle[i].yc;
condition = ((x > THERMOXMIN)&&(x < THERMOXMAX)&&(y > THERMOYMIN)&&(y < THERMOYMAX));
break;
}
case (TH_CONE):
{
x = particle[i].xc;
y = particle[i].yc;
a = (LAMBDA + 0.2)/(LAMBDA - 0.05);
b = LAMBDA*(1.0 - a);
condition = ((y < LAMBDA)&&(y > 0.0)&&(y > a*vabs(x) + b));
break;
}
default: condition = 1;
}
if (condition)
{
particle[i].thermostat = 1;
nthermo++;
}
else particle[i].thermostat = 0;
}
return(nthermo);
}
int partial_efield(double x, double y)
/* */
{
switch (EFIELD_REGION) {
case (EF_CONST): return(1);
case (EF_SQUARE): return(vabs(x) < YMAX);
case (EF_LEFT): return(x < YMIN);
}
}
int partial_bfield(double x, double y)
/* */
{
switch (BFIELD_REGION) {
case (BF_CONST): return(1);
case (BF_SQUARE): return(vabs(x) < YMAX);
}
}
double compute_mean_energy(t_particle particle[NMAXCIRCLES])
{
int i, nactive = 0;
double total_energy = 0.0;
for (i=0; i<ncircles; i++) if (particle[i].active)
{
total_energy += particle[i].energy;
nactive++;
}
return(total_energy/(double)nactive);
}
void compute_inverse_masses(double inv_masses[RD_TYPES+1])
/* compute inverse masses of molecules in chemical reactions */
{
int type;
double mass;
/* default values that apply in most cases */
inv_masses[1] = 1.0/PARTICLE_MASS;
inv_masses[2] = 1.0/PARTICLE_MASS_B;
switch (RD_REACTION) {
case (CHEM_AAB):
{
inv_masses[2] = 0.5/PARTICLE_MASS;
break;
}
case (CHEM_ABC):
{
inv_masses[3] = 1.0/(PARTICLE_MASS + PARTICLE_MASS_B);
break;
}
case (CHEM_A2BC):
{
inv_masses[3] = 1.0/(2.0*PARTICLE_MASS + PARTICLE_MASS_B);
break;
}
case (CHEM_CATALYSIS):
{
inv_masses[3] = 0.5/PARTICLE_MASS;
break;
}
case (CHEM_BAA):
{
inv_masses[1] = 2.0/PARTICLE_MASS_B;
break;
}
case (CHEM_AABAA):
{
inv_masses[2] = 0.5/PARTICLE_MASS;
break;
}
case (CHEM_POLYMER):
{
for (type = 3; type < RD_TYPES+1; type++)
{
mass = PARTICLE_MASS_B + (double)(type-2)*PARTICLE_MASS;
inv_masses[type] = 1.0/(PARTICLE_MASS + mass);
}
break;
}
case (CHEM_POLYMER_DISS):
{
for (type = 3; type < RD_TYPES+1; type++)
{
mass = PARTICLE_MASS_B + (double)(type-2)*PARTICLE_MASS;
inv_masses[type] = 1.0/(PARTICLE_MASS + mass);
}
break;
}
case (CHEM_POLYMER_STEP):
{
for (type = 2; type < RD_TYPES+1; type++)
inv_masses[type] = 1.0/((double)type*PARTICLE_MASS);
break;
}
case (CHEM_CATALYTIC_A2D):
{
inv_masses[3] = 1.0/(1.0/PARTICLE_MASS + 1.0/PARTICLE_MASS_B);
inv_masses[4] = 0.5/PARTICLE_MASS;
break;
}
case (CHEM_ABCAB):
{
inv_masses[3] = 1.0/(PARTICLE_MASS + PARTICLE_MASS_B);
break;
}
case (CHEM_ABCDABC):
{
inv_masses[3] = 1.0/(PARTICLE_MASS + PARTICLE_MASS_B);
inv_masses[4] = 1.0/(2.0*PARTICLE_MASS + PARTICLE_MASS_B);
break;
}
case (CHEM_BZ):
{
for (type = 2; type <= 4; type++)
{
mass = PARTICLE_MASS + (double)(type-2)*PARTICLE_MASS_B;
inv_masses[type] = 1.0/(mass);
}
// inv_masses[5] = 1.0/(3.5*PARTICLE_MASS);
// inv_masses[6] = 0.2/PARTICLE_MASS;
inv_masses[5] = 0.5/(PARTICLE_MASS);
inv_masses[6] = 0.5/PARTICLE_MASS;
inv_masses[7] = 0.5*inv_masses[3];
inv_masses[8] = 0.5*inv_masses[5];
break;
}
case (CHEM_BRUSSELATOR):
{
inv_masses[3] = inv_masses[1];
inv_masses[4] = 1.0/(PARTICLE_MASS + PARTICLE_MASS_B);
inv_masses[5] = inv_masses[1];
inv_masses[6] = inv_masses[1];
break;
}
case (CHEM_ABDACBE):
{
inv_masses[3] = inv_masses[1];
inv_masses[4] = 1.0/(PARTICLE_MASS + PARTICLE_MASS_B);
inv_masses[5] = inv_masses[1];
break;
}
}
}
void compute_radii(double radii[RD_TYPES+1])
/* compute radii of molecules in chemical reactions */
{
int type;
/* default values that apply in most cases */
radii[1] = MU;
radii[2] = MU_B;
switch (RD_REACTION) {
case (CHEM_ABC):
{
radii[3] = 1.5*MU;
break;
}
case (CHEM_A2BC):
{
radii[3] = 1.5*MU;
break;
}
case (CHEM_CATALYSIS):
{
radii[3] = MU_B;
break;
}
case (CHEM_POLYMER):
{
for (type = 3; type < RD_TYPES+1; type++) radii[type] = 1.5*MU;
break;
}
case (CHEM_POLYMER_DISS):
{
for (type = 3; type < RD_TYPES+1; type++) radii[type] = 1.5*MU;
break;
}
case (CHEM_POLYMER_STEP):
{
for (type = 2; type < RD_TYPES+1; type++) radii[type] = 1.2*MU;
break;
}
case (CHEM_CATALYTIC_A2D):
{
radii[3] = MU_B;
radii[4] = MU*1.2;
break;
}
case (CHEM_ABCAB):
{
radii[3] = 1.5*MU;
break;
}
case (CHEM_ABCDABC):
{
radii[3] = 1.5*MU;
radii[4] = 1.5*MU;
break;
}
case (CHEM_BZ):
{
// for (type = 2; type <= 8; type++) radii[type] = MU;
for (type = 2; type <= 4; type++) radii[type] = MU;
radii[5] = MU_B;
radii[6] = 1.5*MU_B;
radii[7] = 1.2*radii[3];
radii[8] = 1.2*radii[5];
break;
}
case (CHEM_BRUSSELATOR):
{
radii[3] = MU;
radii[4] = 1.2*MU_B;
radii[5] = MU;
radii[6] = MU;
break;
}
case (CHEM_ABDACBE):
{
radii[3] = MU;
radii[4] = 1.2*MU_B;
radii[5] = MU;
break;
}
}
}
void adapt_speed_exothermic(t_particle *particle, double delta_ekin)
/* change the particle speed in case of exothermic reaction */
{
double old_energy, e_ratio;
old_energy = (particle->vx*(particle->vx) + particle->vy*(particle->vy))*(particle->mass_inv);
if (old_energy + delta_ekin > 0.0) e_ratio = sqrt((old_energy + delta_ekin)/old_energy);
else e_ratio = 0.0;
particle->vx *= e_ratio;
particle->vy *= e_ratio;
}
int chem_merge_AAB(int i, int newtype, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1])
/* merging particle i with a particle of same type into a particle of type newtype */
/* particular case of chem_merge */
{
int j, k, type1;
short int search = 1;
double distance, rx, ry, mr, newmass_inv;
type1 = particle[i].type;
newmass_inv = inv_masses[newtype];
mr = inv_masses[newtype]/inv_masses[type1];
for (j=0; j<particle[i].hash_nneighb; j++)
{
search = 1;
k = particle[i].hashneighbour[j];
if ((search)&&(particle[k].active)&&(particle[k].type == type1))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
{
particle[i].type = newtype;
particle[i].radius = radii[newtype];
rx = particle[i].xc - particle[k].xc;
ry = particle[i].yc - particle[k].yc;
particle[i].angle = argument(rx, ry);
particle[i].omega = rx*particle[i].vy - ry*particle[i].vx;
particle[i].omega -= rx*particle[k].vy - ry*particle[k].vx;
if (CENTER_COLLIDED_PARTICLES)
{
particle[i].xc = 0.5*(particle[i].xc + particle[k].xc);
particle[i].yc = 0.5*(particle[i].yc + particle[k].yc);
}
particle[i].vx = mr*(particle[i].vx + particle[k].vx);
particle[i].vy = mr*(particle[i].vy + particle[k].vy);
particle[i].mass_inv = newmass_inv;
particle[k].active = 0;
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
search = 0;
}
}
}
return(ncollisions);
}
int chem_merge(int i, int type2, int newtype, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1])
/* merging of particle i and a particle of type type2 into a particle of type newtype */
{
int j, k, type1;
short int search = 1;
double distance, rx, ry, m2, mr1, mr2, newmass_inv, old_energy, e_ratio;
type1 = particle[i].type;
newmass_inv = inv_masses[newtype];
mr1 = newmass_inv/inv_masses[type1];
mr2 = newmass_inv/inv_masses[type2];
for (j=0; j<particle[i].hash_nneighb; j++)
{
k = particle[i].hashneighbour[j];
if ((particle[k].active)&&(particle[k].type == type2))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
{
particle[i].type = newtype;
particle[i].radius = radii[newtype];
rx = particle[i].xc - particle[k].xc;
ry = particle[i].yc - particle[k].yc;
particle[i].angle = argument(rx, ry);
particle[i].omega = rx*particle[i].vy - ry*particle[i].vx;
particle[i].omega -= rx*particle[k].vy - ry*particle[k].vx;
if (CENTER_COLLIDED_PARTICLES)
{
particle[i].xc = 0.5*(particle[i].xc + particle[k].xc);
particle[i].yc = 0.5*(particle[i].yc + particle[k].yc);
}
particle[i].vx = mr1*particle[i].vx + mr2*particle[k].vx;
particle[i].vy = mr1*particle[i].vy + mr2*particle[k].vy;
particle[i].mass_inv = newmass_inv;
if (EXOTHERMIC) adapt_speed_exothermic(&particle[i], DELTA_EKIN);
particle[k].active = 0;
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
}
return(ncollisions);
}
int chem_multi_merge(int i, int ni, int type2, int newtype, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1])
/* merging of ni particles of the same type as particle i (including particle i) */
/* and one particle of type type2 into a particle of type newtype */
{
int j, k, type1, n1, n2, k1[10], k2;
short int search = 1;
double distance, xg, yg, rx, ry, rx1[10], ry1[10], rx2, ry2, mr1, mr2, newmass_inv;
if (ni > 10)
{
printf("Error: need to increase size of k1 table in chem_multi_merge()\n");
exit(1);
}
type1 = particle[i].type;
newmass_inv = inv_masses[newtype];
mr1 = newmass_inv/inv_masses[type1];
mr2 = newmass_inv/inv_masses[type2];
n1 = 0;
n2 = 0;
for (j=0; j<particle[i].hash_nneighb; j++)
{
k = particle[i].hashneighbour[j];
if ((particle[k].active)&&(particle[k].type == type1))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
{
k1[n1] = k;
n1++;
}
}
else if ((particle[k].active)&&(particle[k].type == type2))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
{
k2 = k;
n2 = 1;
}
}
}
if ((n1 == ni-1)&&(n2 == 1))
{
particle[i].type = radii[newtype];
particle[i].radius *= 1.5;
xg = particle[i].xc;
yg = particle[i].yc;
for (n1 = 0; n1 < ni-1; n1++)
{
xg += particle[k1[n1]].xc;
yg += particle[k1[n1]].yc;
}
xg = xg*mr1 + particle[k2].xc*mr2;
yg = yg*mr1 + particle[k2].yc*mr2;
rx = particle[i].xc - xg;
ry = particle[i].yc - yg;
for (n1 = 0; n1 < ni-1; n1++)
{
rx1[n1] = particle[k1[n1]].xc - xg;
ry1[n1] = particle[k1[n1]].yc - yg;
}
rx2 = particle[k2].xc - xg;
ry2 = particle[k2].yc - yg;
particle[i].angle = argument(rx2, ry2);
for (n1 = 0; n1 < ni-1; n1++) particle[i].angle += argument(rx1[n1], ry1[n1]);
if (CENTER_COLLIDED_PARTICLES)
{
particle[i].xc = xg;
particle[i].yc = yg;
}
particle[i].vx = mr1*particle[i].vx + mr2*particle[k2].vx;
particle[i].vy = mr1*particle[i].vy + mr2*particle[k2].vy;
for (n1 = 0; n1 < ni-1; n1++)
{
particle[i].vx += mr1*particle[k1[n1]].vx;
particle[i].vy += mr1*particle[k1[n1]].vx;
}
particle[i].omega = mr1*(rx*particle[i].vy - ry*particle[i].vx);
for (n1 = 0; n1 < ni-1; n1++)
particle[i].omega += mr1*(rx1[n1]*particle[k1[n1]].vy - ry1[n1]*particle[k1[n1]].vx);
particle[i].omega += mr2*(rx2*particle[k2].vy - ry2*particle[k2].vx);
particle[i].mass_inv = newmass_inv;
for (k==0; k<ni; k++) particle[k1[k]].active = 0;
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
return(ncollisions);
}
int chem_catalytic_merge(int i, int type_catalyst, int newtype, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1])
/* merging of 2 particles of the same type as particle i (including particle i) */
/* into a particle of type newtype, provided a particle of type type_catalyst is present */
{
int j, k, type1, n1, n2, k1, k2;
short int search = 1;
double distance, xg, yg, rx, rx1, ry, ry1, mr, newmass_inv;
type1 = particle[i].type;
newmass_inv = inv_masses[newtype];
mr = inv_masses[newtype]/inv_masses[type1];
n1 = 0;
n2 = 0;
for (j=0; j<particle[i].hash_nneighb; j++)
{
k = particle[i].hashneighbour[j];
if ((particle[k].active)&&(particle[k].type == type1))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
{
k1 = k;
n1 = 1;
}
}
else if ((particle[k].active)&&(particle[k].type == type_catalyst))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
{
k2 = k;
n2 = 1;
}
}
}
if ((n1 == 1)&&(n2 == 1))
{
particle[i].type = newtype;
particle[i].radius = radii[newtype];
xg = 0.5*(particle[i].xc + particle[k1].xc + particle[k2].xc);
yg = 0.5*(particle[i].yc + particle[k1].yc + particle[k2].yc);
rx = particle[i].xc - xg;
ry = particle[i].yc - yg;
rx1 = particle[k1].xc - xg;
ry1 = particle[k1].yc - yg;
particle[i].angle = argument(rx1, ry1);
if (CENTER_COLLIDED_PARTICLES)
{
particle[i].xc = xg;
particle[i].yc = yg;
}
particle[i].vx = mr*(particle[i].vx + particle[k1].vx);
particle[i].vy = mr*(particle[i].vy + particle[k1].vy);
particle[i].omega = mr*(rx*particle[i].vy - ry*particle[i].vx);
particle[i].omega += mr*(rx1*particle[k1].vy - ry1*particle[k1].vx);
particle[i].mass_inv = newmass_inv;
particle[k1].active = 0;
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
return(ncollisions);
}
int chem_split(int i, int newtype1, int newtype2, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1])
/* split particle i into particles of type newtype1 and newtype2 */
{
int j, k, oldtype;
short int success;
double distance, rx, ry, xg, yg, normv;
normv = module2(particle[i].vx, particle[i].vy);
rx = -MU*particle[i].vy/normv;
ry = MU*particle[i].vx/normv;
xg = particle[i].xc;
yg = particle[i].yc;
particle[i].xc += rx;
particle[i].yc += ry;
oldtype = particle[i].type;
/* test whether there is room to put two particles */
success = add_particle(xg - rx, yg - ry, particle[i].vx, particle[i].vy, 1.0/inv_masses[newtype1], newtype1, particle);
if (success)
{
particle[i].type = newtype2;
particle[i].mass_inv = inv_masses[newtype2];
particle[i].radius = radii[newtype2];
particle[ncircles-1].type = newtype1;
particle[ncircles-1].mass_inv = inv_masses[newtype1];
particle[ncircles-1].radius = radii[newtype1];
if (EXOTHERMIC)
{
adapt_speed_exothermic(&particle[i], -0.5*DELTA_EKIN);
adapt_speed_exothermic(&particle[ncircles-1], -0.5*DELTA_EKIN);
}
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(oldtype);
if (ncollisions < NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
else
{
particle[i].xc -= rx;
particle[i].yc -= ry;
}
return(ncollisions);
}
int chem_transfer(int i, int type2, int newtype1, int newtype2, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1], double reac_dist)
/* reaction of particle i and a particle of type type2 into a particles of types newtype1 and newtype2 */
{
int j, k, type1;
short int search = 1;
double distance, rx, ry, mass_inv1, mass_inv2, newmass_inv1, newmass_inv2, old_energy, e_ratio, omega;
type1 = particle[i].type;
mass_inv1 = inv_masses[type1];
mass_inv2 = inv_masses[type2];
newmass_inv1 = inv_masses[newtype1];
newmass_inv2 = inv_masses[newtype2];
for (j=0; j<particle[i].hash_nneighb; j++)
{
k = particle[i].hashneighbour[j];
if ((particle[k].active)&&(particle[k].type == type2))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < reac_dist*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
{
particle[i].type = newtype1;
particle[i].radius = radii[newtype1];
particle[k].type = newtype2;
particle[k].radius = radii[newtype2];
/* momentum conservation does not determine outgoing velocities completely */
/* so make some arbitrary/random choices here */
rx = particle[i].xc - particle[k].xc;
ry = particle[i].yc - particle[k].yc;
particle[i].angle = argument(rx, ry);
particle[k].angle = argument(rx, ry) + PI;
omega = gaussian()*OMEGA_INITIAL;
particle[i].omega = omega;
particle[k].omega = -omega;
particle[i].vx *= newmass_inv1/mass_inv1;
particle[i].vy *= newmass_inv1/mass_inv1;
particle[i].mass_inv = newmass_inv1;
particle[k].vx *= newmass_inv2/mass_inv2;
particle[k].vy *= newmass_inv2/mass_inv2;
particle[k].mass_inv = newmass_inv2;
if (EXOTHERMIC)
{
adapt_speed_exothermic(&particle[i], DELTA_EKIN);
adapt_speed_exothermic(&particle[k], DELTA_EKIN);
}
collisions[ncollisions].x = 0.5*(particle[i].xc + particle[k].xc);
collisions[ncollisions].y = 0.5*(particle[i].yc + particle[k].yc);
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
}
return(ncollisions);
}
int chem_convert(int i, int newtype, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1], double reac_prob)
/* convert particle i into new type with probability reac_prob */
{
int oldtype;
oldtype = particle[i].type;
// if (oldtype == 5) printf("Converting type %i to type %i\n", oldtype, newtype);
if ((double)rand()/RAND_MAX < reac_prob)
{
particle[i].type = newtype;
particle[i].radius = radii[newtype];
particle[i].mass_inv = inv_masses[newtype];
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(oldtype);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
return(ncollisions);
}
int chem_catalytic_convert(int i, int type2, int newtype, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double inv_masses[RD_TYPES+1], double radii[RD_TYPES+1], double reaction_dist, double reaction_prob)
/* if 2 particles of the same type as particle i (including particle i) are close */
/* to a particle of type type2, convert particle type from type2 to newtype */
{
int j, k, type1, n1, n2, k1, k2, oldtype;
short int search = 1;
double distance;
type1 = particle[i].type;
oldtype = particle[i].type;
n1 = 0;
n2 = 0;
for (j=0; j<particle[i].hash_nneighb; j++)
{
k = particle[i].hashneighbour[j];
if ((particle[k].active)&&(particle[k].type == type1))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < reaction_dist*MU)&&((double)rand()/RAND_MAX < reaction_prob))
{
k1 = k;
n1 = 1;
}
}
else if ((particle[k].active)&&(particle[k].type == type2))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < reaction_dist*MU)&&((double)rand()/RAND_MAX < reaction_prob))
{
k2 = k;
n2 = 1;
}
}
}
if ((n1 == 1)&&(n2 == 1))
{
particle[k2].type = newtype;
particle[k2].radius = radii[newtype];
particle[k2].mass_inv = inv_masses[newtype];
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(oldtype);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
return(ncollisions);
}
// int chem_dissociate_water_old(int i, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_prob)
// /* dissociation of water molecule i into a pair H+, OH- */
// {
// int j, k, p, q, closeby = 0, reaction = 0;
// double distance, move_factor, xnew, ynew, vxnew, vynew, mr1, mr2;
//
// if ((particle[i].npartners == 2)&&((double)rand()/RAND_MAX < reaction_prob))
// {
// /* choose particle k to dissociate */
// if (rand()%2 == 0)
// {
// j = particle[i].partner[0];
// k = particle[i].partner[1];
// }
// else
// {
// j = particle[i].partner[1];
// k = particle[i].partner[0];
// }
//
// /* propose moving dissociated particles further away */
// move_factor = 2.0;
// xnew = particle[i].xc + move_factor*(particle[k].xc - particle[i].xc);
// ynew = particle[i].yc + move_factor*(particle[k].yc - particle[i].yc);
//
// /* test distance to other particles */
// closeby = 0;
// for (p=0; p<particle[k].hash_nneighb; p++)
// {
// q = particle[k].hashneighbour[p];
// if ((q!=i)&&(q!=j)&&(q!=k)&&(particle[q].active))
// {
// distance = module2(xnew - particle[q].xc, ynew - particle[q].yc);
// if (distance < SAFETY_FACTOR*MU_B) closeby = 1;
// }
// }
//
// if (closeby) printf("Cannot split molecule %i\n", i);
// else
// {
// printf("Splitting molecule %i\n", i);
//
// particle[i].npartners = 1;
// particle[i].partner[0] = j;
//
// particle[j].npartners = 1;
// particle[j].partner[0] = i;
//
// particle[k].npartners = 0;
// particle[k].xc = xnew;
// particle[k].yc = ynew;
//
// collisions[ncollisions].x = particle[i].xc;
// collisions[ncollisions].y = particle[i].yc;
// collisions[ncollisions].time = COLLISION_TIME;
// collisions[ncollisions].color = type_hue(1);
//
// if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
// else printf("Too many collisions\n");
// }
// }
// else if (particle[i].npartners == 1)
// {
// mr1 = PARTICLE_MASS/(PARTICLE_MASS + 2.0*PARTICLE_MASS_B);
// mr2 = 1.0 - mr1;
//
// // for (p=0; p<particle[i].hash_nneighb; p++)
//
// p = 0;
// while ((p<particle[i].hash_nneighb)&&(!reaction))
// {
// k = particle[i].hashneighbour[p];
// if ((particle[k].active)&&(particle[k].type == 2)&&(particle[k].npartners == 0))
// {
// distance = module2(particle[i].deltax[p], particle[i].deltay[p]);
// if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < REACTION_PROB))
// {
// reaction = 1;
// printf("Merging molecule %i with particle %i\n", i, k);
//
// j = particle[i].partner[0];
// particle[i].npartners = 2;
// particle[i].partner[1] = k;
//
// particle[j].npartners = 2;
// particle[j].partner[0] = i; /* needed? */
// particle[j].partner[1] = k;
//
// particle[k].npartners = 2;
// particle[k].partner[0] = i;
// particle[k].partner[1] = j;
//
// distance = 2.0*sin(PARTNER_ANGLE*PI/360.0)*(MU + MU_B)*PAIR_DRATIO;
// particle[k].partner_eqd[1] = distance;
// particle[j].partner_eqd[1] = distance;
//
// /* equalize speeds */
// vxnew = mr1*particle[i].vx + mr2*particle[j].vx + mr2*particle[k].vx;
// vynew = mr1*particle[i].vy + mr2*particle[j].vy + mr2*particle[k].vy;
//
// particle[i].vx = vxnew;
// particle[i].vy = vynew;
// particle[j].vx = vxnew;
// particle[j].vy = vynew;
// particle[k].vx = vxnew;
// particle[k].vy = vynew;
//
// collisions[ncollisions].x = particle[i].xc;
// collisions[ncollisions].y = particle[i].yc;
// collisions[ncollisions].time = COLLISION_TIME;
// collisions[ncollisions].color = 0.0;
// // collisions[ncollisions].color = type_hue(2);
//
// if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
// else printf("Too many collisions\n");
// }
// }
// p++;
// }
// }
//
// return(ncollisions);
// }
int chem_dissociate_molecule(int i, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_prob)
/* dissociation of water molecule i into a pair H+, OH- */
{
int k, p, q, r, ndiss, closeby = 0, reaction = 0, diff, np, j[NMAXPARTNERS];
double distance, move_factor, xnew, ynew, vxnew, vynew, mr1, mr2;
if ((double)rand()/RAND_MAX < reaction_prob)
{
np = particle[i].npartners;
/* choose particle k to dissociate */
ndiss = rand()%np;
k = particle[i].partner[ndiss];
/* new list of partners of i */
for (p=0; p<ndiss; p++) j[p] = particle[i].partner[p];
for (p=ndiss+1; p<np; p++) j[p-1] = particle[i].partner[p];
/* propose moving dissociated particle further away */
move_factor = 2.0 + (double)(np-2);
xnew = particle[i].xc + move_factor*(particle[k].xc - particle[i].xc);
ynew = particle[i].yc + move_factor*(particle[k].yc - particle[i].yc);
/* test distance to other particles */
closeby = 0;
for (p=0; p<particle[k].hash_nneighb; p++)
{
q = particle[k].hashneighbour[p];
diff = ((q!=i)&&(q!=k)&&(particle[q].active));
for (r=0; r<np-1; r++) if (q=j[r]) diff = 0;
if (diff)
{
distance = module2(xnew - particle[q].xc, ynew - particle[q].yc);
if (distance < SAFETY_FACTOR*MU_B) closeby = 1;
}
}
if (closeby) printf("Cannot split molecule %i\n", i);
else
{
printf("Splitting molecule %i\n", i);
particle[i].npartners = np-1;
for (p=0; p<np-1; p++) particle[i].partner[p] = j[p];
for (p=0; p<np-1; p++)
{
particle[j[p]].npartners = np-1;
particle[j[p]].partner[0] = i;
for (q=0; q<p; q++) particle[j[p]].partner[q+1] = j[q];
for (q=p+1; q<np-1; q++) particle[j[p]].partner[q] = j[q];
}
particle[k].npartners = 0;
particle[k].xc = xnew;
particle[k].yc = ynew;
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = partner_color(np);
// collisions[ncollisions].color = type_hue(1);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
return(ncollisions);
}
int chem_merge_molecule(int i, int type2, int maxpartners, t_particle particle[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_prob)
/* merging of molecule i with another molecule of type type 2 */
/* having at most nmaxpartners partners already */
{
int k, p, q, np, n, m, closeby = 0, reaction = 0, j[NMAXPARTNERS], r, kk;
double distance, angle, move_factor, xnew, ynew, vxnew, vynew, mr1, mr2;
np = particle[i].npartners;
mr1 = PARTICLE_MASS/(PARTICLE_MASS + (double)(np+1)*PARTICLE_MASS_B);
mr2 = 1.0 - mr1;
p = 0;
while ((p<particle[i].hash_nneighb)&&(!reaction))
{
k = particle[i].hashneighbour[p];
if ((particle[k].active)&&(particle[k].type == type2)&&(particle[k].npartners <= maxpartners))
{
distance = module2(particle[i].deltax[p], particle[i].deltay[p]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < reaction_prob))
{
reaction = 1;
printf("Merging molecule %i with particle %i\n", i, k);
for (r=0; r<np; r++) j[r] = particle[i].partner[r];
particle[i].npartners++;
particle[i].partner[np] = k;
for (r=0; r<np; r++)
{
particle[j[r]].npartners = np+1;
particle[j[r]].partner[np] = k;
}
particle[k].npartners = np+1;
particle[k].partner[0] = i;
particle[k].partner_eqd[0] = (MU + MU_B)*PAIR_DRATIO;
for (kk=0; kk<np; kk++) particle[k].partner[kk+1] = j[kk];
if (np == 1)
{
distance = 2.0*sin(PARTNER_ANGLE*PI/360.0)*(MU + MU_B)*PAIR_DRATIO;
particle[k].partner_eqd[1] = distance;
particle[j[0]].partner_eqd[1] = distance;
}
else /* distribute particles uniformly around i */
{
j[np] = k;
print_partners(i, particle);
for (q=0; q<np+1; q++)
{
angle = (double)q*DPI/(double)(np+1);
particle[j[q]].npartners = np+1;
particle[j[q]].partner[0] = i;
particle[j[q]].partner_eqd[0] = (MU + MU_B)*PAIR_DRATIO;
particle[i].partner_eqd[q] = (MU + MU_B)*PAIR_DRATIO;
particle[j[q]].xc = particle[i].xc + (MU + MU_B)*PAIR_DRATIO*cos(angle);
particle[j[q]].yc = particle[i].yc + (MU + MU_B)*PAIR_DRATIO*sin(angle);
}
for (q=0; q<np+1; q++)
for (r=0; r<np+1; r++)
{
m = particle[j[q]].partner[r];
particle[j[q]].partner_eqd[r] = module2(particle[j[q]].xc - particle[m].xc, particle[j[q]].yc - particle[m].yc);
}
// for (q=0; q<np+1; q++) print_partners(j[q], particle);
}
/* equalize speeds */
vxnew = mr1*particle[i].vx + mr2*particle[k].vx;
vynew = mr1*particle[i].vy + mr2*particle[k].vy;
for (r=0; r<np; r++)
{
vxnew += mr2*particle[j[r]].vx;
vynew += mr2*particle[j[r]].vy;
}
particle[i].vx = vxnew;
particle[i].vy = vynew;
for (r=0; r<np; r++)
{
particle[j[r]].vx = vxnew;
particle[j[r]].vy = vynew;
}
particle[k].vx = vxnew;
particle[k].vy = vynew;
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
p++;
}
return(ncollisions);
}
int chem_multi_glue_molecule(int i, int type2, int maxpartners, int no_triangles, int no_cluster, int require_charge, int equalize_charge, t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_prob)
/* glue molecule containing particle i with another molecule of type type 2 */
/* having at most nmaxpartners partners already */
/* if no_triangles >= 1, particles that have a common partner are not paired */
/* if no_cluster = 1, particles are not paired if they belong to the same cluster */
{
int k, p, q, p1, q1, p2, np, nq, n, m, closeby = 0, reaction = 0, jp[NMAXPARTNERS], jq[NMAXPARTNERS], r, kk, different, np2, moli, molk, mp, newpartner, pp, type1;
double distance, angle, move_factor, xnew, ynew, vxnew, vynew, m1, m2, mr1, mr2, deltav, x, y;
short int charge_condition, triangle_condition;
type1 = particle[i].type;
np = particle[i].npartners;
moli = particle[i].molecule;
if (np > maxpartners) return(ncollisions);
m1 = 1.0/particle[i].mass_inv;
for (p=0; p<np; p++)
{
jp[p] = particle[i].partner[p];
m1 += 1.0/particle[jp[p]].mass_inv;
}
p = 0;
while ((p<particle[i].hash_nneighb)&&(!reaction))
{
k = particle[i].hashneighbour[p];
nq = particle[k].npartners;
molk = particle[k].molecule;
charge_condition = (!require_charge)||(particle[i].charge*particle[k].charge < 0.0);
triangle_condition = 1;
/* exclude mergers between added particles */
if (particle[i].added == particle[k].added)
{
triangle_condition = particle[i].reactive*particle[k].reactive;
/* allow mergers if molecules are paired to same DNA strand */
if ((particle[i].paired)&&(particle[k].paired)&&(particle[i].partner_molecule % NGRIDY == particle[k].partner_molecule % NGRIDY)) triangle_condition = 1;
}
if ((RD_REACTION == CHEM_DNA_ENZYME)||(RD_REACTION == CHEM_DNA_ENZYME_REPAIR))
{
/* exclude mergers of added molecules to original ones via backbone */
if ((particle[i].added != particle[k].added)&&(type2 <= 2)&&(type1 <= 2))
triangle_condition = 0;
/* NEW TEST */
// if (molk == moli) triangle_condition = 0;
/* NEW TEST */
/* avoid original backbones to form a loop */
if ((!particle[i].added)&&(!particle[k].added)&&((molk-moli > NGRIDX/2)||(moli-molk > NGRIDX/2))) triangle_condition = 0;
}
if (no_triangles == 1) /* do not pair i and k if they have a common partner */
{
for (q=0; q<nq; q++)
for (p1=0; p1<np; p1++)
if (particle[k].partner[q] == particle[i].partner[p1]) triangle_condition = 0;
}
if (no_triangles == 2) /* also exclude i or k being a partner of k or i */
{
for (q=0; q<nq; q++) if (particle[k].partner[q] == i) triangle_condition = 0;
for (p1=0; p1<np; p1++) if (particle[i].partner[p1] == k) triangle_condition = 0;
}
if (no_triangles >= 3) /* exclude connections of different type between the same molecules */
{
if (molk != moli) for (p1=0; p1<np; p1++)
{
n = particle[i].partner[p1];
for (q=0; q<particle[n].npartners; q++)
{
m = particle[n].partner[q];
if (particle[m].molecule == molk)
if (particle[n].type != particle[i].type)
triangle_condition = 0;
}
if ((particle[n].molecule != moli)&&(particle[n].molecule != molk))
triangle_condition = 0;
if (particle[n].molecule == moli) if (particle[n].type == particle[i].type)
{
for (q=0; q<particle[n].npartners; q++)
{
m = particle[n].partner[q];
if ((particle[m].molecule != moli)&&(particle[m].molecule != molk))
triangle_condition = 0;
}
}
}
if (molk != moli) for (q=0; q<nq; q++)
{
n = particle[k].partner[q];
if ((particle[n].molecule != molk)&&(particle[n].molecule != moli))
triangle_condition = 0;
}
}
if (no_triangles >= 4) /* also exclude second-order partners */
{
for (q=0; q<nq; q++)
for (p1=0; p1<np; p1++)
{
n = particle[k].partner[q];
for (q1 = 0; q1 < particle[n].npartners; q1++)
if (particle[n].partner[q1] == particle[i].partner[p1])
triangle_condition = 0;
n = particle[i].partner[p1];
for (p2 = 0; p2 < particle[n].npartners; p2++)
if (particle[n].partner[p2] == particle[k].partner[q])
triangle_condition = 0;
}
}
if ((no_cluster)&&(particle[k].cluster = particle[i].cluster)) triangle_condition = 0;
if ((particle[k].active)&&(charge_condition)&&(triangle_condition)&&(particle[k].type == type2)&&(nq <= maxpartners))
{
distance = module2(particle[i].deltax[p], particle[i].deltay[p]);
if ((distance < REACTION_DIST*MU)&&((double)rand()/RAND_MAX < reaction_prob))
{
m2 = 1.0/particle[k].mass_inv;
for (q=0; q<nq; q++)
{
jq[q] = particle[k].partner[q];
m2 += 1.0/particle[jq[q]].mass_inv;
}
mr1 = m1/(m1 + m2);
mr2 = 1.0 - mr1;
if ((np == 0)&&(nq == 0))
{
reaction = 1;
printf("Merging molecule %i with particle %i\n", i, k);
distance = (particle[i].radius + particle[k].radius)*PAIR_DRATIO;
particle[i].npartners++;
particle[i].partner[0] = k;
particle[i].partner_eqd[0] = distance;
particle[k].npartners++;
particle[k].partner[0] = i;
particle[k].partner_eqd[0] = distance;
/* equalize speeds */
vxnew = mr1*particle[i].vx + mr2*particle[k].vx;
vynew = mr1*particle[i].vy + mr2*particle[k].vy;
particle[i].vx = vxnew;
particle[i].vy = vynew;
particle[k].vx = vxnew;
particle[k].vy = vynew;
/* equalize charges */
if (equalize_charge == 1) particle[k].charge = particle[i].charge;
else if (equalize_charge == 2)
{
particle[i].coulomb = 0;
particle[k].coulomb = 0;
}
/* update molecule data */
mp = molecule[moli].npartners;
if (mp < NMAXPARTNERMOLECULES)
{
newpartner = 1;
for (pp=0; pp<mp; pp++)
if (molecule[moli].partner[pp] == molk) newpartner = 0;
if (newpartner)
{
printf("adding molecule %i as %ith partner of molecule %i\n", molk, mp, moli);
molecule[moli].partner[mp] = molk;
molecule[moli].connection_type[mp] = particle[i].type;
molecule[moli].npartners++;
}
}
mp = molecule[molk].npartners;
if (mp < NMAXPARTNERMOLECULES)
{
newpartner = 1;
for (pp=0; pp<mp; pp++)
if (molecule[molk].partner[pp] == moli) newpartner = 0;
if (newpartner)
{
printf("adding molecule %i as %ith partner of molecule %i\n", moli, mp, molk);
molecule[molk].partner[mp] = moli;
molecule[molk].connection_type[mp] = type2;
molecule[molk].npartners++;
}
}
}
else
{
/* check if i and k belong to different molecules */
// different = 1;
// for (p1=0; p1<np; p1++)
// if (k == jp[p1]) different = 0;
// for (q1=0; q1<nq; q1++)
// if (i == jq[q1]) different = 0;
/* TODO: replace by moli != molk ? */
different = (moli != molk);
if (different)
{
deltav = module2(particle[i].vx - particle[k].vx, particle[i].vy - particle[k].vy);
// printf("Delta v is %.3lg\n", deltav);
if (deltav < DELTAVMAX)
{
reaction = 1;
printf("Pairing molecules %i and %i containing particles %i and %i\n", moli, molk, i, k);
distance = (particle[i].radius + particle[k].radius)*PAIR_DRATIO;
particle[i].npartners++;
particle[i].partner[np] = k;
particle[i].partner_eqd[np] = distance;
particle[k].npartners++;
particle[k].partner[nq] = i;
particle[k].partner_eqd[nq] = distance;
/* equalize speeds */
vxnew = mr1*particle[i].vx + mr2*particle[k].vx;
vynew = mr1*particle[i].vy + mr2*particle[k].vy;
particle[i].vx = vxnew;
particle[i].vy = vynew;
particle[k].vx = vxnew;
particle[k].vy = vynew;
if (equalize_charge) particle[k].charge = particle[i].charge;
for (p1=0; p1<np; p1++) for (q1=0; q1<nq; q1++)
{
particle[jp[p1]].vx = vxnew;
particle[jp[p1]].vy = vynew;
particle[jq[q1]].vx = vxnew;
particle[jq[q1]].vy = vynew;
if (equalize_charge == 1)
{
particle[jp[p1]].charge = particle[i].charge;
particle[jq[q1]].charge = particle[i].charge;
}
else if (equalize_charge == 2)
{
particle[jp[p1]].coulomb = 0;
particle[jq[q1]].coulomb = 0;
}
}
/* update molecule data */
mp = molecule[moli].npartners;
if (mp < NMAXPARTNERMOLECULES)
{
newpartner = 1;
for (pp=0; pp<mp; pp++)
if (molecule[moli].partner[pp] == molk) newpartner = 0;
if (newpartner)
{
// printf("adding molecule %i as %ith partner of molecule %i\n", molk, mp, moli);
molecule[moli].partner[mp] = molk;
molecule[moli].connection_type[mp] = particle[i].type;
molecule[moli].npartners++;
}
}
mp = molecule[molk].npartners;
if (mp < NMAXPARTNERMOLECULES)
{
newpartner = 1;
for (pp=0; pp<mp; pp++)
if (molecule[molk].partner[pp] == moli) newpartner = 0;
if (newpartner)
{
// printf("adding molecule %i as %ith partner of molecule %i\n", moli, mp, molk);
molecule[molk].partner[mp] = moli;
molecule[molk].connection_type[mp] = particle[k].type;
molecule[molk].npartners++;
}
}
/* merge secondary partners, experimental */
if (SECONDARY_PAIRING)
{
np2 = particle[k].npartners;
for (p1 = 0; p1 < np2; p1++)
// if (np2 > 1)
{
n = particle[k].partner[p1];
x = particle[i].xc - particle[n].xc;
y = particle[i].yc - particle[n].yc;
/* deal with periodic boundary conditions */
if (x > 0.5*(XMAX - XMIN)) x -= (XMAX - XMIN);
else if (x < 0.5*(XMIN - XMAX)) x += (XMAX - XMIN);
if (y > 0.5*(YMAX - YMIN)) y -= (YMAX - YMIN);
else if (y < 0.5*(YMIN - YMAX)) y += (YMAX - YMIN);
distance = module2(x, y);
if (distance > particle[i].radius)
{
np = particle[i].npartners;
particle[i].npartners++;
particle[i].partner[np] = n;
particle[i].partner_eqd[np] = distance;
nq = particle[n].npartners;
particle[n].npartners++;
particle[n].partner[nq] = i;
particle[n].partner_eqd[nq] = distance;
}
}
}
}
}
}
if (reaction)
{
/* TEST */
if ((RD_REACTION == CHEM_DNA_ENZYME)||(RD_REACTION == CHEM_DNA_ENZYME_REPAIR))
{
/* consider merged marticles as reactive */
if ((particle[i].added == 1)&&(particle[i].type >= 3)&&(particle[i].type <= 6))
{
particle[i].paired = 1;
particle[i].partner_molecule = molk;
for (p1 = 0; p1<particle[i].npartners; p1++)
{
n = particle[i].partner[p1];
particle[n].paired = 1;
particle[n].partner_molecule = molk;
}
}
if ((particle[k].added == 1)&&(type2 >= 3)&&(type2 <= 6))
{
particle[k].paired = 1;
particle[k].partner_molecule = moli;
for (p1 = 0; p1<particle[k].npartners; p1++)
{
n = particle[k].partner[p1];
particle[n].paired = 1;
particle[n].partner_molecule = moli;
}
}
}
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
}
p++;
}
return(ncollisions);
}
void translate_cluster(int j, t_cluster cluster[NMAXCIRCLES], t_particle particle[NMAXCIRCLES], double dx, double dy)
/* translate a cluster and all partcles it contains */
{
int k, p, np;
np = cluster[j].nparticles;
if (np == 1)
{
p = cluster[j].particle[0];
particle[p].xc += dx;
particle[p].yc += dy;
cluster[j].xg = particle[p].xc;
cluster[j].yg = particle[p].yc;
}
else
{
for (k=0; k<np; k++)
{
p = cluster[j].particle[k];
particle[p].xc += dx;
particle[p].yc += dy;
}
cluster[j].xg += dx;
cluster[j].yg += dy;
}
}
void rotate_cluster(int j, t_cluster cluster[NMAXCIRCLES], t_particle particle[NMAXCIRCLES], double angle)
/* translate a cluster and all partcles it contains */
{
int k, p, np;
double ca, sa, x, y;
np = cluster[j].nparticles;
if (np == 1)
{
p = cluster[j].particle[0];
particle[p].angle += angle;
cluster[j].angle = particle[p].angle;
}
else
{
ca = cos(angle);
sa = sin(angle);
for (k=0; k<np; k++)
{
p = cluster[j].particle[k];
particle[p].angle += angle;
x = particle[p].xc - cluster[j].xg;
y = particle[p].yc - cluster[j].yg;
particle[p].xc = cluster[j].xg + ca*x - sa*y;
particle[p].yc = cluster[j].yg + sa*x + ca*y;
}
cluster[j].angle += angle;
}
}
void rotate_cluster_around_particle(int j, int i, t_cluster cluster[NMAXCIRCLES], t_particle particle[NMAXCIRCLES], double angle)
/* translate a cluster and all partcles it contains around center of particle i */
{
int k, p, np;
double ca, sa, x, y;
np = cluster[j].nparticles;
if (np == 1)
{
p = cluster[j].particle[0];
particle[p].angle += angle;
cluster[j].angle = particle[p].angle;
}
else
{
ca = cos(angle);
sa = sin(angle);
for (k=0; k<np; k++)
{
p = cluster[j].particle[k];
particle[p].angle += angle;
x = particle[p].xc - particle[i].xc;
y = particle[p].yc - particle[i].yc;
particle[p].xc = particle[i].xc + ca*x - sa*y;
particle[p].yc = particle[i].yc + sa*x + ca*y;
}
cluster[j].angle += angle;
}
}
void translate_and_rotate_cluster(int j, t_cluster cluster[NMAXCIRCLES], t_particle particle[NMAXCIRCLES], double dx, double dy, double angle)
/* translate and rotate a cluster and all partcles it contains */
{
int k, p, np;
double ca, sa, x, y;
np = cluster[j].nparticles;
if (np == 1)
{
p = cluster[j].particle[0];
particle[p].xc += dx;
particle[p].yc += dy;
particle[p].angle += angle;
cluster[j].xg = particle[p].xc;
cluster[j].yg = particle[p].yc;
cluster[j].angle = particle[p].angle;
}
else
{
ca = cos(angle);
sa = sin(angle);
for (k=0; k<np; k++)
{
p = cluster[j].particle[k];
x = particle[p].xc - cluster[j].xg;
y = particle[p].yc - cluster[j].yg;
particle[p].xc = cluster[j].xg + ca*x - sa*y + dx;
particle[p].yc = cluster[j].yg + sa*x + ca*y + dy;
}
cluster[j].xg += dx;
cluster[j].yg += dy;
cluster[j].angle += angle;
}
}
int merge_clusters(int i, int j, int pi, int pj, t_particle particle[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES], int adjust_angles, short int flip)
/* merge clusters i and j to form new cluster il */
/* il is number of largest cluster aming i and j */
/* pi and pj are the numbers of the contact particles */
/* returns number of new (largest) cluster il */
{
int p, q, q0, c0, is, il, ns, nl, ps, pl;
double mtot, dangle, d2, newangle;
static double alpha, alpha2;
static int first = 1, nmergers = 0;
if (first)
{
alpha = PI/(double)NPOLY;
alpha2 = 2.0*alpha;
first = 0;
}
if (i == j) return(i);
nmergers++;
/* find largest cluster */
if (cluster[i].nparticles >= cluster[j].nparticles)
{
il = i;
is = j;
pl = pi;
ps = pj;
}
else
{
il = j;
is = i;
pl = pj;
ps = pi;
}
ns = cluster[is].nparticles;
nl = cluster[il].nparticles;
if (ns + nl >= NMAXPARTINCLUSTER)
{
printf("Warning: NMAXPARTINCLUSTER is too small\n");
printf("Try increasing to %i\n", cluster[i].nparticles + cluster[j].nparticles);
return(0);
}
printf("Merging clusters %i and %i, having %i and %i particles\n", il, is, nl, ns);
fprintf(lj_log, "Merging clusters %i and %i, having %i and %i particles\n", il, is, nl, ns);
fprintf(lj_log, "Large cluster %i:\n", il);
for (p=0; p<nl; p++)
{
q = cluster[il].particle[p];
fprintf(lj_log, "Particle %i (%i), angle %.3lg, flip %i\n", p, q, particle[q].angle*180.0/PI, particle[q].flip);
}
fprintf(lj_log, "Reacting particle %i\n\n", pl);
// fprintf(lj_log, "Cluster %i:\n", is);
// for (p=0; p<ns; p++)
// {
// q = cluster[is].particle[p];
// fprintf(lj_log, "Particle %i (%i), angle %.3lg, flip %i\n", p, q, particle[q].angle*180.0/PI, particle[q].flip);
// }
// fprintf(lj_log, "Reacting particle %i\n", ps);
cluster[is].active = 0;
/* update list of particles */
cluster[il].nparticles = ns + nl;
c0 = particle[cluster[il].particle[0]].cluster_color;
for (p=0; p<ns; p++)
{
q = cluster[is].particle[p];
cluster[il].particle[nl+p] = q;
particle[q].cluster = il;
particle[q].cluster_color = c0;
particle[q].collision = nmergers + 1;
}
/* update cluster size for P_CLUSTER_SIZE color scheme */
for (p=0; p<ns+nl; p++)
{
q = cluster[il].particle[p];
particle[q].cluster_size = ns + nl;
}
if ((NPOLY%2 == 1)&&(flip)) for (p=0; p<ns; p++)
{
q = cluster[il].particle[nl + p];
particle[q].flip = 1 - particle[q].flip;
}
fprintf(lj_log, "Small cluster %i:\n", is);
for (p=0; p<ns; p++)
{
q = cluster[is].particle[p];
fprintf(lj_log, "Particle %i (%i), angle %.3lg, flip %i\n", p, q, particle[q].angle*180.0/PI, particle[q].flip);
}
fprintf(lj_log, "Reacting particle %i\n\n", ps);
/* adjust angles */
/* use rotate_cluster ? */
if (adjust_angles)
{
for (p=0; p<nl; p++)
{
q = cluster[il].particle[p];
// printf("p = %i, angle = %.5lg\n", p, particle[q].angle*180.0/PI);
while (particle[q].angle >= alpha2) particle[q].angle -= alpha2;
while (particle[q].angle < 0.0) particle[q].angle += alpha2;
}
q = cluster[il].particle[0];
newangle = particle[q].angle;
cluster[il].angle = newangle;
// if (NPOLY%2==1) newangle += alpha;
for (p=nl; p<ns+nl; p++)
{
q = cluster[il].particle[p];
particle[q].angle = newangle;
// if ((NPOLY%2==1)&&(particle[q].flip != particle[ps].flip)) particle[q].angle += PI;
}
// if (NPOLY%2==1) for (p=nl; p<ns+nl; p++)
// {
// q = cluster[il].particle[p];
// if (particle[q].flip != particle[ps].flip) particle[q].angle += alpha;
// }
if (NPOLY%2==1) for (p=0; p<ns+nl; p++)
{
q = cluster[il].particle[p];
if (particle[q].flip == particle[0].flip) particle[q].angle = newangle;
else particle[q].angle = newangle + alpha;
}
}
/* total mass */
mtot = cluster[is].mass + cluster[il].mass;
/* distance between centers of gravity squared */
d2 = (cluster[i].xg - cluster[j].xg)*(cluster[i].xg - cluster[j].xg);
d2 += (cluster[i].yg - cluster[j].yg)*(cluster[i].yg - cluster[j].yg);
/* update center of gravity */
cluster[il].xg = (cluster[is].xg + cluster[il].xg)/mtot;
cluster[il].yg = (cluster[is].yg + cluster[il].yg)/mtot;
/* update moment of intertia (using parallel axis thm) */
cluster[il].inertia_moment += cluster[is].inertia_moment;
cluster[il].inertia_moment += d2*cluster[il].mass*cluster[is].mass/mtot;
cluster[il].inertia_moment_inv = 1.0/cluster[il].inertia_moment;
/* update total mass */
cluster[il].mass = mtot;
cluster[il].mass_inv = 1.0/mtot;
fprintf(lj_log, "Merged cluster %i:\n", is);
for (p=0; p<ns+nl; p++)
{
q = cluster[il].particle[p];
fprintf(lj_log, "Particle %i (%i), angle %.3lg, flip %i\n", p, q, particle[q].angle*180.0/PI, particle[q].flip);
}
fprintf(lj_log, "Reacting particle %i\n\n", ps);
// short int active; /* has value 1 if cluster is active */
// short int thermostat; /* has value 1 if cluster is coupled to thermostat */
// double xg, yg; /* center of gravity */
// double vx, vy; /* velocity of center of gravity */
// double angle; /* orientation of cluster */
// double omega; /* angular velocity of cluster */
// double mass, mass_inv; /* mass of cluster and its inverse */
// double inertia_moment, inertia_moment_inv; /* moment of inertia */
// double fx, fy, torque; /* force and torque */
// double energy, emean; /* energy and averaged energy */
// double dirmean; /* time-averaged direction */
// int nparticles; /* number of particles in cluster */
// int particle[NMAXPARTINCLUSTER]; /* list of particles in cluster */
return(il);
}
int chem_multi_glue_polygon(int i, int type2, int maxpartners, t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_prob, int mergeclusters)
/* simplified version of chem_multi_glue_molecule without triangle and similar */
/* conditions, but taking polygon geometry into account */
{
int k, p, q, p1, q1, p2, np, nq, n, m, closeby = 0, reaction = 0, jp[NMAXPARTNERS], jq[NMAXPARTNERS], jtot[NMAXPARTNERS], r, kk, different, np2, moli, molk, mp, newpartner, pp, type1, poly_condition, nangle, norient;
double distance, angle, move_factor, xnew, ynew, vxnew, vynew, m1, m2, mr1, mr2, deltav, x, y, ri, rk, alpha, alpha2, ca, rel_angle, rorient, delta, rorient_new, anglei_new, deltax, deltay, x1, y1;
type1 = particle[i].type;
np = particle[i].npartners;
moli = particle[i].molecule;
if (np > maxpartners) return(ncollisions);
m1 = 1.0/particle[i].mass_inv;
for (p=0; p<np; p++)
{
jp[p] = particle[i].partner[p];
m1 += 1.0/particle[jp[p]].mass_inv;
}
alpha = PI/(double)NPOLY;
alpha2 = 2.0*alpha;
ca = cos(alpha);
ri = particle[i].radius*ca;
rk = particle[k].radius*ca;
p = 0;
while ((p<particle[i].hash_nneighb)&&(!reaction))
{
k = particle[i].hashneighbour[p];
nq = particle[k].npartners;
molk = particle[k].molecule;
if (np + nq > maxpartners) return(0);
if ((particle[k].active)&&(particle[k].type == type2)&&(nq <= maxpartners))
{
distance = module2(particle[i].deltax[p], particle[i].deltay[p]);
rel_angle = argument(particle[i].deltax[p], particle[i].deltay[p]);
poly_condition = (distance < REACTION_DIST*(ri+rk));
rorient = particle[k].angle - particle[i].angle;
if (NPOLY%2 == 1) rorient -= alpha;
norient = (int)(rorient/alpha2 + 0.5);
delta = rorient - norient*alpha2;
poly_condition *= (vabs(delta) < DELTAMAX);
if ((poly_condition)&&((double)rand()/RAND_MAX < reaction_prob))
{
/* find nearest orientation facing particle k */
rorient_new = norient*alpha2;
nangle = (int)((particle[i].angle - rel_angle)/alpha2);
anglei_new = rel_angle + (double)(2*nangle+1)*alpha;
m2 = 1.0/particle[k].mass_inv;
for (q=0; q<nq; q++)
{
jq[q] = particle[k].partner[q];
m2 += 1.0/particle[jq[q]].mass_inv;
}
mr1 = m1/(m1 + m2);
mr2 = 1.0 - mr1;
if ((np == 0)&&(nq == 0))
{
reaction = 1;
printf("Merging molecule %i with particle %i\n", i, k);
distance = (ri+rk)*PAIR_DRATIO;
// particle[i].angle = rel_angle - PI/(double)NPOLY;
particle[i].angle = anglei_new;
particle[k].angle = particle[i].angle + rorient_new;
if (NPOLY%2 == 1) particle[k].angle += alpha;
particle[k].xc = particle[i].xc + distance*cos(rel_angle);
particle[k].yc = particle[i].yc + distance*sin(rel_angle);
particle[i].npartners++;
particle[i].partner[0] = k;
particle[i].partner_eqd[0] = distance;
particle[i].partner_eqa[0] = rorient_new;
if (NPOLY%2 == 1) particle[i].partner_eqa[0] += alpha;
particle[k].npartners++;
particle[k].partner[0] = i;
particle[k].partner_eqd[0] = distance;
particle[k].partner_eqa[0] = -rorient_new;
if (NPOLY%2 == 1) particle[k].partner_eqa[0] -= alpha;
/* equalize speeds */
vxnew = mr1*particle[i].vx + mr2*particle[k].vx;
vynew = mr1*particle[i].vy + mr2*particle[k].vy;
particle[i].vx = vxnew;
particle[i].vy = vynew;
particle[k].vx = vxnew;
particle[k].vy = vynew;
if (mergeclusters)
merge_clusters(particle[i].cluster, particle[k].cluster, i, k, particle, cluster, 0, 0);
}
else if (np+nq+2 < NMAXPARTNERS)
{
/* check if i and k belong to different molecules */
different = 1;
for (p1=0; p1<np; p1++)
if (k == jp[p1]) different = 0;
for (q1=0; q1<nq; q1++)
if (i == jq[q1]) different = 0;
if (different)
{
deltav = module2(particle[i].vx - particle[k].vx, particle[i].vy - particle[k].vy);
// printf("Delta v is %.3lg\n", deltav);
if (deltav < DELTAVMAX)
{
reaction = 1;
printf("Pairing clusters containing particles %i and %i\n", i, k);
printf("np = %i, nq = %i\n", np, nq);
fprintf(lj_log, "Pairing clusters containing particles %i and %i\n", i, k);
fprintf(lj_log, "np = %i, nq = %i\n", np, nq);
/* TODO: fix angles */
distance = (ri+rk)*PAIR_DRATIO;
particle[i].angle = anglei_new;
for (p1=0; p1<np; p1++) particle[jp[p1]].angle = anglei_new;
particle[k].angle = particle[i].angle + rorient_new;
if (NPOLY%2 == 1) particle[k].angle += alpha;
/* translate/rotate molecule containing particle k */
deltax = -particle[k].xc + particle[i].xc + distance*cos(rel_angle);
deltay = -particle[k].yc + particle[i].yc + distance*sin(rel_angle);
particle[k].xc += deltax;
particle[k].yc += deltay;
for (q1=0; q1<nq; q1++)
{
particle[jq[q1]].xc += deltax;
particle[jq[q1]].yc += deltay;
particle[jq[q1]].angle = particle[i].angle + rorient_new;
if (NPOLY%2 == 1) particle[jq[q1]].angle += alpha;
}
particle[i].npartners++;
particle[i].partner[np] = k;
particle[i].partner_eqd[np] = distance;
particle[i].partner_eqa[np] = rorient_new;
if (NPOLY%2 == 1) particle[i].partner_eqa[np] += alpha;
for (p1=0; p1<np; p1++)
{
particle[jp[p1]].partner_eqa[p1] = rorient_new;
if (NPOLY%2 == 1) particle[i].partner_eqa[np] += alpha;
}
particle[k].npartners++;
particle[k].partner[nq] = i;
particle[k].partner_eqd[nq] = distance;
particle[k].partner_eqa[nq] = -rorient_new;
if (NPOLY%2 == 1) particle[k].partner_eqa[nq] -= alpha;
/* equalize speeds */
vxnew = mr1*particle[i].vx + mr2*particle[k].vx;
vynew = mr1*particle[i].vy + mr2*particle[k].vy;
particle[i].vx = vxnew;
particle[i].vy = vynew;
particle[k].vx = vxnew;
particle[k].vy = vynew;
/* TODO: fix angles */
for (p1=0; p1<np; p1++) for (q1=0; q1<nq; q1++)
{
particle[jp[p1]].vx = vxnew;
particle[jp[p1]].vy = vynew;
particle[jq[q1]].vx = vxnew;
particle[jq[q1]].vy = vynew;
}
if (mergeclusters)
merge_clusters(particle[i].cluster, particle[k].cluster, i, k, particle, cluster, 0, 0);
}
}
}
if (reaction)
{
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
}
p++;
}
return(ncollisions);
}
void repair_cluster(int cl, t_particle particle[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES], int nsteps, int kill_overlays)
/* repair cluster by letting positions converge to equilibrium position */
{
int t, j, k, p, q, np, nq, m;
double dist, eqdist, fx, fy, dx, dy, r, phi, newphi, newx, newy, eqdist2;
static double alpha, alpha2, ca, sa, kspring;
static int first = 1;
if (first)
{
alpha = PI/(double)NPOLY;
alpha2 = 2.0*alpha;
ca = cos(alpha);
sa = sin(alpha);
kspring = 5000.0;
first = 0;
}
for (t=0; t<nsteps; t++)
{
for (p=0; p<cluster[cl].nparticles; p++)
{
q = cluster[cl].particle[p];
fx = 0.0;
fy = 0.0;
for (j=0; j<particle[q].hash_nneighb; j++)
{
k = particle[q].hashneighbour[j];
if (particle[k].cluster == cl)
{
dx = particle[q].deltax[j];
dy = particle[q].deltay[j];
dist = module2(dx, dy);
eqdist = (particle[k].radius + particle[q].radius)*ca;
if (dist < 1.2*eqdist)
{
if ((NPOLY%2==0)||(particle[q].flip != particle[k].flip))
{
phi = argument(dx, dy);
// printf("phi = %.3lg, angle = %.3lg\t", phi*180.0/PI, particle[q].angle*180.0/PI);
m = (int)(0.5 + (double)NPOLY + (phi - particle[q].angle + alpha)/alpha2) - NPOLY;
newphi = particle[q].angle - alpha + (double)m*alpha2;
// printf("m = %i, newphi = %.3lg\n", m, newphi*180.0/PI);
newx = particle[k].xc - eqdist*cos(newphi);
newy = particle[k].yc - eqdist*sin(newphi);
// printf("(x,y) = (%.3lg, %.3lg) -> (%.3lg, %.3lg)\n", particle[q].xc, particle[q].yc, newx, newy);
dx = newx - particle[q].xc;
dy = newy - particle[q].yc;
r = module2(dx, dy);
// printf("Particle %i distance to target = %.3lg\n", k, r);
// if ((t == nsteps-1)&&(q == 470)&&(k == 472)) fprintf(lj_log, "Particle %i relative distance to target %i = %.5lg\n", q, k, r/eqdist);
fx += r*dx;
fy += r*dy;
}
else
{
eqdist2 = 1.0*(particle[k].radius + particle[q].radius)*sa;
if (dist < eqdist2)
{
fx -= 0.1*(eqdist2 - dist)*dx/dist;
fy -= 0.1*(eqdist2 - dist)*dy/dist;
}
}
}
if ((kill_overlays)&&(dist < REPAIR_MIN_DIST*eqdist))
{
nq = particle[q].npartners;
np = particle[k].npartners;
if (nq == 1) particle[q].active = 0;
if (np == 1) particle[k].active = 0;
// if (np > nq) particle[q].active = 0;
// else particle[k].active = 0;
}
}
}
particle[q].xc += kspring*fx*DT_PARTICLE;
particle[q].yc += kspring*fy*DT_PARTICLE;
}
}
}
int chem_multi_glue_polygon2(int i, int maxpartners, t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_prob, int mergeclusters, int singlecluster)
/* simplified version of chem_multi_glue_molecule without triangle and similar */
/* conditions, but taking polygon geometry into account */
/* version 2, for CHEM_POLYGON_CLUSTER interaction */
{
int k, p, q, p1, q1, p2, np, nq, n, m, closeby = 0, reaction = 0, jp[NMAXPARTNERS], jq[NMAXPARTNERS], jtot[NMAXPARTNERS], r, kk, different, np2, moli, molk, mp, newpartner, pp, type1, poly_condition, nangle, norient, cl1, cl2, cp, cq, newcluster, nmin, clmin, clmax, newreaction;
double distance, angle, move_factor, xnew, ynew, vxnew, vynew, m1, m2, mr1, mr2, deltav, x, y, ri, rk, alpha, alpha2, ca, rel_angle, rorient, delta, rorient_new, anglei_new, deltax, deltay, x1, y1, aratio, delta2, eqdistance, dx, dy, dalpha1, dalpha2, ssize1, ssize2;
static short int first = 1;
static int scluster;
if ((singlecluster)&&(first))
{
scluster = 0;
while (cluster[scluster].active == 0) scluster = rand()%ncircles;
cluster[scluster].selected = 1;
particle[scluster].collision = 1;
printf("Selected cluster %i as single cluster\n", scluster);
first = 0;
}
type1 = particle[i].type;
np = particle[i].npartners;
if (np > maxpartners) return(ncollisions);
cl1 = particle[i].cluster;
cp = cluster[cl1].nparticles;
m1 = cluster[cl1].mass;
alpha = PI/(double)NPOLY;
alpha2 = 2.0*alpha;
ca = cos(alpha);
ri = particle[i].radius*ca;
rk = particle[k].radius*ca;
p = 0;
while ((p<particle[i].hash_nneighb)&&(!reaction))
{
k = particle[i].hashneighbour[p];
nq = particle[k].npartners;
cl2 = particle[k].cluster;
if (cluster[cl1].nparticles < cluster[cl2].nparticles)
{
nmin = np;
clmin = cluster[cl1].nparticles;
clmax = cluster[cl2].nparticles;
}
else
{
nmin = nq;
clmin = cluster[cl2].nparticles;
clmax = cluster[cl1].nparticles;
}
// if (np + nq + 2 > maxpartners) return(ncollisions);
if ((!reaction)&&(particle[k].active)&&(np+nq+1 < maxpartners)&&(cl2!=cl1)&&(nmin <= SMALL_NP_MAXSIZE)&&(clmin <= SMALL_CLUSTER_MAXSIZE))
{
cq = cluster[cl2].nparticles;
// poly_condition = (cl1!=cl2);
/* test distance */
distance = module2(particle[i].deltax[p], particle[i].deltay[p]);
rel_angle = argument(particle[i].deltax[p], particle[i].deltay[p]);
poly_condition = (distance < REACTION_DIST*(ri+rk));
/* test relative angle */
rorient = particle[k].angle - particle[i].angle;
if (NPOLY%2 == 1) rorient -= alpha;
while (rorient > alpha) rorient -= alpha2;
while (rorient < -alpha) rorient += alpha2;
poly_condition *= (vabs(rorient) < DELTAMAX);
// norient = (int)(rorient/alpha2 + 0.5 + 2.0*(double)NPOLY);
// delta = rorient - (double)(norient-2*NPOLY)*alpha2;
// poly_condition *= (vabs(delta) < DELTAMAX);
/* test orientation */
aratio = 0.5*vabs(rel_angle)/alpha;
delta2 = aratio - (double)((int)aratio);
poly_condition *= (vabs(delta2 - 0.5) < 0.5*DELTAMAX*alpha);
/* option singlecluster: only allow merger if one of the clusters is selected cluster */
if (singlecluster) poly_condition *= ((cluster[cl1].selected)||(cluster[cl2].selected)||(clmax <= NOTSELECTED_CLUSTER_MAXSIZE));
newreaction = 0;
if ((poly_condition)&&((double)rand()/RAND_MAX < reaction_prob))
{
/* find nearest orientation facing particle k */
rorient_new = norient*alpha2;
nangle = (int)((particle[i].angle - rel_angle)/alpha2);
anglei_new = rel_angle + (double)(2*nangle+1)*alpha;
m2 = cluster[cl2].mass;
mr1 = m1/(m1 + m2);
mr2 = 1.0 - mr1;
if ((np == 0)&&(nq == 0))
{
reaction = 1;
newreaction = 1;
printf("Merging molecule %i with particle %i\n", i, k);
eqdistance = ri + rk;
particle[i].angle = anglei_new;
particle[k].angle = particle[i].angle + rorient_new;
if (NPOLY%2 == 1) particle[k].angle += alpha;
dx = 0.5*(distance - eqdistance)*cos(rel_angle);
dy = 0.5*(distance - eqdistance)*sin(rel_angle);
translate_cluster(cl1, cluster, particle, dx, dy);
translate_cluster(cl2, cluster, particle, -dx, -dy);
particle[i].npartners++;
particle[i].partner[0] = k;
particle[k].npartners++;
particle[k].partner[0] = i;
/* equalize speeds */
vxnew = mr1*particle[i].vx + mr2*particle[k].vx;
vynew = mr1*particle[i].vy + mr2*particle[k].vy;
particle[i].vx = vxnew;
particle[i].vy = vynew;
particle[k].vx = vxnew;
particle[k].vy = vynew;
if (mergeclusters)
newcluster = merge_clusters(particle[i].cluster, particle[k].cluster, i, k, particle, cluster, 0, (particle[i].flip == particle[k].flip));
}
// else if ((np+nq+2 < NMAXPARTNERS)&&(vabs(delta) < 0.5*DELTAMAX))
else if ((np+nq+2 < NMAXPARTNERS)&&(cp+cq <= CLUSTER_MAXSIZE))
{
deltav = module2(particle[i].vx - particle[k].vx, particle[i].vy - particle[k].vy);
// printf("Delta v is %.3lg\n", deltav);
if (deltav < DELTAVMAX)
{
reaction = 1;
newreaction = 1;
printf("Pairing clusters containing particles %i and %i\n", i, k);
printf("np = %i, nq = %i\n", np, nq);
fprintf(lj_log, "Pairing clusters containing particles %i and %i\n", i, k);
fprintf(lj_log, "np = %i, nq = %i\n", np, nq);
eqdistance = ri + rk;
dx = 0.5*(distance - eqdistance)*cos(rel_angle);
dy = 0.5*(distance - eqdistance)*sin(rel_angle);
dalpha1 = anglei_new - particle[i].angle;
dalpha2 = anglei_new + rorient_new - particle[k].angle;
if (NPOLY%2 == 1) dalpha2 += alpha;
/* round to closest multiple of alpha2 */
while (dalpha1 > 0.5*DELTAMAX) dalpha1 -= alpha2;
while (dalpha1 < -0.5*DELTAMAX) dalpha1 += alpha2;
while (dalpha2 > 0.5*DELTAMAX) dalpha2 -= alpha2;
while (dalpha2 < -0.5*DELTAMAX) dalpha2 += alpha2;
translate_cluster(cl1, cluster, particle, dx, dy);
translate_cluster(cl2, cluster, particle, -dx, -dy);
ssize1 = 0.02*sqrt((double)cluster[cl1].nparticles);
ssize2 = 0.02*sqrt((double)cluster[cl2].nparticles);
if (vabs(dalpha1) + ssize1 < 0.5*DELTAMAX)
rotate_cluster_around_particle(cl1, i, cluster, particle, dalpha1);
else printf("Did not rotate cluster of size %.3lg\n", ssize1);
if (vabs(dalpha2) + ssize2 < 0.5*DELTAMAX)
rotate_cluster_around_particle(cl2, k, cluster, particle, dalpha2);
else printf("Did not rotate cluster of size %.3lg\n", ssize2);
particle[i].npartners++;
particle[i].partner[np] = k;
particle[k].npartners++;
particle[k].partner[nq] = i;
/* equalize speeds */
vxnew = mr1*particle[i].vx + mr2*particle[k].vx;
vynew = mr1*particle[i].vy + mr2*particle[k].vy;
for (p1=0; p1<cluster[cl1].nparticles; p1++)
{
q = cluster[cl1].particle[p1];
particle[q].vx = vxnew;
particle[q].vy = vynew;
}
for (p1=0; p1<cluster[cl2].nparticles; p1++)
{
q = cluster[cl2].particle[p1];
particle[q].vx = vxnew;
particle[q].vy = vynew;
}
if (mergeclusters)
{
newcluster = merge_clusters(particle[i].cluster, particle[k].cluster, i, k, particle, cluster, 1, (particle[i].flip == particle[k].flip));
}
}
}
if ((newreaction)&&(singlecluster)&&((cluster[cl1].selected)||(cluster[cl2].selected)))
{
scluster = newcluster;
printf("[chem_multi_glue_polygon2] cluster 1 = %i, cluster 2 = %i, newcluster = %i\n", cl1, cl2, newcluster);
cluster[cl1].selected = 1;
cluster[cl2].selected = 1;
// cluster[newcluster].selected = 1;
printf("[chem_multi_glue_polygon2] Selected cluster: %i\n", scluster);
}
if (reaction)
{
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = 0.0;
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
}
// else if ((nmin > SMALL_NP_MAXSIZE)||(clmin > SMALL_CLUSTER_MAXSIZE))
// {
// printf("No merger - Parameters: nmin = %i, clmin = %i\n", nmin, clmin);
// }
p++;
}
return(ncollisions);
}
int chem_split_molecule(int i, t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_collision *collisions, int ncollisions)
/* split molecule containing particle i from all other molecules */
{
int np, nmolp, mol, n, m, p, q, r, mm;
int m_table[NMAXPARTNERMOLECULES];
short int reaction = 0;
np = particle[i].npartners;
if (np==0) return(ncollisions);
mol = particle[i].molecule;
nmolp = molecule[mol].npartners;
printf("Molecule %i has %i partners: ", mol, nmolp);
fprintf(lj_log, "[chem_split_molecule] Molecule %i has %i partners: ", mol, nmolp);
for (mm=0; mm<nmolp; mm++)
{
m_table[mm] = molecule[mol].partner[mm];
printf("%i ", m_table[mm]);
fprintf(lj_log, "%i ", m_table[mm]);
}
printf("\n");
fprintf(lj_log, "\n");
for (p=0; p<np; p++)
{
n = particle[i].partner[p];
if (particle[n].molecule != mol)
{
dissociate_particles(i, n, p, particle);
reaction = 1;
}
}
np = particle[i].npartners;
for (p=0; p<np; p++)
{
for (q=0; q<np; q++)
{
n = particle[i].partner[p];
for (r=0; r<particle[n].npartners; r++)
{
m = particle[n].partner[r];
if (particle[m].molecule != mol)
{
dissociate_particles(n, m, r, particle);
reaction = 1;
}
}
}
}
if (reaction)
{
update_single_molecule_data(mol, particle, molecule);
for (mm=0; mm<nmolp; mm++)
update_single_molecule_data(m_table[mm], particle, molecule);
printf("[chem_split_molecule] - dissociating molecule %i\n", mol);
fprintf(lj_log, "[chem_split_molecule] - dissociating molecule %i\n", mol);
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(particle[i].type);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
return(ncollisions);
}
int chem_split_molecule_if_nearby(int i, int type, t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_dist, double reaction_prob)
/* split molecules containing i if i is close to particle of type type */
{
int p, k, mol;
double distance;
short int reaction = 0;
mol = particle[i].molecule;
p = 0;
while ((p<particle[i].hash_nneighb)&&(!reaction))
{
k = particle[i].hashneighbour[p];
if ((particle[k].type == type)&&(particle[k].molecule != mol))
{
distance = module2(particle[i].xc - particle[k].xc, particle[i].yc - particle[k].yc);
if ((distance < reaction_dist)&&((double)rand()/RAND_MAX < reaction_prob))
{
chem_split_molecule(i, particle, molecule, collisions, ncollisions);
reaction = 1;
}
}
p++;
}
if (reaction)
{
printf("dissociating molecule %i\n", mol);
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(particle[i].type);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
return(ncollisions);
}
int chem_local_split_molecule_if_nearby(int i, int type, int enzyme_type, t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_collision *collisions, int ncollisions, double reaction_dist, double reaction_prob)
/* split atoms in molecule containing i from type type, if i is close to particle of type enzyme_type */
{
int p, k, moli, molk, mol_diss, q, n, q1, m, added, mp, pp;
double distance;
short int reaction = 0, reaction1 = 0;
moli = particle[i].molecule;
p = 0;
while ((p<particle[i].hash_nneighb)&&(!reaction))
{
reaction1 = 0;
k = particle[i].hashneighbour[p];
molk = particle[k].molecule;
added = particle[i].added;
// added = 0;
if ((added == 0)&&(particle[k].type == enzyme_type)&&(molk != moli))
{
distance = module2(particle[i].xc - particle[k].xc, particle[i].yc - particle[k].yc);
if ((distance < reaction_dist)&&((double)rand()/RAND_MAX < reaction_prob))
{
if (particle[i].coulomb < 5) particle[i].coulomb = 5;
particle[i].reactive = 0;
for (q=0; q<particle[i].npartners; q++)
{
n = particle[i].partner[q];
if (particle[n].added == 0)
{
if (particle[n].type == type)
{
mol_diss = particle[n].molecule;
dissociate_particles(i, n, q, particle);
if (particle[n].coulomb < 5) particle[n].coulomb = 5;
reaction = 1;
reaction1 = 1;
}
else if (particle[n].type == particle[i].type)
{
for (q1=0; q1<particle[n].npartners; q1++)
{
m = particle[n].partner[q1];
if ((particle[m].type == type)&&(particle[m].added == 0))
{
mol_diss = particle[m].molecule;
dissociate_particles(n, m, q1, particle);
if (particle[n].coulomb < 5) particle[n].coulomb = 5;
if (particle[m].coulomb < 5) particle[m].coulomb = 5;
particle[n].reactive = 0;
particle[m].reactive = 0;
reaction = 1;
reaction1 = 1;
}
}
}
}
}
/* TEST */
if ((RD_REACTION == CHEM_DNA_ENZYME)||(RD_REACTION == CHEM_DNA_ENZYME_REPAIR))
{
if ((particle[i].type >= 3)&&(particle[i].type <= 6))
{
particle[i].paired = 0;
for (q1 = 0; q1 <= particle[i].npartners; q1++)
{
n = particle[i].partner[q1];
particle[n].paired = 0;
}
particle[k].paired = 0;
for (q1 = 0; q1 <= particle[k].npartners; q1++)
{
n = particle[k].partner[q1];
particle[n].paired = 0;
}
}
}
}
/* update molecule data */
if ((reaction1)&&(moli != mol_diss))
{
printf("Splitting molecules %i and %i\n", moli, mol_diss);
mp = molecule[moli].npartners;
pp = 0;
while (molecule[moli].partner[pp] != mol_diss) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[moli].partner[pp] = molecule[moli].partner[pp+1];
molecule[moli].connection_type[pp] = molecule[moli].connection_type[pp+1];
pp++;
}
molecule[moli].npartners--;
}
mp = molecule[mol_diss].npartners;
pp = 0;
while (molecule[mol_diss].partner[pp] != moli) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[mol_diss].partner[pp] = molecule[mol_diss].partner[pp+1];
molecule[mol_diss].connection_type[pp] = molecule[mol_diss].connection_type[pp+1];
pp++;
}
molecule[mol_diss].npartners--;
}
// sleep(3);
}
}
p++;
}
if (reaction)
{
printf("dissociating molecule %i\n", moli);
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(particle[i].type);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
return(ncollisions);
}
void change_type_proportion(t_particle particle[NMAXCIRCLES], double prop)
/* change proportion of particles of types 1 or 2 */
{
int i, n0=0, n1=0, n2=0, ntot, nc=0, nmod, counter=0, cmax=1000;
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].type == 0) n0++;
if (particle[i].type == 1) n1++;
if (particle[i].type == 2) n2++;
}
ntot = n0 + n1 + n2;
n0 += n1;
if ((double)(n0+1) < prop*(double)ntot)
{
nmod = (int)((double)ntot*prop - (double)n0);
if (nmod > 0) while ((nc<nmod)&&(counter<cmax))
{
i = rand()%ncircles;
if ((particle[i].type == 2)&&(particle[i].active))
{
particle[i].type = 1;
particle[i].radius = MU;
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;
particle[i].charge = CHARGE;
nc++;
}
counter++;
}
}
else if ((double)(n0-1) > prop*(double)ntot)
{
nmod = (int)((double)n0 - (double)ntot*prop);
if (nmod > 0) while ((nc<nmod)&&(counter<cmax))
{
i = rand()%ncircles;
if ((particle[i].type <= 1)&&(particle[i].active))
{
particle[i].type = 2;
particle[i].radius = MU_B;
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].charge = CHARGE_B;
nc++;
}
counter++;
}
}
}
int find_partners(t_particle *particle, int position, t_particle* *pstack, int *stacksize, int cluster)
/* look for active partners of particle[position], returns difference in partners */
{
int k, nopen = 0, n;
if (!pstack[position]->active) return(-1);
pstack[position]->cluster = cluster;
for (k=0; k<pstack[position]->npartners; k++)
{
n = pstack[position]->partner[k];
if ((particle[n].active)&&(particle[n].cluster != cluster))
{
particle[n].tested = 1;
particle[n].cluster = cluster;
particle[n].cactive = 1;
nopen++;
if (*stacksize < ncircles)
{
(*stacksize)++;
pstack[*stacksize-1] = &particle[n];
}
}
}
if (nopen == 0)
{
pstack[position]->cactive = 0;
return(-1);
}
else return(nopen);
}
int update_cluster_color(t_particle particle[NMAXCIRCLES])
/* update the colors of clusters */
{
int i, k, p, position, nactive, stacksize, nclusters = 0;
t_particle **cstack;
cstack = (t_particle* *)malloc(ncircles*sizeof(struct t_particle *));
#pragma omp parallel for private(i)
for (i=0; i<ncircles; i++) particle[i].tested = 0;
for (i=0; i<ncircles; i++) if ((particle[i].active)&&(!particle[i].tested))
{
position = 0;
stacksize = 1;
nactive = 1;
cstack[0] = &particle[i];
particle[i].cactive = 1;
particle[i].tested = 1;
/* do a depth first search starting in i */
while (nactive > 0)
{
while (!cstack[position]->cactive) position++;
if (position == stacksize) position = 0;
/* find an active partner in stack */
nactive += find_partners(particle, position, cstack, &stacksize, particle[i].cluster);
}
nclusters++;
}
free(cstack);
return(nclusters);
}
int repair_dna(t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_collision *collisions, int ncollisions)
/* repair unwanted connections in DNA, for CHEM_DNA_ENZYME_REPAIR reaction type */
{
int i, mol, moli, j, molj, p, q, p1, q1, molp, molpp, molq, molqq, r, type, type1, type2, type3, type4, np, nq, pp, qq, mp, split, common_partner, smol1, smol2, npartners;
int p_table[NMAXPARTNERMOLECULES], q_table[NMAXPARTNERMOLECULES];
double dist;
printf("Repairing DNA\n");
/* Prevent single base from connecting to two different molecules */
for (moli=0; moli<nmolecules; moli++) if (molecule[moli].added)
{
split = 0;
/* search for double connections */
for (j=0; j<molecule[moli].npartners; j++)
{
molj = molecule[moli].partner[j];
type1 = molecule[moli].connection_type[j];
if (type1 >= 3)
{
for (p=0; p<molecule[moli].npartners; p++)
{
molp = molecule[moli].partner[p];
type2 = molecule[moli].connection_type[p];
if ((type2 >= 3)&&(molp != molj))
{
smol1 = molj;
smol2 = molp;
split = 1;
}
}
}
}
if (split)
{
printf("Splitting molecule %i from molecules %i and %i\n\n\n", moli, smol1, smol2);
for (p=0; p<molecule[moli].nparticles; p++)
{
p1 = molecule[moli].particle[p];
type = particle[p1].type;
if (type >= 3)
{
i = p1;
for (q=0; q<particle[p1].npartners; q++)
{
q1 = particle[p1].partner[q];
if (particle[q1].molecule == molj)
dissociate_particles(p1, q1, q, particle);
}
}
}
for (q=0; q<molecule[smol1].nparticles; q++)
{
q1 = molecule[smol1].particle[q];
type = particle[q1].type;
if (type >= 3)
for (p=0; p<particle[q1].npartners; p++)
{
p1 = particle[q1].partner[p];
if (particle[p1].molecule == moli)
dissociate_particles(q1, p1, p, particle);
}
}
for (q=0; q<molecule[smol2].nparticles; q++)
{
q1 = molecule[smol2].particle[q];
type = particle[q1].type;
if (type >= 3)
for (p=0; p<particle[q1].npartners; p++)
{
p1 = particle[q1].partner[p];
if (particle[p1].molecule == moli)
dissociate_particles(q1, p1, p, particle);
}
}
/* update molecule data */
mp = molecule[moli].npartners;
pp = 0;
while (molecule[moli].partner[pp] != smol1) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[moli].partner[pp] = molecule[moli].partner[pp+1];
molecule[moli].connection_type[pp] = molecule[moli].connection_type[pp+1];
pp++;
}
molecule[moli].npartners--;
}
pp = 0;
while (molecule[moli].partner[pp] != smol2) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[moli].partner[pp] = molecule[moli].partner[pp+1];
molecule[moli].connection_type[pp] = molecule[moli].connection_type[pp+1];
pp++;
}
molecule[moli].npartners--;
}
mp = molecule[smol1].npartners;
pp = 0;
while (molecule[smol1].partner[pp] != moli) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[smol1].partner[pp] = molecule[smol1].partner[pp+1];
molecule[smol1].connection_type[pp] = molecule[smol1].connection_type[pp+1];
pp++;
}
molecule[smol1].npartners--;
}
mp = molecule[smol2].npartners;
pp = 0;
while (molecule[smol2].partner[pp] != moli) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[smol2].partner[pp] = molecule[smol2].partner[pp+1];
molecule[smol2].connection_type[pp] = molecule[smol2].connection_type[pp+1];
pp++;
}
molecule[smol2].npartners--;
}
printf("dissociating molecule %i\n", moli);
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(particle[i].type);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
/* Test for skipped bases */
/* if moli and molj are paired molecules, check whether they form a "square" */
/* i.e. a partner of a partner of moli is a partner of molj */
/* taking the connection types into account */
for (moli=0; moli<nmolecules; moli++) if (molecule[moli].added)
for (j=0; j<molecule[moli].npartners; j++)
{
molj = molecule[moli].partner[j];
type1 = molecule[moli].connection_type[j];
type2 = 3 - type1;
if ((molecule[molj].added)&&((type1 == 1)||(type1 == 2))) /* i-j connection is a backbone */
{
/* find base-neighbours for moli (normally there should be at most one) */
np = 0;
for (p=0; p<molecule[moli].npartners; p++)
{
molp = molecule[moli].partner[p];
type3 = molecule[moli].connection_type[p];
if (type3 >= 3)
{
p_table[np] = molp;
np++;
}
}
/* find base-neighbours for molj (normally there should be at most one) */
nq = 0;
for (q=0; q<molecule[molj].npartners; q++)
{
molq = molecule[molj].partner[q];
type3 = molecule[molj].connection_type[q];
// if ((molq != molj)&&(type3 >= 3))
if (type3 >= 3)
{
q_table[nq] = molq;
nq++;
}
}
/* test for split, split occurs if each molecule has a base-neighbour,
and they are not neighbours */
if ((np == 0)||(nq == 0)) split = 0;
else
{
split = 1;
printf("Base partners of molecule %i(%i): ", moli, type1);
for (p=0; p<np; p++) printf("%i ", p_table[p]);
printf("\n");
printf("Base partners of molecule %i(%i): ", molj, type2);
for (q=0; q<nq; q++) printf("%i ", q_table[q]);
printf("\n");
for (p=0; p<np; p++)
{
molp = p_table[p];
for (q=0; q<nq; q++)
{
molq = q_table[q];
printf("Checking base partners %i and %i\n", molp, molq);
printf("type1 = %i, type2 = %i\n", type1, type2);
printf("Molecule %i has %i partners: ", molp, molecule[molp].npartners);
for (pp = 0; pp < molecule[molp].npartners; pp++)
printf("%i(%i) ", molecule[molp].partner[pp], molecule[molp].connection_type[pp]);
printf("\n");
printf("Molecule %i has %i partners: ", molq, molecule[molq].npartners);
for (pp = 0; pp < molecule[molq].npartners; pp++)
printf("%i(%i) ", molecule[molq].partner[pp], molecule[molq].connection_type[pp]);
printf("\n");
for (pp = 0; pp < molecule[molp].npartners; pp++)
{
molpp = molecule[molp].partner[pp];
type4 = molecule[molp].connection_type[pp];
printf("Testing molecule %i(%i)\n", molpp, type4);
if ((type4 == type2)&&(molpp == molq))
split = 0;
}
}
}
}
if (split) /* break backbone link between i and j */
{
printf("Splitting molecules %i and %i\n\n\n", moli, molj);
for (p=0; p<molecule[moli].nparticles; p++)
{
p1 = molecule[moli].particle[p];
type = particle[p1].type;
if (type == type1)
i = p1;
for (q=0; q<particle[p1].npartners; q++)
{
q1 = particle[p1].partner[q];
if (particle[q1].molecule == molj)
dissociate_particles(p1, q1, q, particle);
}
}
for (q=0; q<molecule[molj].nparticles; q++)
{
q1 = molecule[molj].particle[q];
type = particle[q1].type;
if (type == type2)
for (p=0; p<particle[q1].npartners; p++)
{
p1 = particle[q1].partner[p];
if (particle[p1].molecule == moli)
dissociate_particles(q1, p1, p, particle);
}
}
// printf("Splitting molecules %i and %i\n", moli, mol_diss);
/* update molecule data */
mp = molecule[moli].npartners;
pp = 0;
while (molecule[moli].partner[pp] != molj) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[moli].partner[pp] = molecule[moli].partner[pp+1];
molecule[moli].connection_type[pp] = molecule[moli].connection_type[pp+1];
pp++;
}
molecule[moli].npartners--;
}
mp = molecule[molj].npartners;
pp = 0;
while (molecule[molj].partner[pp] != moli) pp++;
if (pp < mp)
{
while (pp < mp-1)
{
molecule[molj].partner[pp] = molecule[molj].partner[pp+1];
molecule[molj].connection_type[pp] = molecule[molj].connection_type[pp+1];
pp++;
}
molecule[molj].npartners--;
}
printf("dissociating molecule %i\n", moli);
collisions[ncollisions].x = particle[i].xc;
collisions[ncollisions].y = particle[i].yc;
collisions[ncollisions].time = COLLISION_TIME;
collisions[ncollisions].color = type_hue(particle[i].type);
if (ncollisions < 2*NMAXCOLLISIONS - 1) ncollisions++;
else printf("Too many collisions\n");
}
}
}
return(ncollisions);
}
int check_dna_pairing(t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_collision *collisions, int ncollisions)
/* repair unwanted connections in DNA, for CHEM_DNA_ENZYME_REPAIR reaction type */
{
int i, moli, j, type1, molj, p, pp, q, qq, deltatype;
short int split;
double dist;
printf("Checking DNA pairing\n");
// fprintf(lj_log, "Checking DNA pairing\n");
/* Test for wrong connections */
/* On rare occasions, it happens that a base pair tries to connect, but the connection fails */
/* However, the nucleotides are still recorded as connected */
/* This part tests for such falsely recorded connections based on the distance, and removes them */
for (moli=0; moli<nmolecules; moli++) if (molecule[moli].added)
{
for (j=0; j<molecule[moli].npartners; j++)
{
split = 0;
molj = molecule[moli].partner[j];
type1 = molecule[moli].connection_type[j];
/* search for wrongly recorded connections */
for (p=0; p<molecule[moli].nparticles; p++)
{
pp = molecule[moli].particle[p];
if (particle[pp].type == type1)
{
for (q=0; q<particle[p].npartners; q++)
{
qq = particle[p].partner[q];
deltatype = particle[qq].type - type1;
if (deltatype < 0) deltatype *= -1;
// if ((particle[qq].molecule == molj)&&(particle[qq].type >= 1))
if ((particle[qq].molecule == molj)&&(deltatype == 1))
{
dist = module2(particle[pp].xc - particle[qq].xc, particle[pp].yc - particle[qq].yc);
// if (dist > 10.0*MU)
// if (dist > 15.0*MU)
if (dist > 20.0*MU)
{
split = 1;
i = pp;
fprintf(lj_log, "\n\n\n Time = %i\n", frame_time);
fprintf(lj_log, "Particles %i(%i) in molecule %i and %i(%i) in molecule %i are at distance %.3lg\n", pp, particle[pp].type, moli, qq, particle[qq].type, molj, dist);
}
}
}
}
}
if (split)
{
printf("Splitting molecule %i from molecule %i because of wrong connection\n", moli, molj);
fprintf(lj_log, "Splitting molecule %i from molecule %i because of wrong connection\n", moli, molj);
ncollisions = chem_split_molecule(i, particle, molecule, collisions, ncollisions);
}
}
}
return(ncollisions);
}
int update_types(t_particle particle[NMAXCIRCLES], t_molecule molecule[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES], t_collision *collisions, int ncollisions, int *particle_numbers, int time, double *delta_e)
/* update the types in case of reaction-diffusion equation */
{
int i, j, k, n, n3, n4, p, type, atype, btype, oldncollisions, delta_n;
short int reacted;
double distance, rnd, p1, p2, reac_dist;
static double inv_masses[RD_TYPES+1], radii[RD_TYPES+1];
static int first = 1, repair_counter = 0;
if (first) /* compute total mass and mass ratios */
{
compute_inverse_masses(inv_masses);
compute_radii(radii);
first = 0;
}
if (EXOTHERMIC) oldncollisions = ncollisions;
switch (RD_REACTION) {
case (CHEM_RPS):
{
for (i=0; i<ncircles; i++)
for (j=0; j<particle[i].hash_nneighb; j++)
{
k = particle[i].hashneighbour[j];
if ((particle[k].type == particle[i].type + 1)||((particle[i].type == RD_TYPES)&&(particle[k].type == 1)))
{
distance = module2(particle[i].deltax[j], particle[i].deltay[j]);
if ((distance < EQUILIBRIUM_DIST)&&((double)rand()/RAND_MAX < REACTION_PROB))
particle[k].type = particle[i].type;
}
}
return(0);
}
case (CHEM_AAB):
{
for (i=0; i<ncircles; i++)
if ((particle[i].active)&&(particle[i].type == 1))
ncollisions = chem_merge_AAB(i, 2, particle, collisions, ncollisions, inv_masses, radii);
return(ncollisions);
}
case (CHEM_ABC):
{
for (i=0; i<ncircles; i++) if ((particle[i].active)&&(particle[i].type == 1))
ncollisions = chem_merge(i, 2, 3, particle, collisions, ncollisions, inv_masses, radii);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
if (EXOTHERMIC) *delta_e = (double)(delta_n)*DELTA_EKIN;
return(ncollisions);
}
case (CHEM_A2BC):
{
for (i=0; i<ncircles; i++) if ((particle[i].active)&&(particle[i].type == 1))
ncollisions = chem_multi_merge(i, 2, 2, 3, particle, collisions, ncollisions, inv_masses, radii);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_CATALYSIS):
{
for (i=0; i<ncircles; i++) if ((particle[i].active)&&(particle[i].type == 1))
ncollisions = chem_catalytic_merge(i, 2, 3, particle, collisions, ncollisions, inv_masses, radii);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_AUTOCATALYSIS):
{
for (i=0; i<ncircles; i++) if ((particle[i].active)&&(particle[i].type == 1))
ncollisions = chem_catalytic_merge(i, 2, 2, particle, collisions, ncollisions, inv_masses, radii);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_BAA):
{
for (i=0; i<ncircles; i++)
if ((particle[i].active)&&(particle[i].type == 2)&&((double)rand()/RAND_MAX < DISSOCIATION_PROB))
ncollisions = chem_split(i, 1, 1, particle, collisions, ncollisions, inv_masses, radii);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_AABAA):
{
*delta_e = 0.0;
for (i=0; i<ncircles; i++)
{
oldncollisions = ncollisions;
if ((particle[i].active)&&(particle[i].type == 1))
{
ncollisions = chem_merge_AAB(i, 2, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e += (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
else if ((particle[i].active)&&(particle[i].type == 2)&&((double)rand()/RAND_MAX < DISSOCIATION_PROB))
{
ncollisions = chem_split(i, 1, 1, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e -= (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
}
printf("Delta_E = %.3lg\n", *delta_e);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_POLYMER):
{
for (i=0; i<ncircles; i++) if ((particle[i].active)&&(particle[i].type == 1))
for (k=2; k<RD_TYPES; k++)
ncollisions = chem_merge(i, k, k+1, particle, collisions, ncollisions, inv_masses, radii);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_POLYMER_DISS):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].type == 1) for (k=2; k<RD_TYPES; k++)
ncollisions = chem_merge(i, k, k+1, particle, collisions, ncollisions, inv_masses, radii);
else if ((particle[i].type > 2)&&((double)rand()/RAND_MAX < DISSOCIATION_PROB))
ncollisions = chem_split(i, 1, particle[i].type - 1, particle, collisions, ncollisions, inv_masses, radii);
}
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_POLYMER_STEP):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
type = particle[i].type;
for (k=1; k<RD_TYPES+1-type; k++)
ncollisions = chem_merge(i, k, k+type, particle, collisions, ncollisions, inv_masses, radii);
if ((type > 1)&&((double)rand()/RAND_MAX < DISSOCIATION_PROB))
{
k = rand()%(type-1) + 1;
// printf("Splitting type %i into type %i and type %i\n", type, k, type - k);
ncollisions = chem_split(i, k, type - k, particle, collisions, ncollisions, inv_masses, radii);
}
}
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_CATALYTIC_A2D):
{
for (i=0; i<ncircles; i++) if ((particle[i].active)&&(particle[i].type == 1))
{
ncollisions = chem_merge(i, 2, 3, particle, collisions, ncollisions, inv_masses, radii);
ncollisions = chem_transfer(i, 3, 4, 2, particle, collisions, ncollisions, inv_masses, radii, 1.05*REACTION_DIST);
}
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_ABCAB):
{
*delta_e = 0.0;
for (i=0; i<ncircles; i++)
{
oldncollisions = ncollisions;
if ((particle[i].active)&&(particle[i].type == 1))
{
ncollisions = chem_merge(i, 2, 3, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e += (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
else if ((particle[i].active)&&(particle[i].type == 3)&&((double)rand()/RAND_MAX < DISSOCIATION_PROB))
{
ncollisions = chem_split(i, 1, 2, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e -= (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
}
printf("Delta_E = %.3lg\n", *delta_e);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_ABCDABC):
{
*delta_e = 0.0;
for (i=0; i<ncircles; i++)
{
oldncollisions = ncollisions;
if ((particle[i].active)&&(particle[i].type == 1))
{
ncollisions = chem_merge(i, 2, 3, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e += (double)(ncollisions - oldncollisions)*DELTA_EKIN;
ncollisions = chem_merge(i, 3, 4, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e += (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
else if ((particle[i].active)&&(particle[i].type == 3)&&((double)rand()/RAND_MAX < DISSOCIATION_PROB))
{
ncollisions = chem_split(i, 1, 2, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e -= (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
else if ((particle[i].active)&&(particle[i].type == 4)&&((double)rand()/RAND_MAX < DISSOCIATION_PROB))
{
ncollisions = chem_split(i, 1, 3, particle, collisions, ncollisions, inv_masses, radii);
if (EXOTHERMIC) *delta_e -= (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
}
printf("Delta_E = %.3lg\n", *delta_e);
printf("%i collisions\n", ncollisions);
return(ncollisions);
}
case (CHEM_BZ):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
oldncollisions = ncollisions;
switch (particle[i].type) {
case (1):
{
ncollisions = chem_transfer(i, 4, 2, 3, particle, collisions, ncollisions, inv_masses, radii, REACTION_DIST);
ncollisions = chem_transfer(i, 3, 2, 2, particle, collisions, ncollisions, inv_masses, radii, REACTION_DIST);
break;
}
case (2):
{
// particle[i].active = 0;
ncollisions = chem_transfer(i, 2, 1, 3, particle, collisions, ncollisions, inv_masses, radii, 0.95*REACTION_DIST);
break;
}
case (3):
{
ncollisions = chem_transfer(i, 3, 2, 4, particle, collisions, ncollisions, inv_masses, radii, REACTION_DIST);
ncollisions = chem_transfer(i, 4, 7, 8, particle, collisions, ncollisions, inv_masses, radii, REACTION_DIST);
break;
}
case (5):
{
rnd = (double)rand()/RAND_MAX;
if (rnd < 0.2)
ncollisions = chem_merge(i, 6, 1, particle, collisions, ncollisions, inv_masses, radii);
else /*if (rnd < 0.9)*/
ncollisions = chem_merge(i, 6, 6, particle, collisions, ncollisions, inv_masses, radii);
// else if (rnd < 0.6)
// ncollisions = chem_transfer(i, 6, 1, 1, particle, collisions, ncollisions, inv_masses, radii, REACTION_DIST);
break;
}
case (7):
{
ncollisions = chem_split(i, 3, 3, particle, collisions, ncollisions, inv_masses, radii);
break;
}
case (8):
{
ncollisions = chem_split(i, 5, 5, particle, collisions, ncollisions, inv_masses, radii);
break;
}
}
}
return(ncollisions);
}
case (CHEM_BRUSSELATOR):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
oldncollisions = ncollisions;
switch (particle[i].type) {
case (1): /* A -> X */
{
ncollisions = chem_convert(i, 3, particle, collisions, ncollisions, inv_masses, radii, DISSOCIATION_PROB);
break;
}
case (2): /* B + X -> Y + D */
{
ncollisions = chem_transfer(i, 3, 4, 5, particle, collisions, ncollisions, inv_masses, radii, REACTION_DIST);
break;
}
case (3):
{
/* X -> B, modified from Brusselator */
ncollisions = chem_convert(i, 2, particle, collisions, ncollisions, inv_masses, radii, 0.5*DISSOCIATION_PROB);
/* 2X + Y -> 3X */
ncollisions = chem_catalytic_convert(i, 4, 3, particle, collisions, ncollisions, inv_masses, radii, 3.0*REACTION_DIST, 1.0);
break;
}
case (5): /* D -> A or B if concentration of X or Y is small */
{
n = time*(RD_TYPES+1);
n3 = particle_numbers[n+3];
n4 = particle_numbers[n+4];
p1 = 1.0/((double)(n3*n3/10+1));
p2 = 5.0*DISSOCIATION_PROB + 1.0/((double)(n4*n4/200+1));
rnd = (double)rand()/RAND_MAX;
if (rnd < p1/(p1+p2))
ncollisions = chem_convert(i, 1, particle, collisions, ncollisions, inv_masses, radii, p1);
else /*if (n4 < 1000)*/
ncollisions = chem_convert(i, 2, particle, collisions, ncollisions, inv_masses, radii, p2);
}
}
}
return(ncollisions);
}
case (CHEM_ABDACBE):
{
for (i=0; i<ncircles; i++)
{
// oldncollisions = ncollisions;
if ((particle[i].active)&&(particle[i].type == 1))
{
ncollisions = chem_merge(i, 2, 4, particle, collisions, ncollisions, inv_masses, radii);
// if (EXOTHERMIC) *delta_e += (double)(ncollisions - oldncollisions)*DELTA_EKIN;
ncollisions = chem_transfer(i, 3, 2, 5, particle, collisions, ncollisions, inv_masses, radii, REACTION_DIST);
// if (EXOTHERMIC) *delta_e += (double)(ncollisions - oldncollisions)*DELTA_EKIN;
}
}
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
if (EXOTHERMIC) *delta_e = (double)(delta_n)*DELTA_EKIN;
return(ncollisions);
}
case (CHEM_H2O_H_OH):
{
for (i=0; i<ncircles; i++)
{
if ((particle[i].active)&&(particle[i].type <= 1))
{
if (particle[i].npartners == 2)
ncollisions = chem_dissociate_molecule(i, particle, collisions, ncollisions, DISSOCIATION_PROB);
else if (particle[i].npartners == 1)
ncollisions = chem_merge_molecule(i, 2, 0, particle, collisions, ncollisions, REACTION_PROB);
}
}
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_2H2O_H3O_OH):
{
for (i=0; i<ncircles; i++)
{
if ((particle[i].active)&&(particle[i].type <= 1))
{
if (particle[i].npartners == 2)
{
oldncollisions = ncollisions;
ncollisions = chem_dissociate_molecule(i, particle, collisions, ncollisions, DISSOCIATION_PROB);
if (ncollisions == oldncollisions)
ncollisions = chem_merge_molecule(i, 2, 0, particle, collisions, ncollisions, REACTION_PROB);
}
else if (particle[i].npartners == 1)
ncollisions = chem_merge_molecule(i, 2, 0, particle, collisions, ncollisions, REACTION_PROB);
else if (particle[i].npartners == 3)
ncollisions = chem_dissociate_molecule(i, particle, collisions, ncollisions, 2.0*DISSOCIATION_PROB);
}
}
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_AGGREGATION):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX) for (k=0; k<3; k++)
{
ncollisions = chem_multi_glue_molecule(i, k, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_AGGREGATION_CHARGE):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX) for (k=0; k<3; k++)
{
ncollisions = chem_multi_glue_molecule(i, k, AGREGMAX, 0, 0, 1, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_AGGREGATION_NNEIGH):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX) for (k=0; k<3; k++)
{
ncollisions = chem_multi_glue_molecule(i, k, AGREGMAX, 1, 0, 1, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_DNA):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX) switch (particle[i].type)
{
case (2):
{
ncollisions = chem_multi_glue_molecule(i, 2, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
break;
}
case (3):
{
ncollisions = chem_multi_glue_molecule(i, 4, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
break;
}
case (4):
{
ncollisions = chem_multi_glue_molecule(i, 3, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
break;
}
case (5):
{
ncollisions = chem_multi_glue_molecule(i, 6, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
break;
}
case (6):
{
ncollisions = chem_multi_glue_molecule(i, 5, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
break;
}
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_DNA_ALT):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX)
{
atype = particle[i].type;
if (atype%2 == 1) btype = atype+1;
else btype = atype-1;
if (atype > 0)
ncollisions = chem_multi_glue_molecule(i, btype, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
// if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_DNA_DOUBLE):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX)
{
atype = particle[i].type;
if (atype%2 == 1) btype = atype+1;
else btype = atype-1;
if (atype > 0)
ncollisions = chem_multi_glue_molecule(i, btype, AGREGMAX, 0, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_DNA_DSPLIT):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX)
{
atype = particle[i].type;
if (atype%2 == 1) btype = atype+1;
else btype = atype-1;
if (atype > 0)
ncollisions = chem_multi_glue_molecule(i, btype, AGREGMAX, 3, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* split molecule with a certain probability */
if ((double)rand()/RAND_MAX < DISSOCIATION_PROB)
ncollisions = chem_split_molecule(i, particle, molecule, collisions, ncollisions);
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_DNA_BASE_SPLIT):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX)
{
atype = particle[i].type;
if (atype%2 == 1) btype = atype+1;
else btype = atype-1;
if (atype > 0)
ncollisions = chem_multi_glue_molecule(i, btype, AGREGMAX, 3, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* split molecule with a certain probability */
if ((double)rand()/RAND_MAX < DISSOCIATION_PROB)
ncollisions = chem_split_molecule(i, particle, molecule, collisions, ncollisions);
reac_dist = 2.5*MU;
switch (particle[i].type) {
case (3):
{
ncollisions = chem_split_molecule_if_nearby(i, 3, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 5, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 6, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (4):
{
ncollisions = chem_split_molecule_if_nearby(i, 4, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 5, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 6, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (5):
{
ncollisions = chem_split_molecule_if_nearby(i, 3, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 3, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 5, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (6):
{
ncollisions = chem_split_molecule_if_nearby(i, 3, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 4, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
ncollisions = chem_split_molecule_if_nearby(i, 6, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_DNA_ENZYME):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX)
{
atype = particle[i].type;
if (atype%2 == 1) btype = atype+1;
else btype = atype-1;
if (atype > 0)
ncollisions = chem_multi_glue_molecule(i, btype, AGREGMAX, 3, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* split molecule with a certain probability */
/* TODO update molecules after split */
if ((double)rand()/RAND_MAX < DISSOCIATION_PROB)
ncollisions = chem_split_molecule(i, particle, molecule, collisions, ncollisions);
reac_dist = 2.5*MU;
switch (particle[i].type) {
case (3):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 4, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (4):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 3, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (5):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 6, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (6):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 5, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (7):
{
if ((double)rand()/RAND_MAX < KILLING_PROB) particle[i].active = 0;
break;
}
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
/* for debugging */
// for (i=0; i<nmolecules; i++)
// if (molecule[i].npartners > 0)
// {
// printf("Molecule %i has %i partners: ", i, molecule[i].npartners);
// for (j=0; j<molecule[i].npartners; j++)
// printf("%i ", molecule[i].partner[j]);
// printf("\n");
// }
// sleep(3);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_DNA_ENZYME_REPAIR):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX)
{
atype = particle[i].type;
if (atype%2 == 1) btype = atype+1;
else btype = atype-1;
if (atype > 0) /* TEST */
ncollisions = chem_multi_glue_molecule(i, btype, AGREGMAX, 3, 0, 0, 0, particle, molecule, collisions, ncollisions, REACTION_PROB);
// ncollisions = chem_multi_glue_molecule(i, btype, AGREGMAX, 3, 0, 0, 2, particle, molecule, collisions, ncollisions, REACTION_PROB);
}
/* split molecule with a certain probability */
/* TODO update molecules after split */
if ((double)rand()/RAND_MAX < DISSOCIATION_PROB)
ncollisions = chem_split_molecule(i, particle, molecule, collisions, ncollisions);
reac_dist = 2.5*MU;
switch (particle[i].type) {
case (3):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 4, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (4):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 3, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (5):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 6, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (6):
{
ncollisions = chem_local_split_molecule_if_nearby(i, 5, 7, particle, molecule, collisions, ncollisions, reac_dist, REACTION_PROB);
break;
}
case (7):
{
if ((double)rand()/RAND_MAX < KILLING_PROB) particle[i].active = 0;
break;
}
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
// ncollisions = repair_dna(particle, molecule, collisions, ncollisions);
repair_counter++;
if (repair_counter >= 2)
{
ncollisions = repair_dna(particle, molecule, collisions, ncollisions);
// ncollisions = check_dna_pairing(particle, molecule, collisions, ncollisions);
repair_counter = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
/* for debugging */
// for (i=0; i<nmolecules; i++)
// if (molecule[i].npartners > 0)
// {
// printf("Molecule %i has %i partners: ", i, molecule[i].npartners);
// for (j=0; j<molecule[i].npartners; j++)
// printf("%i[%i] ", molecule[i].partner[j], molecule[i].connection_type[j]);
// printf("\n");
// }
// sleep(3);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_POLYGON_AGGREGATION):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < AGREGMAX) for (k=0; k<3; k++)
{
ncollisions = chem_multi_glue_polygon(i, k, AGREGMAX, particle, molecule, cluster, collisions, ncollisions, REACTION_PROB, 0);
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* update cluster color scheme */
if ((PLOT == P_CLUSTER)||(PLOT_B == P_CLUSTER))
update_cluster_color(particle);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
case (CHEM_POLYGON_CLUSTER):
{
for (i=0; i<ncircles; i++) if (particle[i].active)
{
if (particle[i].npartners < NPOLY)
{
ncollisions = chem_multi_glue_polygon2(i, NPOLY, particle, molecule, cluster, collisions, ncollisions, REACTION_PROB, 1, 0);
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
/* repair clusters */
if (!REPAIR_CLUSTERS) for (i=0; i<ncircles; i++)
if ((cluster[i].active)&&(cluster[i].nparticles >= 2))
repair_cluster(i, particle, cluster, 1000, 0);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}case (CHEM_POLYGON_ONECLUSTER):
{
i = 0;
reacted = 0;
while ((i<ncircles)&&(!reacted))
{
if (particle[i].active)
{
if (particle[i].npartners < NPOLY)
{
oldncollisions = ncollisions;
ncollisions = chem_multi_glue_polygon2(i, NPOLY, particle, molecule, cluster, collisions, ncollisions, REACTION_PROB, 1, 1);
if (ncollisions > oldncollisions) reacted = 1;
}
/* decouple particles with several partners from thermostat */
if (particle[i].npartners >= AGREG_DECOUPLE) particle[i].thermostat = 0;
}
i++;
}
/* repair clusters */
if (!REPAIR_CLUSTERS) for (i=0; i<ncircles; i++)
if ((cluster[i].active)&&(cluster[i].nparticles >= 2))
repair_cluster(i, particle, cluster, 1000, 1);
printf("%i collisions\n", ncollisions);
delta_n = ncollisions - oldncollisions;
printf("delta_n = %i\n", delta_n);
return(ncollisions);
}
}
}
double plot_coord(double x, double xmin, double xmax)
{
return(xmin + x*(xmax - xmin));
}
void draw_speed_plot(t_group_data *group_speeds, int i)
/* draw plot of obstacle speeds as a function of time */
{
int j, group;
char message[100];
static double xmin, xmax, ymin, ymax, xmid, ymid, dx, dy, plotxmin, plotxmax, plotymin, plotymax;
double pos[2], x1, y1, x2, y2, rgb[3];
static int first = 1, gshift = INITIAL_TIME + NSTEPS;
if (first)
{
// xmin = XMAX - 1.35;
// xmax = XMAX - 0.05;
// ymin = YMAX - 1.8;
// ymax = YMAX - 0.5;
xmin = XMAX - 1.8;
xmax = XMAX - 0.066;
ymin = YMAX - 2.5;
ymax = YMAX - 0.76;
xmid = 0.5*(xmin + xmax);
ymid = 0.5*(ymin + ymax);
dx = 0.5*(xmax - xmin);
dy = 0.5*(ymax - ymin);
plotxmin = xmin + 0.05;
plotxmax = xmax - 0.1;
plotymin = ymin + 0.07;
plotymax = ymax - 0.15;
first = 0;
}
// rgb[0] = 1.0; rgb[1] = 1.0; rgb[2] = 1.0;
glLineWidth(2);
/* plot angular speed */
for (group=1; group<ngroups; group++)
{
set_segment_group_color(group, 0.5, rgb);
x1 = plotxmin;
y1 = plotymin;
for (j=0; j<i; j++)
{
x2 = plot_coord((double)j/(double)NSTEPS, plotxmin, plotxmax);
y2 = plot_coord(group_speeds[(group-1)*gshift + j].omega/VMAX_PLOT_SPEEDS, plotymin, plotymax);
draw_line(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
sprintf(message, "omega");
write_text_fixedwidth(plotxmin - 0.22 + (double)(group-1)*0.3, plotymax + 0.16, message);
}
/* plot speed of obstacles */
for (group=1; group<ngroups; group++)
{
set_segment_group_color(group, 1.0, rgb);
x1 = plotxmin;
y1 = plotymin;
for (j=0; j<i; j++)
{
x2 = plot_coord((double)j/(double)NSTEPS, plotxmin, plotxmax);
y2 = plot_coord(group_speeds[(group-1)*gshift + j].vy/VMAX_PLOT_SPEEDS, plotymin, plotymax);
draw_line(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
sprintf(message, "vy");
write_text_fixedwidth(plotxmin - 0.22 + (double)(group-1)*0.3, plotymax + 0.25, message);
}
glColor3f(1.0, 1.0, 1.0);
/* axes and labels */
draw_line(plotxmin, plotymin, plotxmax + 0.05, plotymin);
draw_line(plotxmin, plotymin, plotxmin, plotymax + 0.1);
for (j=1; j<=(int)(10.0*VMAX_PLOT_SPEEDS); j++)
{
y1 = plot_coord((double)j/(10.0*VMAX_PLOT_SPEEDS), plotymin, plotymax);
draw_line(plotxmin - 0.02, y1, plotxmin + 0.02, y1);
}
sprintf(message, "%.1f", VMAX_PLOT_SPEEDS);
write_text_fixedwidth(plotxmin - 0.28, y1 - 0.025, message);
// write_text_fixedwidth(plotxmin - 0.22, y1 - 0.025, message);
sprintf(message, "time");
write_text_fixedwidth(plotxmax - 0.13, plotymin - 0.12, message);
// write_text_fixedwidth(plotxmax - 0.1, plotymin - 0.08, message);
}
void draw_trajectory_plot(t_group_data *group_speeds, int i)
/* draw plot of obstacle speeds as a function of time */
{
int j, group;
char message[100];
static double xmin, xmax, ymin, ymax, xmid, ymid, dx, dy, plotxmin, plotxmax, plotymin, plotymax, scalex, scaley, yinitial[NMAXGROUPS];
double pos[2], x0, y0, x1, y1, x2, y2, rgb[3];
static int first = 1, gshift = INITIAL_TIME + NSTEPS;
if (first)
{
xmin = XMAX - 1.8;
xmax = XMAX - 0.066;
ymin = YMIN + 0.1;
ymax = YMIN + 1.9;
xmid = 0.5*(xmin + xmax);
ymid = 0.5*(ymin + ymax);
dx = 0.5*(xmax - xmin);
dy = 0.5*(ymax - ymin);
plotxmin = xmin + 0.05;
plotxmax = xmax - 0.1;
plotymin = ymin + 0.07;
plotymax = ymax - 0.15;
scalex = 6.0;
scaley = 3.0;
for (group = 1; group < ngroups; group++)
yinitial[group] = group_speeds[(group-1)*gshift].yc;
first = 0;
}
if (ALTITUDE_LINES)
{
glLineWidth(1);
glColor3f(0.5, 0.5, 0.5);
for (j=1; j<(int)scaley + 1; j++)
{
y1 = plot_coord((double)j/scaley, plotymin, plotymax);
if (y1 < plotymax) draw_line(plotxmin, y1, plotxmax + 0.05, y1);
}
}
glLineWidth(2);
printf("ngroups = %i\n", ngroups);
/* plot trajectories */
for (group=1; group<ngroups; group++)
{
set_segment_group_color(group, 0.75, rgb);
x1 = group_speeds[(group-1)*gshift].xc;
y1 = group_speeds[(group-1)*gshift].yc - yinitial[group];
for (j=0; j<i-1; j++)
{
x0 = group_speeds[(group-1)*gshift + j].xc;
y0 = group_speeds[(group-1)*gshift + j].yc - yinitial[group];
if (y0 > scaley) scaley = y0;
x2 = plot_coord(0.5 + x0/scalex, plotxmin, plotxmax);
y2 = plot_coord(y0/scaley + 0.05, plotymin, plotymax);
// printf("yinitial = %.3lg, (x0, y0) = (%.3lg, %.3lg), (x2, y2) = (%.3lg, %.3lg)\n", yinitial, x0, y0, x2, y2);
if ((j>0)&&(module2(x2-x1, y2-y1) < 0.1)) draw_line(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
if (i>0) draw_colored_circle_precomp(x1, y1, 0.015, rgb);
}
glColor3f(1.0, 1.0, 1.0);
/* axes and labels */
draw_line(plotxmin, plotymin, plotxmax + 0.05, plotymin);
draw_line(plotxmin, plotymin, plotxmin, plotymax + 0.1);
for (j=1; j<(int)scaley; j++)
{
y1 = plot_coord((double)j/scaley, plotymin, plotymax);
draw_line(plotxmin - 0.02, y1, plotxmin + 0.02, y1);
}
sprintf(message, "%i", (int)scaley - 1);
write_text_fixedwidth(plotxmin - 0.15, y1 - 0.025, message);
// sprintf(message, "%.1f", VMAX_PLOT_SPEEDS);
sprintf(message, "y");
write_text_fixedwidth(plotxmin - 0.1, plotymax - 0.005, message);
sprintf(message, "x");
write_text_fixedwidth(plotxmax - 0.1, plotymin - 0.1, message);
}
void write_size_text(double x, double y, char *message, int largewin)
{
if (largewin) write_text(x, y, message);
else write_text_fixedwidth(x, y, message);
}
void draw_particle_nb_plot(int *particle_numbers, int i)
/* draw plot of number of particles as a function of time */
{
int j, type, power;
char message[100];
static double xmin, xmax, ymin, ymax, xmid, ymid, dx, dy, plotxmin, plotxmax, plotymin, plotymax;
double pos[2], x1, y1, x2, y2, rgb[3];
static int second = 2, gshift = INITIAL_TIME + NSTEPS, nmax, ygrad, largewin;
if (second > 0)
{
xmin = XMAX - 1.05;
xmax = XMAX - 0.05;
ymin = YMAX - 1.0;
ymax = YMAX - 0.05;
xmid = 0.5*(xmin + xmax);
ymid = 0.5*(ymin + ymax);
dx = 0.5*(xmax - xmin);
dy = 0.5*(ymax - ymin);
plotxmin = xmin + 0.18;
plotxmax = xmax - 0.1;
plotymin = ymin + 0.07;
plotymax = ymax - 0.15;
nmax = 0;
for (j=1; j<RD_TYPES+1; j++)
if (particle_numbers[RD_TYPES+1+j] > nmax) nmax = particle_numbers[RD_TYPES+1+j];
nmax *= PARTICLE_NB_PLOT_FACTOR;
power = (int)(log((double)nmax)/log(10.0)) - 1.0;
ygrad = (int)ipow(10.0, power);
largewin = (WINWIDTH > 1280);
second--;
}
erase_area_hsl(xmid, ymid, dx, dy, 0.0, 0.9, 0.0);
glLineWidth(2);
/* plot particle number */
for (type=1; type<RD_TYPES+1; type++)
{
set_type_color(type, 1.0, rgb);
x1 = plotxmin;
y1 = plotymin;
glBegin(GL_LINE_STRIP);
for (j=1; j<i; j++)
{
x2 = plot_coord((double)j/(double)NSTEPS, plotxmin, plotxmax);
y2 = plot_coord(particle_numbers[(RD_TYPES+1)*j+type]/(double)nmax, plotymin, plotymax);
glVertex2d(x2, y2);
// draw_line(x1, y1, x2, y2);
// x1 = x2;
// y1 = y2;
}
glEnd();
sprintf(message, "%5d molecules", particle_numbers[(RD_TYPES+1)*i+type]);
// write_size_text(xmax - 0.5, ymax - 0.04 - 0.06*(double)type, message, largewin);
// if (largewin) write_text(xmax - 0.5, ymax - 0.1 - 0.1*(double)type, message);
// else
write_text_fixedwidth(xmax - 0.5, ymax - 0.06*(double)type, message);
}
glColor3f(1.0, 1.0, 1.0);
/* axes and labels */
draw_line(plotxmin, plotymin, plotxmax + 0.05, plotymin);
draw_line(plotxmin, plotymin, plotxmin, plotymax + 0.1);
glLineWidth(1);
j = 1;
while (j*ygrad <= nmax + 5)
{
y1 = plot_coord((double)(j*ygrad)/(double)nmax, plotymin, plotymax);
draw_line(plotxmin - 0.015, y1, plotxmin + 0.015, y1);
if (j%10 == 0)
{
draw_line(plotxmin - 0.025, y1, plotxmin + 0.025, y1);
sprintf(message, "%i", j*ygrad);
// write_size_text(plotxmin - 0.15, y1 - 0.015, message, largewin);
// if (largewin) write_text(plotxmin - 0.17, y1 - 0.015, message);
// else
write_text_fixedwidth(plotxmin - 0.15, y1 - 0.015, message);
}
else if ((j%5 == 0)&&(nmax<5*ygrad))
{
sprintf(message, "%i", j*ygrad);
// if (j*ygrad >= 1000) write_size_text(plotxmin - 0.15, y1 - 0.015, message, largewin);
// else write_size_text(plotxmin - 0.12, y1 - 0.015, message, largewin);
if (j*ygrad >= 1000) write_text_fixedwidth(plotxmin - 0.15, y1 - 0.015, message);
else write_text_fixedwidth(plotxmin - 0.12, y1 - 0.015, message);
}
j++;
}
sprintf(message, "time");
// write_size_text(plotxmax - 0.1, plotymin - 0.05, message, largewin);
write_text_fixedwidth(plotxmax - 0.1, plotymin - 0.05, message);
}
void init_segment_group(t_segment segment[NMAXSEGMENTS], int group, t_group_segments segment_group[NMAXGROUPS])
/* initialize center of mass and similar data of grouped segments */
{
int i, nseg_group = 0;
double xc = 0.0, yc = 0.0;
for (i=0; i<nsegments; i++) if (segment[i].group == group)
{
xc += 0.5*(segment[i].x1 + segment[i].x2);
yc += 0.5*(segment[i].y1 + segment[i].y2);
// printf("Segment group data %i: z1 = (%.3lg, %.3lg)\n z2 = (%.3lg, %.3lg)\n zc = (%.3lg, %.3lg)\n\n", group,
// segment[i].x1, segment[i].y1, segment[i].x2, segment[i].y2, xc, yc);
nseg_group++;
}
if (nseg_group == 0) nseg_group = 1;
segment_group[group].xc = xc/(double)nseg_group;
segment_group[group].yc = yc/(double)nseg_group;
segment_group[group].angle = 0.0;
segment_group[group].vx = 0.0;
segment_group[group].vy = 0.0;
segment_group[group].omega = 0.0;
segment_group[group].mass = SEGMENT_GROUP_MASS;
segment_group[group].moment_inertia = SEGMENT_GROUP_I;
printf("Segment group data %i: (%.3lg, %.3lg)\n", group, segment_group[group].xc, segment_group[group].yc);
}
void reset_energy(t_particle particle[NMAXCIRCLES], double px[NMAXCIRCLES], double py[NMAXCIRCLES], double totalenergy, double emean)
/* decrease energy in case of blow-up */
{
int i;
double vratio, emax;
emax = 10.0*emean/(double)ncircles;
// printf("Warning: blow-up, resetting energy from %.5lg to %.5lg\n\n", totalenergy, emean);
printf("Warning: blow-up, resetting energy of some particles\n");
// vratio = sqrt(emean/totalenergy);
for (i=0; i < ncircles; i++) if (particle[i].energy > emax)
{
printf("Particle %i at (%.3lg, %.3lg) has energy %.5lg, resetting to 0\n", i, particle[i].xc, particle[i].yc, particle[i].energy);
particle[i].vx = 0.0;
particle[i].vy = 0.0;
px[i] = 0.0;
py[i] = 0.0;
particle[i].energy = 0.0;
}
printf("\n\n");
}
int init_cluster_config(t_particle particle[NMAXCIRCLES], t_cluster cluster[NMAXCIRCLES])
/* initialize the clusters for option CLUSTER_PARTICLES */
/* returns number of active clusters */
{
int i, j, tmp, nclusters = 0;
for (i=0; i<ncircles; i++) /*if (particle[i].active)*/
{
nclusters++;
cluster[i].active = particle[i].active;
cluster[i].thermostat = 1;
cluster[i].selected = 0;
cluster[i].xg = particle[i].xc;
cluster[i].yg = particle[i].yc;
cluster[i].angle = particle[i].angle;
cluster[i].vx = particle[i].vx;
cluster[i].vy = particle[i].vy;
cluster[i].omega = particle[i].omega;
cluster[i].mass_inv = particle[i].mass_inv;
cluster[i].mass = 1.0/particle[i].mass_inv;
cluster[i].inertia_moment_inv = particle[i].inertia_moment_inv;
cluster[i].inertia_moment = 1.0/particle[i].inertia_moment_inv;
cluster[i].fx = particle[i].fx;
cluster[i].fy = particle[i].fy;
cluster[i].torque = particle[i].torque;
cluster[i].energy = particle[i].energy;
cluster[i].emean = particle[i].emean;
cluster[i].nparticles = 1;
cluster[i].particle[0] = i;
// cluster[i].angle_ref = 0;
particle[i].cluster = i;
particle[i].cluster_color = i;
particle[i].cluster_size = 1;
particle[i].flip = 0;
}
/* randomize cluster number using Fisher-Yates algorithm */
for (i=0; i<ncircles-1; i++)
{
j = i + rand()%(ncircles-i);
tmp = particle[i].cluster_color;
particle[i].cluster_color = particle[j].cluster_color;
particle[j].cluster_color = tmp;
}
// for (i=0; i<ncircles; i++)
// {
// j = cluster[i].particle[0];
// particle[j].cluster = i;
// }
//
// for (i=0; i<ncircles; i++)
// {
// fprintf(lj_log, "Particle %i -> cluster %i -> particle %i\n", i, particle[i].cluster, cluster[particle[i].cluster].particle[0]);
// }
return(nclusters);
}
void compute_cluster_force(t_cluster cluster[NMAXCIRCLES], t_particle particle[NMAXCIRCLES])
/* compute force and torque on clusters from force and torque on their particles */
{
int i, p, j;
double fx, fy, torque, energy;
for (i=0; i<ncircles; i++) if (cluster[i].active)
{
fx = 0.0;
fy = 0.0;
torque = 0.0;
for (p=0; p<cluster[i].nparticles; p++)
{
j = cluster[i].particle[p];
fx += particle[j].fx;
fy += particle[j].fy;
torque += particle[j].torque;
torque += (particle[j].xc - cluster[i].xg)*particle[j].fy;
torque -= (particle[j].yc - cluster[i].yg)*particle[j].fx;
}
cluster[i].fx = fx;
cluster[i].fy = fy;
cluster[i].torque = torque;
}
}
void update_conveyor_belts(t_segment segment[NMAXSEGMENTS], t_belt belt[NMAXBELTS])
/* move shovels of conveyor belt */
{
int b, shovel, firstseg, seg;
double position, length, width, angle, newpos, deltapos, beltlength, shift, beta;
for (b = 0; b < nbelts; b++)
{
length = belt[b].length;
width = belt[b].width;
angle = belt[b].angle;
beltlength = 2.0*length + DPI*width;
for (shovel = 0; shovel < belt[b].nshovels; shovel++)
{
position = belt[b].shovel_pos[shovel];
deltapos = belt[b].speed*DT_PARTICLE;
newpos = position + deltapos;
firstseg = belt[b].shovel_segment[shovel];
if (newpos < length)
{
shift = deltapos;
for (seg = firstseg; seg < firstseg+6; seg++)
translate_one_segment(segment, seg, shift*belt[b].tx, shift*belt[b].ty);
}
else if (newpos < length + deltapos)
{
shift = length - position;
beta = (newpos - length)/width;
for (seg = firstseg; seg < firstseg+6; seg++)
{
translate_one_segment(segment, seg, shift*belt[b].tx, shift*belt[b].ty);
rotate_one_segment(segment, seg, -beta, belt[b].x2, belt[b].y2);
}
}
else if (newpos < length + PI*width)
{
beta = deltapos/width;
for (seg = firstseg; seg < firstseg+6; seg++)
{
rotate_one_segment(segment, seg, -beta, belt[b].x2, belt[b].y2);
}
}
else if (newpos < length + PI*width + deltapos)
{
beta = (length + PI*width - position)/width;
shift = newpos - length - PI*width;
for (seg = firstseg; seg < firstseg+6; seg++)
{
rotate_one_segment(segment, seg, -beta, belt[b].x2, belt[b].y2);
translate_one_segment(segment, seg, -shift*belt[b].tx, -shift*belt[b].ty);
}
}
else if (newpos < 2.0*length + PI*width)
{
shift = deltapos;
for (seg = firstseg; seg < firstseg+6; seg++)
translate_one_segment(segment, seg, -shift*belt[b].tx, -shift*belt[b].ty);
}
else if (newpos < 2.0*length + PI*width + deltapos)
{
shift = 2.0*length + PI*width - position;
beta = (newpos - 2.0*length - PI*width)/width;
for (seg = firstseg; seg < firstseg+6; seg++)
{
translate_one_segment(segment, seg, -shift*belt[b].tx, -shift*belt[b].ty);
rotate_one_segment(segment, seg, -beta, belt[b].x2, belt[b].y2);
}
}
else if (newpos < beltlength)
{
beta = deltapos/width;
for (seg = firstseg; seg < firstseg+6; seg++)
{
rotate_one_segment(segment, seg, -beta, belt[b].x1, belt[b].y1);
}
}
else
{
beta = (beltlength - position)/width;
shift = newpos - beltlength;
for (seg = firstseg; seg < firstseg+6; seg++)
{
rotate_one_segment(segment, seg, -beta, belt[b].x1, belt[b].y1);
translate_one_segment(segment, seg, shift*belt[b].tx, shift*belt[b].ty);
}
}
if (newpos > beltlength) newpos -= beltlength;
belt[b].shovel_pos[shovel] = newpos;
}
}
}
void draw_frame(int i, int plot, int bg_color, int ncollisions, int traj_position,
int traj_length,
int wall, double pressure[N_PRESSURES], double pleft, double pright,
int *particle_numbers, short int refresh,
t_lj_parameters params, t_particle particle[NMAXCIRCLES],
t_cluster cluster[NMAXCIRCLES],
t_collision *collisions, t_hashgrid hashgrid[HASHX*HASHY],
t_tracer trajectory[TRAJECTORY_LENGTH*N_TRACER_PARTICLES],
t_obstacle obstacle[NMAXOBSTACLES], t_segment segment[NMAXSEGMENTS],
t_group_data *group_speeds, t_group_segments *segment_group,
t_belt *conveyor_belt, int *tracer_n,
t_otriangle otriangle[NMAX_TRIANGLES_PER_OBSTACLE*NMAXOBSTACLES], t_ofacet ofacet[NMAXOBSTACLES])
/* draw a movie frame */
{
printf("Drawing frame\n");
if ((COLOR_BACKGROUND)&&(bg_color > 0)) color_background(particle, obstacle, bg_color, hashgrid);
// else if (!TRACER_PARTICLE) blank();
if (DRAW_OBSTACLE_LINKS) draw_obstacle_triangles(obstacle, otriangle, ofacet);
// if (DRAW_OBSTACLE_LINKS) old_draw_obstacle_triangles(obstacle);
if (TRACER_PARTICLE) draw_trajectory(trajectory, traj_position, traj_length, particle, cluster, tracer_n, plot);
draw_particles(particle, cluster, plot, params.beta, collisions, ncollisions, bg_color, hashgrid, params);
draw_container(params.xmincontainer, params.xmaxcontainer, obstacle, segment, conveyor_belt, wall);
if (PRINT_PARAMETERS) print_parameters(params, PRINT_LEFT, pressure, refresh);
if (PLOT_SPEEDS) draw_speed_plot(group_speeds, i);
if (PLOT_TRAJECTORIES) draw_trajectory_plot(group_speeds, i);
if ((i > INITIAL_TIME)&&(PLOT_PARTICLE_NUMBER)) draw_particle_nb_plot(particle_numbers, i - INITIAL_TIME);
// if (PLOT_PARTICLE_NUMBER) draw_particle_nb_plot(particle_numbers, i - INITIAL_TIME);
if (BOUNDARY_COND == BC_EHRENFEST) print_ehrenfest_parameters(particle, pleft, pright);
else if (PRINT_PARTICLE_NUMBER)
{
if (REACTION_DIFFUSION) print_particle_types_number(particle, RD_TYPES);
else print_particle_number(ncircles);
}
else if (PRINT_PARTICLE_SPEEDS) print_particles_speeds(particle);
else if (PRINT_SEGMENTS_SPEEDS) print_segment_group_speeds(segment_group);
// printf("5\n");
}