1 /*
2  * ***************************************************************************
3  * GAMER = < Geometry-preserving Adaptive MeshER >
4  * Copyright (C) 1994-- Michael Holst and Zeyun Yu
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  * ***************************************************************************
20  */
21 
22 /*
23  * ***************************************************************************
24  * File:     MolecularMesh.C    < ... >
25  *
26  * Author:   Zeyun Yu (zeyun.yu@gmail.com)
27  *
28  * Purpose:  The main function of MolecularMesh (the old GAMer application)
29  * ***************************************************************************
30  */
31 
32 #include <gamer/gamer.h>
33 
34 // parameters for mesh smoothing, refinement, and coarsening
35 
36 /** @brief Maximal number of nodes allowed */
37 #define MeshSizeUpperLimit   80001
38 /** @brief Minimal number of nodes allowed */
39 #define MeshSizeLowerLimit   10000
40 
41 /** @brief Number of iterations in mesh quality improvement */
42 #define ITER_NUM             6
43 
44 /*
45  * ***************************************************************************
46  * Routine:  SmoothMolecularSurface    < ... >
47  *
48  * Author:   Zeyun Yu (zeyun.yu@gmail.com)
49  *
50  * Purpose:  Main function for surface smoothing, refining, and coarsening
51  *           a molecular surface mesh
52  * ***************************************************************************
53  */
SmoothMolecularSurface(SurfaceMesh * surfmesh,int off_flag,ATOM * sphere_list,int num_spheres,unsigned int * sphere_markers)54 void SmoothMolecularSurface(SurfaceMesh *surfmesh, int off_flag, ATOM *sphere_list,
55 			    int num_spheres, unsigned int *sphere_markers)
56 {
57   int m,n,a0,b0;
58   NPNT3 *first_ngr, *second_ngr, *tmp_ngr;
59   char stop;
60 
61   bool smoothed;
62 
63   //GenerateHistogram(surfmesh);
64 
65   /* Check the neighborhood information */
66   SurfaceMesh_createNeighborlist(surfmesh);
67 
68   /* Feature-preserving quality improvement */
69 
70   // initial surface mesh smoothing if the input mesh is not in OFF format ...
71   if (!off_flag)
72     SurfaceMesh_smooth(surfmesh, 10, 160, ITER_NUM, false);
73 
74   while (1) {
75     // mesh coarsening ....
76     if (surfmesh->nv > MeshSizeUpperLimit || !off_flag) {
77       off_flag = 1;
78       /* Mesh-coarsening based on surface curvature */
79       printf("\nbegin assigning active sites ....\n");
80       SurfaceMesh_assignActiveSites(surfmesh, sphere_list, num_spheres,
81 				    sphere_markers);
82       printf("\nbegin mesh coarsening ....\n");
83       stop = SurfaceMesh_coarse(surfmesh, CoarsenRate, 1, 1, 0.5);
84 
85       printf("After coarsening: Nodes = %d,  Faces = %d\n\n",surfmesh->nv,surfmesh->nf);
86 
87       /* Feature-preserving quality improvement */
88       m = 1;
89       while (1) {
90 
91 	smoothed = SurfaceMesh_smooth(surfmesh, 20, 140, 1, true);
92 
93       	if ( smoothed )
94       	  break;
95 
96       	m++;
97       	if ((surfmesh->nv > MeshSizeUpperLimit && m > ITER_NUM) ||
98       	    (surfmesh->nv <= MeshSizeUpperLimit && m > 2*ITER_NUM))
99       	  break;
100       }
101       m=1;
102       printf("\nbegin surface diffusion ....\n");
103       SurfaceMesh_normalSmooth(surfmesh);
104 
105     }
106 
107     else if (surfmesh->nv < MeshSizeLowerLimit) {
108 
109       SurfaceMesh_refine(surfmesh);
110 
111       printf("After Refinement: Nodes = %d, Faces = %d\n", surfmesh->nv, surfmesh->nf);
112       SurfaceMesh_smooth(surfmesh, 10, 150, ITER_NUM, true);
113 
114       if (surfmesh->nv >= MeshSizeLowerLimit)
115     	break;
116     }
117 
118     // nothing to be done...
119     else
120       break;
121   }
122 
123   // Assign active sites again
124   SurfaceMesh_assignActiveSites(surfmesh, sphere_list, num_spheres,
125 				sphere_markers);
126 
127   //GenerateHistogram(surfmesh);
128 
129   SurfaceMesh_destroyNeighborlist(surfmesh);
130 
131   return;
132 }
133 
134 
135 /*
136  * ***************************************************************************
137  * Rutine:   MolecularMeshCALL.C    < ... >
138  *
139  * Author:   Zeyun Yu (zeyun.yu@gmail.com)
140  *
141  * Purpose:  The main entrance of GAMER
142  * ***************************************************************************
143  */
MolecularMesh_CALL(char active_flag,char molsurf,char * input_name,char * input_site,int output_flag,GemMesh * Gem_mesh)144 int MolecularMesh_CALL(char active_flag, char molsurf, char *input_name,
145 		       char *input_site, int output_flag, GemMesh *Gem_mesh)
146 {
147   bool write_to_file = Gem_mesh == NULL;
148   float iso_val,max_density;
149   time_t t1,t2;
150   FILE *fout;
151   SurfaceMesh *surfmesh, *surfmesh_inner, *surfmesh_outer;
152   int xdim,ydim,zdim;
153   float *dataset;
154   float distance,radius;
155   SPNT *holelist;
156   float min[3],max[3],span[3];
157   char filename[256];
158   int atom_num = 0;
159   unsigned int num_spheres = 0;
160   ATOM *atom_list = NULL;
161   ATOM *sphere_list = NULL;
162   unsigned int *sphere_markers = NULL;
163   float x,y,z;
164   ATOM center_radius;
165 
166   tetgenio in, out;
167   tetgenio::facet *f;
168   tetgenio::polygon *p;
169   int i,j,k;
170   char buf[1024];
171   unsigned int c;
172   char IsOFF;
173   char IsRawiv;
174   char IsXYZR;
175 
176   // Read sphere list from file if givven
177   if (active_flag)
178     ReadActiveSiteFile(input_site, num_spheres, sphere_list, sphere_markers);
179 
180   strcpy(filename,input_name);
181   IsOFF = 0;
182   for(i = 0; i<256; i++) {
183     if (filename[i+3] == '\0')
184       break;
185     else if (filename[i] == '.' &&
186 	     (filename[i+1] == 'O' || filename[i+1] == 'o') &&
187 	     (filename[i+2] == 'F' || filename[i+2] == 'f') &&
188 	     (filename[i+3] == 'F' || filename[i+3] == 'f') &&
189              filename[i+4] == '\0') {
190       IsOFF = 1;
191       break;
192     }
193   }
194   strcpy(filename,input_name);
195   IsRawiv = 0;
196   for(i = 0; i<256; i++) {
197     if (filename[i+5] == '\0')
198       break;
199     else if (filename[i] == '.' &&
200              (filename[i+1] == 'R' || filename[i+1] == 'r') &&
201              (filename[i+2] == 'A' || filename[i+2] == 'a') &&
202              (filename[i+3] == 'W' || filename[i+3] == 'w') &&
203 	     (filename[i+4] == 'I' || filename[i+4] == 'i') &&
204              (filename[i+5] == 'V' || filename[i+5] == 'v') &&
205              filename[i+6] == '\0') {
206       IsRawiv = 1;
207       break;
208     }
209   }
210   strcpy(filename,input_name);
211   IsXYZR = 0;
212   for(i = 0; i<256; i++) {
213     if (filename[i+4] == '\0')
214       break;
215     else if (filename[i] == '.' &&
216              (filename[i+1] == 'X' || filename[i+1] == 'x') &&
217              (filename[i+2] == 'Y' || filename[i+2] == 'y') &&
218              (filename[i+3] == 'Z' || filename[i+3] == 'z') &&
219              (filename[i+4] == 'R' || filename[i+4] == 'r') &&
220              filename[i+5] == '\0') {
221       IsXYZR = 1;
222       break;
223     }
224   }
225 
226   if (IsOFF) {
227     // load user-defined molecualr surface meshes in OFF format
228     printf("loading the user-specified surface/volumetric mesh....\n");
229     surfmesh_inner = SurfaceMesh_readOFF(input_name);
230     printf("begin surface smoothing ... \n");
231 
232     (void)time(&t1);
233     atom_num = 0;
234     SmoothMolecularSurface(surfmesh_inner, 1, sphere_list, num_spheres,
235 			   sphere_markers);
236     (void)time(&t2);
237     printf("time to smooth surface mesh: %d seconds. \n\n",(int)(t2-t1));
238   }
239   else if (IsRawiv) {
240     ReadRawiv(&xdim,&ydim,&zdim,&dataset,input_name,span,min);
241     printf("begin extracting isosurfaces ... \n");
242     (void)time(&t1);
243     iso_val = 255;
244     if (iso_val > IsoValue)
245       iso_val = IsoValue;
246     printf("isovalue: %f \n",iso_val);
247     surfmesh_inner = SurfaceMesh_marchingCube(xdim, ydim, zdim, dataset,
248 					      iso_val, &holelist);
249     (void)time(&t2);
250     printf("vertices: %d, faces: %d\n",surfmesh_inner->nv,surfmesh_inner->nf);
251     printf("time to extract isosurface: %d seconds. \n\n",(int)(t2-t1));
252     free(dataset);
253 
254     // convert from pixel to angstrom
255     for (j=0; j<surfmesh_inner->nv; j++) {
256       surfmesh_inner->vertex[j].x = surfmesh_inner->vertex[j].x*span[0]+min[0];
257       surfmesh_inner->vertex[j].y = surfmesh_inner->vertex[j].y*span[1]+min[1];
258       surfmesh_inner->vertex[j].z = surfmesh_inner->vertex[j].z*span[2]+min[2];
259     }
260 
261     printf("begin surface smoothing ... \n");
262     (void)time(&t1);
263     atom_num = 0;
264     SmoothMolecularSurface(surfmesh_inner, 0, sphere_list, num_spheres,
265 			   sphere_markers);
266     (void)time(&t2);
267     printf("time to smooth surface mesh: %d seconds. \n\n",(int)(t2-t1));
268   }
269   else {
270 
271     // Read PDB or PQR files, or XYZR format
272     if (molsurf == 0) {
273       printf("\nbegin blurring PDB/PQR coordinates ... \n");
274       (void)time(&t1);
275       max_density = PDB2Volume(input_name, &dataset, &xdim, &ydim, &zdim, min,max,
276 			       &atom_list, &atom_num, IsXYZR);
277       (void)time(&t2);
278       printf("time to generate volume from PDB: %d seconds. \n\n",(int)(t2-t1));
279       span[0] = (max[0]-min[0])/(float)(xdim-1);
280       span[1] = (max[1]-min[1])/(float)(ydim-1);
281       span[2] = (max[2]-min[2])/(float)(zdim-1);
282 
283       printf("begin extracting isosurfaces ... \n");
284       (void)time(&t1);
285       iso_val = 0.44*max_density;
286       if (iso_val > IsoValue)
287 	iso_val = IsoValue;
288       printf("isovalue: %f \n",iso_val);
289       surfmesh_inner = SurfaceMesh_marchingCube(xdim, ydim, zdim, dataset,
290 						iso_val, &holelist);
291       (void)time(&t2);
292       printf("vertices: %d, faces: %d\n",surfmesh_inner->nv,surfmesh_inner->nf);
293       printf("time to extract isosurface: %d seconds. \n\n",(int)(t2-t1));
294       free(dataset);
295 
296       // convert from pixel to angstrom
297       for (j=0; j<surfmesh_inner->nv; j++) {
298 	surfmesh_inner->vertex[j].x = surfmesh_inner->vertex[j].x*span[0]+min[0];
299 	surfmesh_inner->vertex[j].y = surfmesh_inner->vertex[j].y*span[1]+min[1];
300 	surfmesh_inner->vertex[j].z = surfmesh_inner->vertex[j].z*span[2]+min[2];
301       }
302     }
303     else if (molsurf == 1) {
304       surfmesh_inner = SurfaceMesh_readPDB(input_name);
305     }
306     else {
307       printf("molsurf can only be 0 or 1 \n");
308       exit(0);
309     }
310 
311     printf("begin surface smoothing ... \n");
312     (void)time(&t1);
313     SmoothMolecularSurface(surfmesh_inner, 0, sphere_list, num_spheres,
314 			   sphere_markers);
315     (void)time(&t2);
316     printf("time to smooth surface mesh: %d seconds. \n\n",(int)(t2-t1));
317   }
318 
319   // Get the center and radius of Surface mesh
320   center_radius = SurfaceMesh_getCenterRadius(surfmesh_inner);
321   printf("center/radius: %f %f %f  %f\n", center_radius.x, center_radius.y,
322 	 center_radius.z, center_radius.radius);
323 
324 
325   // Generate exterior sphere mesh
326   printf("Generating the mesh for the bounding sphere ....\n");
327   surfmesh_outer = SurfaceMesh_sphere(4);
328   for (j=0; j < surfmesh_outer->nv; j++) {
329     surfmesh_outer->vertex[j].x = surfmesh_outer->vertex[j].x*center_radius.radius*
330       SphereRatio+center_radius.x;
331     surfmesh_outer->vertex[j].y = surfmesh_outer->vertex[j].y*center_radius.radius*
332       SphereRatio+center_radius.y;
333     surfmesh_outer->vertex[j].z = surfmesh_outer->vertex[j].z*center_radius.radius*
334       SphereRatio+center_radius.z;
335   }
336   printf("\n");
337 
338   printf("Mesh generated ....\n");
339 
340   // Merge molecular mesh and sphere mesh
341   surfmesh = SurfaceMesh_merge(surfmesh_inner, surfmesh_outer);
342 
343   // Output surface meshes
344   sprintf(filename, "%s.output.surf.off", input_name);
345 
346   SurfaceMesh_writeOFF(surfmesh_inner, filename);
347 
348   // Compute and Output all tetrahedra meshes into .m format
349   printf("begin all tetrahedra generating ... \n");
350   (void)time(&t1);
351   in.firstnumber = 1;
352   in.numberofpoints = surfmesh->nv;
353   in.pointlist = new REAL[in.numberofpoints * 3];
354   for (j = 0; j < in.numberofpoints; j++) {
355     in.pointlist[j*3+0]  = surfmesh->vertex[j].x;
356     in.pointlist[j*3+1]  = surfmesh->vertex[j].y;
357     in.pointlist[j*3+2]  = surfmesh->vertex[j].z;
358   }
359   in.numberoffacets = surfmesh->nf;
360   in.facetlist = new tetgenio::facet[in.numberoffacets];
361   for (j = 0; j < in.numberoffacets; j++) {
362     f = &in.facetlist[j];
363     f->holelist = (REAL *) NULL;
364     f->numberofholes = 0;
365     f->numberofpolygons = 1;
366     f->polygonlist = new tetgenio::polygon[f->numberofpolygons];
367     p = &f->polygonlist[0];
368     p->numberofvertices = 3;
369     p->vertexlist = new int[p->numberofvertices];
370     p->vertexlist[0] = surfmesh->face[j].a+in.firstnumber;
371     p->vertexlist[1] = surfmesh->face[j].b+in.firstnumber;
372     p->vertexlist[2] = surfmesh->face[j].c+in.firstnumber;
373   }
374 
375   // insert each atom as a node
376   if (atom_num > 0) {
377     in.addpointlist = new REAL[atom_num * 3];
378     for (i = 0; i < atom_num; i++) {
379       in.addpointlist[i*3+0] = atom_list[i].x;
380       in.addpointlist[i*3+1] = atom_list[i].y;
381       in.addpointlist[i*3+2] = atom_list[i].z;
382     }
383     in.numberofaddpoints = atom_num;
384   }
385 
386   // add boundary marker on each node
387   in.pointmarkerlist = new int[in.numberofpoints];
388   for (j = 0; j < in.numberofpoints; j++)
389     in.pointmarkerlist[j] = 1;
390 
391   tetrahedralize("npq1.333AAYY", &in, &out, NULL);
392 
393   (void)time(&t2);
394   printf("time to generate all tetrahedra: %d seconds. \n\n",(int)(t2-t1));
395   printf("begin writing all meshes ... \n");
396   (void)time(&t1);
397 
398   // Assign active site information
399   // Morphology-based hole-filling by dilation
400   char *ActiveSite;
401   ActiveSite = new char[out.numberofpoints];
402   for (i = 0; i < out.numberoftetrahedra; i++) {
403     c = 0;
404     for (j = 0; j < 4; j++) {
405       k = out.tetrahedronlist[i * 4 + j] - 1;
406       if (k < surfmesh_inner->nv)
407 
408   	if (surfmesh->nvm > 0 && surfmesh->vertex_markers[k] > 0)
409   	  c = surfmesh->vertex_markers[k];
410     }
411     for (j = 0; j < 4; j++) {
412       k = out.tetrahedronlist[i * 4 + j] - 1;
413       if (out.pointmarkerlist[k]) {
414   	ActiveSite[k] = c;
415       }
416     }
417   }
418 
419   // Release surface meshs
420   SurfaceMesh_dtor(surfmesh);
421   SurfaceMesh_dtor(surfmesh_inner);
422   SurfaceMesh_dtor(surfmesh_outer);
423 
424   // Construct a GemMesh structur
425 
426   Gem_mesh = GemMesh_fromPdb(&out, radius*SphereRatio, center_radius.x, center_radius.y, center_radius.z, ActiveSite, output_flag);
427 
428   // Check if we are writing the mesh to file
429   if (write_to_file) {
430 
431     if (output_flag == 1)
432       sprintf(filename, "%s.output.all.m", input_name);
433     if (output_flag == 2)
434       sprintf(filename, "%s.output.in.m", input_name);
435     if (output_flag == 3)
436       sprintf(filename, "%s.output.out.m", input_name);
437     GemMesh_writeMcsf(Gem_mesh, filename);
438     GemMesh_dtor(Gem_mesh);
439   }
440 
441   delete[] ActiveSite;
442   if (atom_list !=NULL)
443     free(atom_list);
444   if (sphere_list !=NULL)
445     free(sphere_list);
446   if (sphere_markers !=NULL)
447     free(sphere_markers);
448 
449   return(0);
450 }
451 
452 
453 
main(int argc,char * argv[])454 int main(int argc, char *argv[])
455 {
456   int region_flag;
457 
458 
459   if (argc != 4 && argc != 3){
460     printf("\nUsage: MolecularMesh <Input> <Domain> [ActiveSite] \n");
461     printf("      <Input>: PDB, PQR, OFF, XYZR, or Rawiv \n");
462     printf("      <Domain>: 1 --> generate both inner and outer meshes \n");
463     printf("                2 --> generate only inner mesh \n");
464     printf("                3 --> generate only outer mesh \n");
465     printf("      <ActiveSite>: See README for specification \n\n");
466     exit(0);
467   }
468 
469   region_flag = atoi(argv[2]);
470   if (region_flag!=1 && region_flag!=2 && region_flag!=3) {
471     printf("\n<Domain> must be 1, 2 or 3....\n");
472     printf("Type <MolecularMesh -help> for more information....\n\n");
473     exit(0);
474   }
475 
476 
477 /*
478  * **************************************************************************
479  * You may call GAMer in two ways:
480  *     (1) Output the meshes to disks...
481  *         In this case, you do nothing but just checking you current
482  *         direcotry for the outputs when GAMer is finished.
483  *
484  *     (2) Output the meshes to a data structure "GemMesh"...
485  *         For the second case, you convert the GemMesh into your own
486  *         data structure such as "Gem" in FETK.
487  * ***************************************************************************
488  */
489 
490 
491   // CASE 1:
492   if (argc == 4) {// active site is specified
493     // change the second parameter to 1 if the new approach is to be used
494     MolecularMesh_CALL(1, 1, argv[1], argv[3], region_flag, NULL);
495   }
496   else {// no active site is specified
497     // change the second parameter to 1 if the new approach is to be used
498     MolecularMesh_CALL(0, 1, argv[1], NULL, region_flag, NULL);
499   }
500 
501   /*
502   // CASE 2:
503   GemMesh *gamer_mesh;
504   if (argc == 4) // active site is specified
505     MolecularMesh_CALL(1, 0, argv[1], argv[3], region_flag, gamer_mesh);
506   else // no active site is specified
507     MolecularMesh_CALL(0, 0, argv[1], NULL, region_flag, gamer_mesh);
508 
509   // You can now convert "gamer_mesh" into your own data structure...
510   // The GemMesh structure is defined in src/tetgen/gamer/tetgen.h
511   */
512 
513   return(0);
514 }
515