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