1 /* ----------------------------------------------------------------------
2    SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
3    http://sparta.sandia.gov
4    Steve Plimpton, sjplimp@sandia.gov, Michael Gallis, magalli@sandia.gov
5    Sandia National Laboratories
6 
7    Copyright (2014) Sandia Corporation.  Under the terms of Contract
8    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
9    certain rights in this software.  This software is distributed under
10    the GNU General Public License.
11 
12    See the README file in the top-level SPARTA directory.
13 ------------------------------------------------------------------------- */
14 
15 #include "mpi.h"
16 #include "string.h"
17 #include "stdlib.h"
18 #include "ctype.h"
19 #include "dirent.h"
20 #include "read_surf.h"
21 #include "math_extra.h"
22 #include "surf.h"
23 #include "domain.h"
24 #include "grid.h"
25 #include "comm.h"
26 #include "geometry.h"
27 #include "input.h"
28 #include "write_surf.h"
29 #include "math_const.h"
30 #include "memory.h"
31 #include "error.h"
32 
33 using namespace SPARTA_NS;
34 using namespace MathConst;
35 
36 enum{NEITHER,BAD,GOOD};
37 enum{NONE,CHECK,KEEP};
38 enum{UNKNOWN,OUTSIDE,INSIDE,OVERLAP};           // several files
39 enum{LOCAL,MINE,TEMPALL,TEMPSTRIDE};
40 
41 #define MAXLINE 256
42 #define CHUNK 16384
43 #define EPSILON_NORM 1.0e-12
44 #define BIG 1.0e20
45 #define DELTA 128           // must be 2 or greater
46 #define DELTA_DISCARD 128
47 
48 /* ---------------------------------------------------------------------- */
49 
ReadSurf(SPARTA * sparta)50 ReadSurf::ReadSurf(SPARTA *sparta) : Pointers(sparta)
51 {
52   MPI_Comm_rank(world,&me);
53   MPI_Comm_size(world,&nprocs);
54 
55   line = new char[MAXLINE];
56   keyword = new char[MAXLINE];
57   buffer = new char[CHUNK*MAXLINE];
58 }
59 
60 /* ---------------------------------------------------------------------- */
61 
~ReadSurf()62 ReadSurf::~ReadSurf()
63 {
64   delete [] line;
65   delete [] keyword;
66   delete [] buffer;
67 }
68 
69 /* ---------------------------------------------------------------------- */
70 
command(int narg,char ** arg)71 void ReadSurf::command(int narg, char **arg)
72 {
73   if (!grid->exist)
74     error->all(FLERR,"Cannot read_surf before grid is defined");
75   if (surf->implicit)
76     error->all(FLERR,"Cannot read_surf unless global surfs explicit is set");
77 
78   surf->exist = 1;
79   dim = domain->dimension;
80   distributed = surf->distributed;
81 
82   if (narg < 1) error->all(FLERR,"Illegal read_surf command");
83 
84   // if filename contains "*", search dir for latest surface file
85 
86   char *file = new char[strlen(arg[0]) + 16];
87   if (strchr(arg[0],'*')) {
88     int n;
89     if (me == 0) {
90       file_search(arg[0],file);
91       n = strlen(file) + 1;
92     }
93     MPI_Bcast(&n,1,MPI_INT,0,world);
94     MPI_Bcast(file,n,MPI_CHAR,0,world);
95   } else strcpy(file,arg[0]);
96 
97   // check for multiproc files
98 
99   if (strchr(arg[0],'%')) multiproc = 1;
100   else multiproc = 0;
101 
102   if (me == 0)
103     if (screen) fprintf(screen,"Reading surface file ...\n");
104 
105   MPI_Barrier(world);
106   double time1 = MPI_Wtime();
107 
108   // -----------------------
109   // read surface data from file(s)
110   // -----------------------
111 
112   // multiproc = 0/1 = single or multiple files
113   // files may list Points or not
114   // store surfs as distributed or all
115 
116   if (!multiproc) read_single(file);
117   else read_multiple(file);
118 
119   delete [] file;
120 
121   MPI_Barrier(world);
122   double time2 = MPI_Wtime();
123 
124   // -----------------------
125   // transform and check surface elements
126   // -----------------------
127 
128   // check for consecutive IDs
129 
130   check_consecutive();
131 
132   // process command-line args
133   // geometry transformations, group, type, etc
134 
135   process_args(1,narg,arg);
136 
137   // write out new surf file if requested
138   // do this before grid cell assignment or checks, in case an error occurs
139 
140   if (filearg) {
141     WriteSurf *wf = new WriteSurf(sparta);
142     wf->statflag = 0;
143     wf->command(narg-filearg,&arg[filearg]);
144     delete wf;
145   }
146 
147   // output extent of new surfs, tiny ones may have been created by clip
148 
149   if (dim == 2) surf->output_extent(nsurf_old);
150   else surf->output_extent(nsurf_old);
151 
152   // compute normals of new surfs
153 
154   if (dim == 2) surf->compute_line_normal(nsurf_old);
155   else surf->compute_tri_normal(nsurf_old);
156 
157   // error check on new surfs
158   // all points must be inside or on surface of simulation box
159 
160   if (dim == 2) surf->check_point_inside(nsurf_old);
161   else surf->check_point_inside(nsurf_old);
162 
163   // error checks that can be done before surfs are mapped to grid cells
164 
165   if (dim == 2) {
166     surf->check_watertight_2d();
167     check_neighbor_norm_2d();
168   } else {
169     surf->check_watertight_3d();
170     check_neighbor_norm_3d();
171   }
172 
173   MPI_Barrier(world);
174   double time3 = MPI_Wtime();
175 
176   // -----------------------
177   // map surfs to grid cells
178   // -----------------------
179 
180   // sort particles
181 
182   if (particle->exist) particle->sort();
183 
184   // make list of surf elements I own
185   // clear grid of surf info including split cells
186 
187   surf->setup_owned();
188   grid->unset_neighbors();
189   grid->remove_ghosts();
190 
191   if (particle->exist && grid->nsplitlocal) {
192     Grid::ChildCell *cells = grid->cells;
193     int nglocal = grid->nlocal;
194     for (int icell = 0; icell < nglocal; icell++)
195       if (cells[icell].nsplit > 1)
196 	grid->combine_split_cell_particles(icell,1);
197   }
198 
199   grid->clear_surf();
200 
201   MPI_Barrier(world);
202   double time4 = MPI_Wtime();
203 
204   // assign surfs to grid cells
205 
206   grid->surf2grid(1);
207 
208   MPI_Barrier(world);
209   double time5 = MPI_Wtime();
210 
211   // error check on any points too near other surfs
212   // done on per-grid-cell basis, expensive to do globally
213 
214   if (dim == 2) surf->check_point_near_surf_2d();
215   else surf->check_point_near_surf_3d();
216 
217   // re-setup grid ghosts and neighbors
218 
219   grid->setup_owned();
220   grid->acquire_ghosts();
221   grid->reset_neighbors();
222   comm->reset_neighbors();
223 
224   MPI_Barrier(world);
225   double time6 = MPI_Wtime();
226 
227   // flag cells and corners as OUTSIDE or INSIDE
228 
229   grid->set_inout();
230   grid->type_check();
231 
232   // DEBUG
233   //grid->debug();
234 
235   MPI_Barrier(world);
236   double time7 = MPI_Wtime();
237 
238   // remove particles in any cell that is now INSIDE or has new surfs
239   // reassign particles in split cells to sub cell owner
240   // compress particles if any flagged for deletion
241 
242   bigint ndeleted;
243   if (particle->exist) {
244     Grid::ChildCell *cells = grid->cells;
245     Grid::ChildInfo *cinfo = grid->cinfo;
246     int nglocal = grid->nlocal;
247     int delflag = 0;
248 
249     for (int icell = 0; icell < nglocal; icell++) {
250       if (cinfo[icell].type == INSIDE) {
251 	if (partflag == KEEP)
252           error->one(FLERR,"Particles are inside new surfaces");
253 	if (cinfo[icell].count) delflag = 1;
254 	particle->remove_all_from_cell(cinfo[icell].first);
255 	cinfo[icell].count = 0;
256 	cinfo[icell].first = -1;
257 	continue;
258       }
259       if (cells[icell].nsurf && cells[icell].nsplit >= 1) {
260 	int nsurf = cells[icell].nsurf;
261 	surfint *csurfs = cells[icell].csurfs;
262 	int m;
263 	if (dim == 2) {
264           Surf::Line *lines = surf->lines;
265 	  for (m = 0; m < nsurf; m++) {
266 	    if (lines[csurfs[m]].id > nsurf_total_old) break;
267 	  }
268 	} else {
269           Surf::Tri *tris = surf->tris;
270 	  for (m = 0; m < nsurf; m++) {
271 	    if (tris[csurfs[m]].id >= nsurf_total_old) break;
272 	  }
273 	}
274 	if (m < nsurf && partflag == CHECK) {
275 	  if (cinfo[icell].count) delflag = 1;
276 	  particle->remove_all_from_cell(cinfo[icell].first);
277 	  cinfo[icell].count = 0;
278 	  cinfo[icell].first = -1;
279 	}
280       }
281       if (cells[icell].nsplit > 1)
282 	grid->assign_split_cell_particles(icell);
283     }
284     int nlocal_old = particle->nlocal;
285     if (delflag) particle->compress_rebalance();
286     bigint delta = nlocal_old - particle->nlocal;
287     MPI_Allreduce(&delta,&ndeleted,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
288   }
289 
290   MPI_Barrier(world);
291   double time8 = MPI_Wtime();
292 
293   // stats
294 
295   double time_total = time6-time1;
296   double time_s2g = time5-time4;
297 
298   if (comm->me == 0) {
299     if (screen) {
300       if (particle->exist)
301 	fprintf(screen,"  " BIGINT_FORMAT " deleted particles\n",ndeleted);
302       fprintf(screen,"  CPU time = %g secs\n",time_total);
303       fprintf(screen,"  read/check/sort/surf2grid/ghost/"
304               "inout/particle percent = "
305               "%g %g %g %g %g %g %g\n",
306               100.0*(time2-time1)/time_total,100.0*(time3-time2)/time_total,
307               100.0*(time4-time3)/time_total,100.0*(time5-time4)/time_total,
308               100.0*(time6-time5)/time_total,100.0*(time7-time6)/time_total,
309               100.0*(time8-time7)/time_total);
310       fprintf(screen,"  surf2grid time = %g secs\n",time_s2g);
311       fprintf(screen,"  map/comm1/comm2/comm3/comm4/split percent = "
312 	      "%g %g %g %g %g %g\n",
313 	      100.0*grid->tmap/time_s2g,100.0*grid->tcomm1/time_s2g,
314 	      100.0*grid->tcomm2/time_s2g,100.0*grid->tcomm3/time_s2g,
315 	      100.0*grid->tcomm4/time_s2g,100.0*grid->tsplit/time_s2g);
316     }
317 
318     if (logfile) {
319       if (particle->exist)
320 	fprintf(logfile,"  " BIGINT_FORMAT " deleted particles\n",ndeleted);
321       fprintf(logfile,"  CPU time = %g secs\n",time_total);
322       fprintf(logfile,"  read/check/sort/surf2grid/ghost/"
323               "inout/particle percent = "
324               "%g %g %g %g %g %g %g\n",
325               100.0*(time2-time1)/time_total,100.0*(time3-time2)/time_total,
326               100.0*(time4-time3)/time_total,100.0*(time5-time4)/time_total,
327               100.0*(time6-time5)/time_total,100.0*(time7-time6)/time_total,
328               100.0*(time8-time7)/time_total);
329       fprintf(logfile,"  surf2grid time = %g secs\n",time_s2g);
330       fprintf(logfile,"  map/comm1/comm2/comm3/comm4/split percent = "
331 	      "%g %g %g %g %g %g\n",
332 	      100.0*grid->tmap/time_s2g,100.0*grid->tcomm1/time_s2g,
333 	      100.0*grid->tcomm2/time_s2g,100.0*grid->tcomm3/time_s2g,
334 	      100.0*grid->tcomm4/time_s2g,100.0*grid->tsplit/time_s2g);
335     }
336   }
337 }
338 
339 // ----------------------------------------------------------------------
340 // methods to read surface files
341 // ----------------------------------------------------------------------
342 
343 /* ----------------------------------------------------------------------
344    read a single input file
345    store surfs directly in lines/tris (all) or mylines/mytris (distributed)
346 ------------------------------------------------------------------------- */
347 
read_single(char * file)348 void ReadSurf::read_single(char *file)
349 {
350   // surf counts before new read
351 
352   nsurf_total_old = surf->nsurf;
353   if (distributed) nsurf_old = surf->nown;
354   else nsurf_old = surf->nlocal;
355 
356   // read file
357 
358   if (me == 0) filereader = 1;
359   else filereader = 0;
360   filecomm = world;
361   me_file = me;
362   nprocs_file = nprocs;
363 
364   if (distributed) read_file(file,MINE);
365   else read_file(file,LOCAL);
366 
367   // surf counts, stats, error check
368 
369   surf_counts();
370 }
371 
372 /* ----------------------------------------------------------------------
373    read multiple files, cluster of procs for each file
374    store surfs initially in distributed tmplines/tmptris
375    communicate to populate lines/tris (all) or mylines/mytris (distributed)
376 ------------------------------------------------------------------------- */
377 
read_multiple(char * file)378 void ReadSurf::read_multiple(char *file)
379 {
380   base(file);
381 
382   // surf counts before new read
383 
384   nsurf_total_old = surf->nsurf;
385   if (distributed) nsurf_old = surf->nown;
386   else nsurf_old = surf->nlocal;
387 
388   // setup for read into temporary tmplines/tmptris
389 
390   surf->ntmp = surf->nmaxtmp = 0;
391   surf->tmplines = NULL;
392   surf->tmptris = NULL;
393 
394   char *procfile = new char[strlen(file) + 16];
395   char *ptr = strchr(file,'%');
396 
397   // if nprocs > files, break procs into clusters, each cluster reads one file
398 
399   if (nprocs > nfiles) {
400 
401     // icluster = which cluster of procs this proc is in
402     // fileproc = ID of proc in my cluster who reads from file
403     // filereader = 1 if this proc reads file, else 0
404 
405     int icluster = static_cast<int> ((bigint) me * nfiles/nprocs);
406     int fileproc = static_cast<int> ((bigint) icluster * nprocs/nfiles);
407     int fcluster = static_cast<int> ((bigint) fileproc * nfiles/nprocs);
408     if (fcluster < icluster) fileproc++;
409     if (me == fileproc) filereader = 1;
410     else filereader = 0;
411     MPI_Comm_split(world,icluster,0,&filecomm);
412     MPI_Comm_rank(filecomm,&me_file);
413     MPI_Comm_size(filecomm,&nprocs_file);
414 
415     // each cluster reads a file, stores surfs in tmplines/tmptris
416 
417     *ptr = '\0';
418     sprintf(procfile,"%s%d%s",file,icluster,ptr+1);
419     *ptr = '%';
420 
421     read_file(procfile,TEMPSTRIDE);
422 
423   // if nprocs <= files, each proc reads one or more files
424 
425   } else {
426 
427     filereader = 1;
428     MPI_Comm_split(world,me,0,&filecomm);
429     me_file = 0;
430     nprocs_file = 1;
431 
432     // each proc reads every Pth file, stores surfs in tmplines/tmptris
433 
434     surf->ntmp = surf->nmaxtmp = 0;
435     surf->tmplines = NULL;
436     surf->tmptris = NULL;
437 
438     for (int iproc = me; iproc < nfiles; iproc += nprocs) {
439       *ptr = '\0';
440       sprintf(procfile,"%s%d%s",file,iproc,ptr+1);
441       *ptr = '%';
442 
443       read_file(procfile,TEMPALL);
444     }
445   }
446 
447   delete [] procfile;
448 
449   // communicate surf data from tmplines/tmptris to lines/tris or mylines/mytris
450   // for all: perform MPI_Allgatherv
451   // for distributed: rendezvous comm, each proc fills its mylines/mytris
452 
453   if (!distributed) {
454 
455     bigint nbytes;
456     if (dim == 2) nbytes = (bigint) nsurf_basefile * sizeof(Surf::Line);
457     else nbytes = (bigint) nsurf_basefile * sizeof(Surf::Tri);
458     if (nbytes > MAXSMALLINT)
459       error->all(FLERR,"Aggregate surf byte count is too large");
460 
461     int *recvcounts,*displs;
462     memory->create(recvcounts,nprocs,"read_surf:recvcounts");
463     memory->create(displs,nprocs,"read_surf:displs");
464 
465     int n;
466     if (dim == 2) n = surf->ntmp * sizeof(Surf::Line);
467     else n = surf->ntmp * sizeof(Surf::Tri);
468 
469     MPI_Allgather(&n,1,MPI_INT,recvcounts,1,MPI_INT,world);
470     displs[0] = 0;
471     for (int i = 1; i < nprocs; i++) displs[i] = displs[i-1] + recvcounts[i-1];
472 
473     // allocate space in lines/tris for newly read surfs
474     // Allgatherv() puts new surfs at end of old surfs in lines/tris
475 
476     if (nsurf_total_old + nsurf_basefile > surf->nmax) {
477       int old = surf->nmax;
478       surf->nmax = nsurf_total_old + nsurf_basefile;
479       surf->grow(old);
480     }
481 
482     if (dim == 2)
483       MPI_Allgatherv(surf->tmplines,surf->ntmp*sizeof(Surf::Line),MPI_CHAR,
484                      &surf->lines[nsurf_old],recvcounts,displs,MPI_CHAR,world);
485     else
486       MPI_Allgatherv(surf->tmptris,surf->ntmp*sizeof(Surf::Tri),MPI_CHAR,
487                      &surf->tris[nsurf_old],recvcounts,displs,MPI_CHAR,world);
488 
489     // set surf->nlocal to aggregate size of Allgatherv()
490 
491     surf->nlocal = nsurf_total_old + nsurf_basefile;
492 
493     memory->destroy(recvcounts);
494     memory->destroy(displs);
495 
496   } else {
497     // NOTE: this is a big fat kludge
498     // but need to worry about more surfs in P files than in basefile
499     // could check for that earlier
500     bigint ntmp = surf->ntmp;
501     bigint nall;
502     MPI_Allreduce(&ntmp,&nall,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
503     bigint nnew = nall / nprocs;
504     if (me < nall % nprocs) nnew++;
505     if (nnew > MAXSMALLINT)
506       error->one(FLERR,"Too many distributed surfs per processor");
507     int nown_new = nnew;
508 
509     if (dim == 2) surf->redistribute_lines_temporary(nown_new);
510     else surf->redistribute_tris_temporary(nown_new);
511   }
512 
513   // clean-up
514 
515   MPI_Comm_free(&filecomm);
516   memory->sfree(surf->tmplines);
517   memory->sfree(surf->tmptris);
518 
519   // surf counts, stats, error check
520 
521   surf_counts();
522 }
523 
524 /* ----------------------------------------------------------------------
525    check that surf count after file read is correct on all procs
526 ------------------------------------------------------------------------- */
527 
surf_counts()528 void ReadSurf::surf_counts()
529 {
530   // set surf->nsurf based on single file header or base file
531   // set surf_new based on surf->nsurf
532 
533   if (!multiproc) surf->nsurf = nsurf_total_old + nsurf_file;
534   else surf->nsurf = nsurf_total_old + nsurf_basefile;
535 
536   if (distributed) {
537     bigint nnew = surf->nsurf / nprocs;
538     if (me < surf->nsurf % nprocs) nnew++;
539     nsurf_new = nnew;
540   } else nsurf_new = surf->nsurf;
541 
542   // print stats
543 
544   if (me == 0) {
545     if (!multiproc && npoint_file) {
546       if (screen) fprintf(screen,"  %d points\n",npoint_file);
547       if (logfile) fprintf(logfile,"  %d points\n",npoint_file);
548     }
549     if (dim == 2) {
550       if (screen) fprintf(screen,"  " BIGINT_FORMAT " lines\n",surf->nsurf);
551       if (logfile) fprintf(logfile,"  " BIGINT_FORMAT " lines\n",surf->nsurf);
552     }
553     if (dim == 3) {
554       if (screen) fprintf(screen,"  " BIGINT_FORMAT " triangles\n",surf->nsurf);
555       if (logfile) fprintf(logfile,"  " BIGINT_FORMAT " triangles\n",surf->nsurf);
556     }
557   }
558 
559   if (distributed) {
560     bigint n = surf->nown;
561     bigint nall;
562     MPI_Allreduce(&n,&nall,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
563     if (nall != surf->nsurf)
564       error->all(FLERR,"Surface element count does not match file");
565   } else {
566     if (surf->nlocal != surf->nsurf)
567       error->all(FLERR,"Surface element count does not match file");
568   }
569 }
570 
571 /* ----------------------------------------------------------------------
572    read free-format header of data file
573    1st line and blank lines are skipped
574    non-blank lines are checked for header keywords and leading value is read
575    header ends with non-blank line containing no header keyword (or EOF)
576    return line with non-blank line (or empty line if EOF)
577    file does not have to contain points keyword
578 ------------------------------------------------------------------------- */
579 
header()580 void ReadSurf::header()
581 {
582   int n;
583   char *ptr;
584 
585   // skip 1st line of file
586 
587   if (filereader) {
588     char *eof = fgets(line,MAXLINE,fp);
589     if (eof == NULL) error->one(FLERR,"Unexpected end of surf file");
590   }
591 
592   npoint_file = 0;
593   int nline_file = 0;
594   int ntri_file = 0;
595 
596   while (1) {
597 
598     // read a line and bcast length
599 
600     if (filereader) {
601       if (fgets(line,MAXLINE,fp) == NULL) n = 0;
602       else n = strlen(line) + 1;
603     }
604     MPI_Bcast(&n,1,MPI_INT,0,filecomm);
605 
606     // if n = 0 then end-of-file so return with blank line
607 
608     if (n == 0) {
609       line[0] = '\0';
610       return;
611     }
612 
613     // bcast line
614 
615     MPI_Bcast(line,n,MPI_CHAR,0,filecomm);
616 
617     // trim anything from '#' onward
618     // if line is blank, continue
619 
620     if ((ptr = strchr(line,'#'))) *ptr = '\0';
621     if (strspn(line," \t\n\r") == strlen(line)) continue;
622 
623     // search line for header keyword and set corresponding variable
624     // else exit and line will store Section keyword
625 
626     if (strstr(line,"points")) {
627       bigint bnpoint;
628       sscanf(line,BIGINT_FORMAT,&bnpoint);
629       if (bnpoint > MAXSMALLINT)
630         error->one(FLERR,"Read surf npoint is too large");
631       npoint_file = bnpoint;
632     } else if (strstr(line,"lines")) {
633       if (dim == 3)
634 	error->one(FLERR,"Surf file cannot contain lines for 3d simulation");
635       bigint bnline;
636       sscanf(line,BIGINT_FORMAT,&bnline);
637       if (bnline > MAXSMALLINT) error->all(FLERR,"Read surf nline is too large");
638       nsurf_file = nline_file = bnline;
639     } else if (strstr(line,"triangles")) {
640       if (dim == 2)
641 	error->one(FLERR,
642 		   "Surf file cannot contain triangles for 2d simulation");
643       bigint bntri;
644       sscanf(line,BIGINT_FORMAT,&bntri);
645       if (bntri > MAXSMALLINT) error->one(FLERR,"Read surf ntri is too large");
646       nsurf_file = ntri_file = bntri;
647 
648     } else break;
649   }
650 
651   if (dim == 2 && nline_file == 0)
652     error->one(FLERR,"Surf file does not contain lines");
653   if (dim == 3 && ntri_file == 0)
654     error->one(FLERR,"Surf file does not contain triangles");
655 }
656 
657 /* ----------------------------------------------------------------------
658    read free-format base file for multiproc files
659    1st line and blank lines are skipped
660    non-blank lines are checked for header keywords and leading value is read
661 ------------------------------------------------------------------------- */
662 
base(char * file)663 void ReadSurf::base(char *file)
664 {
665   int n;
666   char *ptr;
667 
668   // open base file
669 
670   if (me == 0) {
671     char *hfile;
672     hfile = new char[strlen(file) + 16];
673     char *ptr = strchr(file,'%');
674     *ptr = '\0';
675     sprintf(hfile,"%s%s%s",file,"base",ptr+1);
676     *ptr = '%';
677     fp = fopen(hfile,"r");
678     if (fp == NULL) {
679       char str[128];
680       sprintf(str,"Cannot open surface base file %s",hfile);
681       error->one(FLERR,str);
682     }
683     delete [] hfile;
684   }
685 
686   // skip 1st line of file
687 
688   if (me == 0) {
689     char *eof = fgets(line,MAXLINE,fp);
690     if (eof == NULL) error->one(FLERR,"Unexpected end of surf base file");
691   }
692 
693   // read keywords from file
694 
695   nfiles = 0;
696   bigint nline_basefile = 0;
697   bigint ntri_basefile = 0;
698 
699   while (1) {
700 
701     // read a line and bcast length
702 
703     if (me == 0) {
704       if (fgets(line,MAXLINE,fp) == NULL) n = 0;
705       else n = strlen(line) + 1;
706     }
707     MPI_Bcast(&n,1,MPI_INT,0,world);
708 
709     // if n = 0 then end-of-file so break
710 
711     if (n == 0) break;
712 
713     // bcast line
714 
715     MPI_Bcast(line,n,MPI_CHAR,0,world);
716 
717     // trim anything from '#' onward
718     // if line is blank, continue
719 
720     if ((ptr = strchr(line,'#'))) *ptr = '\0';
721     if (strspn(line," \t\n\r") == strlen(line)) continue;
722 
723     // search line for header keyword and set corresponding variable
724 
725     if (strstr(line,"files")) {
726       sscanf(line,"%d",&nfiles);
727     } else if (strstr(line,"lines")) {
728       if (dim == 3)
729 	error->all(FLERR,"Surf file cannot contain lines for 3d simulation");
730       sscanf(line,BIGINT_FORMAT,&nline_basefile);
731       nsurf_basefile = nline_basefile;
732     } else if (strstr(line,"triangles")) {
733       if (dim == 2)
734 	error->all(FLERR,
735 		   "Surf file cannot contain triangles for 2d simulation");
736       sscanf(line,BIGINT_FORMAT,&ntri_basefile);
737       nsurf_basefile = ntri_basefile;
738 
739     } else error->all(FLERR,"Invalid keyword in surf base file");
740   }
741 
742   if (nfiles == 0)
743     error->all(FLERR,"Surf base file does not contain files");
744   if (dim == 2 && nline_basefile == 0)
745     error->all(FLERR,"Surf base file does not contain lines");
746   if (dim == 3 && ntri_basefile == 0)
747     error->all(FLERR,"Surf base file does not contain triangles");
748 
749   if (me == 0) fclose(fp);
750 }
751 
752 /* ----------------------------------------------------------------------
753    read a single input file, header and post-header sections
754    proc with filereader = 1 reads the file
755    filecomm = MPI communicator for all procs who share this file
756    storeflag = 0/1/2/3 = LOCAL/MINE/TEMPALL/TEMPSTRIDE
757      is passed to read points/lines/tris
758      LOCAL = store surfs in lines/tris
759      MINE = store surfs in mylines/mytris
760      TEMPALL/TEMPSTRIDE = store surfs in tmplines/tmptris
761 ------------------------------------------------------------------------- */
762 
read_file(char * file,int storeflag)763 void ReadSurf::read_file(char *file, int storeflag)
764 {
765   // open file
766 
767   if (filereader) open(file);
768 
769   // read header
770 
771   pts = NULL;
772   header();
773 
774   // for single file & distributed surfs, allocate Surf mylines/mytris now
775   // since read lines/tri will store directly into it
776   // assumes added surf IDs are from 1 to N, checked when added
777 
778   if (storeflag == MINE) {
779     bigint nnew = (surf->nsurf+nsurf_file) / nprocs;
780     if (me < (surf->nsurf+nsurf_file) % nprocs) nnew++;
781     if (nnew > MAXSMALLINT)
782       error->one(FLERR,"Too many distributed surfs per processor");
783     nsurf_new = nnew;    // NOTE: this line is a kludge but needed for now
784     int maxown_old = surf->maxown;
785     surf->nown = surf->maxown = nnew;
786     surf->grow_own(maxown_old);
787   }
788 
789   // read and store data from Points section
790 
791   parse_keyword(1);
792 
793   if (strcmp(keyword,"Points") == 0) {
794     if (npoint_file == 0)
795       error->all(FLERR,"Read_surf file has no points keyword");
796     read_points();
797     parse_keyword(0);
798   } else if (npoint_file)
799     error->one(FLERR,"Read_surf file has no Points section");
800 
801   // read and store data from Lines or Triangles section
802 
803   if (dim == 2) {
804     if (strcmp(keyword,"Lines") != 0)
805       error->one(FLERR,"Read_surf did not find Lines section of surf file");
806     read_lines(storeflag);
807   } else {
808     if (strcmp(keyword,"Triangles") != 0)
809       error->one(FLERR,"Read_surf did not find Triangles section of surf file");
810     read_tris(storeflag);
811   }
812 
813   // can now free Points
814 
815   if (npoint_file) memory->sfree(pts);
816 
817   // close file
818 
819   if (filereader) {
820     if (compressed) pclose(fp);
821     else fclose(fp);
822   }
823 }
824 
825 /* ----------------------------------------------------------------------
826    read all points from a single file
827    store in local pts data struct
828 ------------------------------------------------------------------------- */
829 
read_points()830 void ReadSurf::read_points()
831 {
832   int i,m,n,nchunk;
833   char *next,*buf;
834 
835   bigint nbytes = (bigint) npoint_file * sizeof(Point);
836   pts = (Point *) memory->smalloc(nbytes,"readsurf:pts");
837 
838   // read and broadcast one CHUNK of points at a time
839 
840   int nread = 0;
841 
842   while (nread < npoint_file) {
843     if (npoint_file-nread > CHUNK) nchunk = CHUNK;
844     else nchunk = npoint_file - nread;
845     if (filereader) {
846       char *eof;
847       m = 0;
848       for (i = 0; i < nchunk; i++) {
849 	eof = fgets(&buffer[m],MAXLINE,fp);
850 	if (eof == NULL) error->one(FLERR,"Unexpected end of surf file");
851 	m += strlen(&buffer[m]);
852       }
853       m++;
854     }
855     MPI_Bcast(&m,1,MPI_INT,0,filecomm);
856     MPI_Bcast(buffer,m,MPI_CHAR,0,filecomm);
857 
858     buf = buffer;
859     next = strchr(buf,'\n');
860     *next = '\0';
861     int nwords = input->count_words(buf);
862     *next = '\n';
863 
864     if (dim == 2 && nwords != 3)
865       error->one(FLERR,"Incorrect point format in surf file");
866     if (dim == 3 && nwords != 4)
867       error->one(FLERR,"Incorrect point format in surf file");
868 
869     n = nread;
870 
871     for (int i = 0; i < nchunk; i++) {
872       next = strchr(buf,'\n');
873       strtok(buf," \t\n\r\f");
874       pts[n].x[0] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
875       pts[n].x[1] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
876       if (dim == 3)
877         pts[n].x[2] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
878       else pts[n].x[2] = 0.0;
879       n++;
880       buf = next + 1;
881     }
882 
883     nread += nchunk;
884   }
885 }
886 
887 /* ----------------------------------------------------------------------
888    read Lines section of file
889    storeflag determines how each line is stored
890 ------------------------------------------------------------------------- */
891 
read_lines(int storeflag)892 void ReadSurf::read_lines(int storeflag)
893 {
894   int i,m,nchunk,type,p1,p2;
895   surfint id;
896   double x1[2],x2[2];
897   char *next,*buf;
898 
899   // read and broadcast one CHUNK of lines at a time
900 
901   int nread = 0;
902 
903   while (nread < nsurf_file) {
904     if (nsurf_file - nread > CHUNK) nchunk = CHUNK;
905     else nchunk = nsurf_file - nread;
906     if (filereader) {
907       char *eof;
908       m = 0;
909       for (i = 0; i < nchunk; i++) {
910 	eof = fgets(&buffer[m],MAXLINE,fp);
911 	if (eof == NULL) error->one(FLERR,"Unexpected end of surf file");
912 	m += strlen(&buffer[m]);
913       }
914       m++;
915     }
916     MPI_Bcast(&m,1,MPI_INT,0,filecomm);
917     MPI_Bcast(buffer,m,MPI_CHAR,0,filecomm);
918 
919     buf = buffer;
920     next = strchr(buf,'\n');
921     *next = '\0';
922     int nwords = input->count_words(buf);
923     *next = '\n';
924 
925     // allow for optional type in each line element
926     // different logic depending on whether points included with each line
927 
928     int typeflag = 0;
929 
930     if (npoint_file) {
931       if (nwords != 3 && nwords != 4)
932         error->all(FLERR,"Incorrect line format in surf file");
933       if (nwords == 4) typeflag = 1;
934     } else {
935       if (nwords != 5 && nwords != 6)
936         error->all(FLERR,"Incorrect line format in surf file");
937       if (nwords == 6) typeflag = 1;
938     }
939 
940     // if Points section in file, each read line has indices into it
941     // augment line IDs by previously read surfaces
942     // for storeflag = MINE, only store if I own surf ID
943 
944     if (npoint_file) {
945       for (int i = 0; i < nchunk; i++) {
946         next = strchr(buf,'\n');
947         id = ATOSURFINT(strtok(buf," \t\n\r\f"));
948         id += nsurf_total_old;
949         if (typeflag) type = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
950         else type = 1;
951 
952         p1 = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
953         p2 = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
954         if (p1 < 1 || p1 > npoint_file || p2 < 1 || p2 > npoint_file || p1 == p2)
955           error->all(FLERR,"Invalid point index in Lines section");
956 
957         if (storeflag == LOCAL) {
958           surf->add_line(id,type,pts[p1-1].x,pts[p2-1].x);
959         } else if (storeflag == MINE) {
960           if ((id-1) % nprocs == me) {
961             if ((id-1) / nprocs >= nsurf_new)
962               error->one(FLERR,"Invalid surf ID in read_surf file");
963             surf->add_line_own(id,type,pts[p1-1].x,pts[p2-1].x);
964           }
965         } else if (storeflag == TEMPALL) {
966           surf->add_line_temporary(id,type,pts[p1-1].x,pts[p2-1].x);
967         } else if (storeflag == TEMPSTRIDE) {
968           if (nread+i % nprocs_file == me_file)
969             surf->add_line_temporary(id,type,pts[p1-1].x,pts[p2-1].x);
970         }
971 
972         buf = next + 1;
973       }
974 
975     // if no Points section, each read line has point coords
976     // augment line IDs by previously read surfaces
977     // for storeflag = MINE, only store if I own surf ID
978 
979     } else {
980       for (int i = 0; i < nchunk; i++) {
981         next = strchr(buf,'\n');
982         id = ATOSURFINT(strtok(buf," \t\n\r\f"));
983         id += nsurf_total_old;
984         if (typeflag) type = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
985         else type = 1;
986 
987         x1[0] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
988         x1[1] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
989         x2[0] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
990         x2[1] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
991 
992         if (storeflag == LOCAL) {
993           surf->add_line(id,type,x1,x2);
994         } else if (storeflag == MINE) {
995           if ((id-1) % nprocs == me) {
996             if ((id-1) / nprocs >= nsurf_new)
997               error->one(FLERR,"Invalid surf ID in read_surf file");
998             surf->add_line_own(id,type,x1,x2);
999           }
1000         } else if (storeflag == TEMPALL) {
1001           surf->add_line_temporary(id,type,x1,x2);
1002         } else if (storeflag == TEMPSTRIDE) {
1003           if ((nread+i) % nprocs_file == me_file)
1004             surf->add_line_temporary(id,type,x1,x2);
1005         }
1006 
1007         buf = next + 1;
1008       }
1009     }
1010 
1011     nread += nchunk;
1012   }
1013 }
1014 
1015 /* ----------------------------------------------------------------------
1016    read Triangles section of file
1017    storeflag determines how each tri is stored
1018 ------------------------------------------------------------------------- */
1019 
read_tris(int storeflag)1020 void ReadSurf::read_tris(int storeflag)
1021 {
1022   int i,m,nchunk,type,p1,p2,p3,ipt;
1023   surfint id;
1024   double x1[3],x2[3],x3[3];
1025   char *next,*buf;
1026 
1027   // read and broadcast one CHUNK of triangles at a time
1028 
1029   int nread = 0;
1030 
1031   // DEBUG
1032   int count = 0;
1033 
1034   while (nread < nsurf_file) {
1035     if (nsurf_file - nread > CHUNK) nchunk = CHUNK;
1036     else nchunk = nsurf_file - nread;
1037     if (filereader) {
1038       char *eof;
1039       m = 0;
1040       for (i = 0; i < nchunk; i++) {
1041 	eof = fgets(&buffer[m],MAXLINE,fp);
1042 	if (eof == NULL) error->one(FLERR,"Unexpected end of surf file");
1043 	m += strlen(&buffer[m]);
1044       }
1045       m++;
1046     }
1047     MPI_Bcast(&m,1,MPI_INT,0,filecomm);
1048     MPI_Bcast(buffer,m,MPI_CHAR,0,filecomm);
1049 
1050     buf = buffer;
1051     next = strchr(buf,'\n');
1052     *next = '\0';
1053     int nwords = input->count_words(buf);
1054     *next = '\n';
1055 
1056     // allow for optional type in each line element
1057     // different logic depending on whether points included with each line
1058 
1059     int typeflag = 0;
1060 
1061     if (npoint_file) {
1062       if (nwords != 4 && nwords != 5)
1063         error->all(FLERR,"Incorrect triangle format in surf file");
1064       if (nwords == 5) typeflag = 1;
1065     } else {
1066       if (nwords != 10 && nwords != 11)
1067         error->all(FLERR,"Incorrect triangle format in surf file");
1068       if (nwords == 11) typeflag = 1;
1069     }
1070 
1071     // if Points section in file, each read line has indices into it
1072     // augment tri IDs by previously read surfaces
1073     // for storeflag = MINE, only store if I own surf ID
1074 
1075     if (npoint_file) {
1076       for (int i = 0; i < nchunk; i++) {
1077         next = strchr(buf,'\n');
1078         id = ATOSURFINT(strtok(buf," \t\n\r\f"));
1079         id += nsurf_total_old;
1080         if (typeflag) type = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
1081         else type = 1;
1082 
1083         p1 = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
1084         p2 = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
1085         p3 = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
1086         if (p1 < 1 || p1 > npoint_file || p2 < 1 || p2 > npoint_file ||
1087             p3 < 1 || p3 > npoint_file || p1 == p2 || p2 == p3)
1088           error->all(FLERR,"Invalid point index in Triangles section");
1089 
1090         if (storeflag == LOCAL) {
1091           surf->add_tri(id,type,pts[p1-1].x,pts[p2-1].x,pts[p3-1].x);
1092         } else if (storeflag == MINE) {
1093           if ((id-1) % nprocs == me) {
1094             if ((id-1) / nprocs >= nsurf_new)
1095               error->one(FLERR,"Invalid surf ID in read_surf file");
1096             surf->add_tri_own(id,type,pts[p1-1].x,pts[p2-1].x,pts[p3-1].x);
1097           }
1098         } else if (storeflag == TEMPALL) {
1099           surf->add_tri_temporary(id,type,pts[p1-1].x,pts[p2-1].x,pts[p3-1].x);
1100         } else if (storeflag == TEMPSTRIDE) {
1101           if (nread+i % nprocs_file == me_file)
1102             surf->add_tri_temporary(id,type,pts[p1-1].x,pts[p2-1].x,pts[p3-1].x);
1103         }
1104 
1105         buf = next + 1;
1106       }
1107 
1108     // if no Points section, each read line has point coords
1109     // augment tri IDs by previously read surfaces
1110     // for storeflag = MINE, only store if I own surf ID
1111 
1112     } else {
1113       for (int i = 0; i < nchunk; i++) {
1114         next = strchr(buf,'\n');
1115         id = ATOSURFINT(strtok(buf," \t\n\r\f"));
1116         id += nsurf_total_old;
1117         if (typeflag) type = input->inumeric(FLERR,strtok(NULL," \t\n\r\f"));
1118         else type = 1;
1119 
1120         x1[0] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1121         x1[1] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1122         x1[2] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1123         x2[0] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1124         x2[1] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1125         x2[2] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1126         x3[0] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1127         x3[1] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1128         x3[2] = input->numeric(FLERR,strtok(NULL," \t\n\r\f"));
1129 
1130         if (storeflag == LOCAL) {
1131           surf->add_tri(id,type,x1,x2,x3);
1132         } else if (storeflag == MINE) {
1133           if ((id-1) % nprocs == me) {
1134             if ((id-1) / nprocs >= nsurf_new)
1135               error->one(FLERR,"Invalid surf ID in read_surf file");
1136             surf->add_tri_own(id,type,x1,x2,x3);
1137           }
1138         } else if (storeflag == TEMPALL) {
1139           surf->add_tri_temporary(id,type,x1,x2,x3);
1140         } else if (storeflag == TEMPSTRIDE) {
1141           if ((nread+i) % nprocs_file == me_file) {
1142             surf->add_tri_temporary(id,type,x1,x2,x3);
1143             count++;
1144           }
1145         }
1146 
1147         buf = next + 1;
1148       }
1149     }
1150 
1151     nread += nchunk;
1152   }
1153 }
1154 
1155 // -----------------------
1156 // transform surface elements
1157 // -----------------------
1158 
1159 /* ----------------------------------------------------------------------
1160    apply optional keywords for geometric transformations
1161    store optional keywords for group and type information
1162    store optional keyword for file output
1163 ------------------------------------------------------------------------- */
1164 
process_args(int start,int narg,char ** arg)1165 void ReadSurf::process_args(int start, int narg, char **arg)
1166 {
1167   origin[0] = origin[1] = origin[2] = 0.0;
1168   int grouparg = 0;
1169   int typeadd = 0;
1170   partflag = NONE;
1171   filearg = 0;
1172 
1173   int iarg = start;
1174   while (iarg < narg) {
1175     if (strcmp(arg[iarg],"origin") == 0) {
1176       if (iarg+4 > narg) error->all(FLERR,"Invalid read_surf command");
1177       double ox = atof(arg[iarg+1]);
1178       double oy = atof(arg[iarg+2]);
1179       double oz = atof(arg[iarg+3]);
1180       if (dim == 2 && oz != 0.0)
1181 	error->all(FLERR,"Invalid read_surf geometry transformation "
1182 		   "for 2d simulation");
1183       origin[0] = ox;
1184       origin[1] = oy;
1185       origin[2] = oz;
1186       iarg += 4;
1187     } else if (strcmp(arg[iarg],"trans") == 0) {
1188       if (iarg+4 > narg) error->all(FLERR,"Invalid read_surf command");
1189       double dx = input->numeric(FLERR,arg[iarg+1]);
1190       double dy = input->numeric(FLERR,arg[iarg+2]);
1191       double dz = input->numeric(FLERR,arg[iarg+3]);
1192       if (dim == 2 && dz != 0.0)
1193 	error->all(FLERR,"Invalid read_surf geometry transformation "
1194 		   "for 2d simulation");
1195       origin[0] += dx;
1196       origin[1] += dy;
1197       origin[2] += dz;
1198       translate(dx,dy,dz);
1199       iarg += 4;
1200     } else if (strcmp(arg[iarg],"atrans") == 0) {
1201       if (iarg+4 > narg) error->all(FLERR,"Invalid read_surf command");
1202       double ax = input->numeric(FLERR,arg[iarg+1]);
1203       double ay = input->numeric(FLERR,arg[iarg+2]);
1204       double az = input->numeric(FLERR,arg[iarg+3]);
1205       if (dim == 2 && az != 0.0)
1206 	error->all(FLERR,"Invalid read_surf geometry transformation "
1207 		   "for 2d simulation");
1208       double dx = ax - origin[0];
1209       double dy = ay - origin[1];
1210       double dz = az - origin[2];
1211       origin[0] = ax;
1212       origin[1] = ay;
1213       origin[2] = az;
1214       translate(dx,dy,dz);
1215       iarg += 4;
1216     } else if (strcmp(arg[iarg],"ftrans") == 0) {
1217       if (iarg+4 > narg) error->all(FLERR,"Invalid read_surf command");
1218       double fx = input->numeric(FLERR,arg[iarg+1]);
1219       double fy = input->numeric(FLERR,arg[iarg+2]);
1220       double fz = input->numeric(FLERR,arg[iarg+3]);
1221       if (dim == 2 && fz != 0.5)
1222 	error->all(FLERR,"Invalid read_surf geometry transformation "
1223 		   "for 2d simulation");
1224       double ax = domain->boxlo[0] + fx*domain->xprd;
1225       double ay = domain->boxlo[1] + fy*domain->yprd;
1226       double az;
1227       if (dim == 3) az = domain->boxlo[2] + fz*domain->zprd;
1228       else az = 0.0;
1229       double dx = ax - origin[0];
1230       double dy = ay - origin[1];
1231       double dz = az - origin[2];
1232       origin[0] = ax;
1233       origin[1] = ay;
1234       origin[2] = az;
1235       translate(dx,dy,dz);
1236       iarg += 4;
1237     } else if (strcmp(arg[iarg],"scale") == 0) {
1238       if (iarg+4 > narg) error->all(FLERR,"Invalid read_surf command");
1239       double sx = input->numeric(FLERR,arg[iarg+1]);
1240       double sy = input->numeric(FLERR,arg[iarg+2]);
1241       double sz = input->numeric(FLERR,arg[iarg+3]);
1242       if (dim == 2 && sz != 1.0)
1243 	error->all(FLERR,"Invalid read_surf geometry transformation "
1244 		   "for 2d simulation");
1245       scale(sx,sy,sz);
1246       iarg += 4;
1247     } else if (strcmp(arg[iarg],"rotate") == 0) {
1248       if (iarg+5 > narg) error->all(FLERR,"Invalid read_surf command");
1249       double theta = input->numeric(FLERR,arg[iarg+1]);
1250       double rx = input->numeric(FLERR,arg[iarg+2]);
1251       double ry = input->numeric(FLERR,arg[iarg+3]);
1252       double rz = input->numeric(FLERR,arg[iarg+4]);
1253       if (dim == 2 && (rx != 0.0 || ry != 0.0 || rz != 1.0))
1254 	error->all(FLERR,"Invalid read_surf geometry transformation "
1255 		   "for 2d simulation");
1256       if (rx == 0.0 && ry == 0.0 && rz == 0.0)
1257 	error->all(FLERR,"Invalid read_surf geometry transformation "
1258 		   "for 2d simulation");
1259       rotate(theta,rx,ry,rz);
1260       iarg += 5;
1261     } else if (strcmp(arg[iarg],"invert") == 0) {
1262       invert();
1263       iarg += 1;
1264     } else if (strcmp(arg[iarg],"clip") == 0) {
1265       double frac = 0.0;
1266       if (iarg+1 < narg) {
1267         char c = arg[iarg+1][0];
1268         if (isdigit(c) || c == '-' || c == '+' || c == '.') {
1269           frac = input->numeric(FLERR,arg[iarg+1]);
1270           if (frac < 0.0 || frac >= 0.5)
1271             error->all(FLERR,"Invalid read_surf command");
1272           iarg++;
1273         }
1274       }
1275       if (frac > 0.0) push_points_to_boundary(frac);
1276       if (dim == 2) clip2d();
1277       else clip3d();
1278       iarg++;
1279 
1280     } else if (strcmp(arg[iarg],"group") == 0) {
1281       if (iarg+2 > narg) error->all(FLERR,"Invalid read_surf command");
1282       grouparg = iarg+1;
1283       iarg += 2;
1284 
1285     } else if (strcmp(arg[iarg],"typeadd") == 0) {
1286       if (iarg+2 > narg) error->all(FLERR,"Invalid read_surf command");
1287       typeadd = input->inumeric(FLERR,arg[iarg+1]);
1288       if (typeadd < 0) error->all(FLERR,"Invalid read_surf command");
1289       iarg += 2;
1290 
1291     } else if (strcmp(arg[iarg],"particle") == 0) {
1292       if (iarg+2 > narg) error->all(FLERR,"Invalid read_surf command");
1293       if (strcmp(arg[iarg+1],"none") == 0) partflag = NONE;
1294       else if (strcmp(arg[iarg+1],"check") == 0) partflag = CHECK;
1295       else if (strcmp(arg[iarg+1],"keep") == 0) partflag = KEEP;
1296       else error->all(FLERR,"Invalid read_surf command");
1297       iarg += 2;
1298 
1299     } else if (strcmp(arg[iarg],"transparent") == 0) {
1300       transparent();
1301       iarg++;
1302 
1303     // file must be last keyword, else WriteSurf will flag error
1304 
1305     } else if (strcmp(arg[iarg],"file") == 0) {
1306       if (iarg+2 > narg) error->all(FLERR,"Invalid read_surf command");
1307       filearg = iarg+1;
1308       iarg = narg;
1309 
1310     } else error->all(FLERR,"Invalid read_surf command");
1311   }
1312 
1313   // error test on particles
1314 
1315   if (particle->exist && partflag == NONE)
1316     error->all(FLERR,"Using read_surf particle none when particles exist");
1317 
1318   // if specified, apply group and typeadd keywords
1319   // these reset per-element mask/type info
1320 
1321   if (grouparg) {
1322     int igroup = surf->find_group(arg[grouparg]);
1323     if (igroup < 0) igroup = surf->add_group(arg[grouparg]);
1324     int groupbit = surf->bitmask[igroup];
1325     if (dim == 2) {
1326       Surf::Line *lines;
1327       if (distributed) lines = surf->mylines;
1328       else lines = surf->lines;
1329       for (int i = nsurf_old; i < nsurf_new; i++) lines[i].mask |= groupbit;
1330     } else {
1331       Surf::Tri *tris;
1332       if (distributed) tris = surf->mytris;
1333       else tris = surf->tris;
1334       for (int i = nsurf_old; i < nsurf_new; i++) tris[i].mask |= groupbit;
1335     }
1336   }
1337 
1338   if (typeadd) {
1339     if (dim == 2) {
1340       Surf::Line *lines;
1341       if (distributed) lines = surf->mylines;
1342       else lines = surf->lines;
1343       for (int i = nsurf_old; i < nsurf_new; i++) lines[i].type += typeadd;
1344     } else {
1345       Surf::Tri *tris;
1346       if (distributed) tris = surf->mytris;
1347       else tris = surf->tris;
1348       for (int i = nsurf_old; i < nsurf_new; i++) tris[i].type += typeadd;
1349     }
1350   }
1351 }
1352 
1353 /* ----------------------------------------------------------------------
1354    translate vertices by (dx,dy,dz)
1355    for 2d, dz will be 0.0
1356 ------------------------------------------------------------------------- */
1357 
translate(double dx,double dy,double dz)1358 void ReadSurf::translate(double dx, double dy, double dz)
1359 {
1360   if (dim == 2) {
1361     Surf::Line *lines;
1362     if (distributed) lines = surf->mylines;
1363     else lines = surf->lines;
1364 
1365     for (int i = nsurf_old; i < nsurf_new; i++) {
1366       lines[i].p1[0] += dx;
1367       lines[i].p1[1] += dy;
1368       lines[i].p1[2] += dz;
1369       lines[i].p2[0] += dx;
1370       lines[i].p2[1] += dy;
1371       lines[i].p2[2] += dz;
1372     }
1373 
1374   } else if (dim == 3) {
1375     Surf::Tri *tris;
1376     if (distributed) tris = surf->mytris;
1377     else tris = surf->tris;
1378 
1379     for (int i = nsurf_old; i < nsurf_new; i++) {
1380       tris[i].p1[0] += dx;
1381       tris[i].p1[1] += dy;
1382       tris[i].p1[2] += dz;
1383       tris[i].p2[0] += dx;
1384       tris[i].p2[1] += dy;
1385       tris[i].p2[2] += dz;
1386       tris[i].p3[0] += dx;
1387       tris[i].p3[1] += dy;
1388       tris[i].p3[2] += dz;
1389     }
1390   }
1391 }
1392 
1393 /* ----------------------------------------------------------------------
1394    scale vertices by (sx,sy,sz) around origin
1395    for 2d, do not reset x[2] to avoid epsilon change
1396 ------------------------------------------------------------------------- */
1397 
scale(double sx,double sy,double sz)1398 void ReadSurf::scale(double sx, double sy, double sz)
1399 {
1400   if (dim == 2) {
1401     Surf::Line *lines;
1402     if (distributed) lines = surf->mylines;
1403     else lines = surf->lines;
1404 
1405     for (int i = nsurf_old; i < nsurf_new; i++) {
1406       lines[i].p1[0] = sx*(lines[i].p1[0]-origin[0]) + origin[0];
1407       lines[i].p1[1] = sy*(lines[i].p1[1]-origin[1]) + origin[1];
1408       lines[i].p2[0] = sx*(lines[i].p2[0]-origin[0]) + origin[0];
1409       lines[i].p2[1] = sy*(lines[i].p2[1]-origin[1]) + origin[1];
1410     }
1411 
1412   } else if (dim == 3) {
1413     Surf::Tri *tris;
1414     if (distributed) tris = surf->mytris;
1415     else tris = surf->tris;
1416 
1417     for (int i = nsurf_old; i < nsurf_new; i++) {
1418       tris[i].p1[0] = sx*(tris[i].p1[0]-origin[0]) + origin[0];
1419       tris[i].p1[1] = sy*(tris[i].p1[1]-origin[1]) + origin[1];
1420       tris[i].p1[2] = sz*(tris[i].p1[2]-origin[2]) + origin[2];
1421       tris[i].p2[0] = sx*(tris[i].p2[0]-origin[0]) + origin[0];
1422       tris[i].p2[1] = sy*(tris[i].p2[1]-origin[1]) + origin[1];
1423       tris[i].p2[2] = sz*(tris[i].p2[2]-origin[2]) + origin[2];
1424       tris[i].p3[0] = sx*(tris[i].p3[0]-origin[0]) + origin[0];
1425       tris[i].p3[1] = sy*(tris[i].p3[1]-origin[1]) + origin[1];
1426       tris[i].p3[2] = sz*(tris[i].p3[2]-origin[2]) + origin[2];
1427     }
1428   }
1429 }
1430 
1431 /* ----------------------------------------------------------------------
1432    rotate vertices around origin
1433    for 2d, do not reset x[2] to avoid epsilon change
1434 ------------------------------------------------------------------------- */
1435 
rotate(double theta,double rx,double ry,double rz)1436 void ReadSurf::rotate(double theta, double rx, double ry, double rz)
1437 {
1438   double r[3],q[4],d[3],dnew[3];
1439   double rotmat[3][3];
1440 
1441   theta *= MY_PI/180.0;
1442 
1443   r[0] = rx; r[1] = ry; r[2] = rz;
1444   MathExtra::norm3(r);
1445   MathExtra::axisangle_to_quat(r,theta,q);
1446   MathExtra::quat_to_mat(q,rotmat);
1447 
1448   if (dim == 2) {
1449     Surf::Line *lines;
1450     if (distributed) lines = surf->mylines;
1451     else lines = surf->lines;
1452 
1453     for (int i = nsurf_old; i < nsurf_new; i++) {
1454       d[0] = lines[i].p1[0] - origin[0];
1455       d[1] = lines[i].p1[1] - origin[1];
1456       d[2] = lines[i].p1[2] - origin[2];
1457       MathExtra::matvec(rotmat,d,dnew);
1458       lines[i].p1[0] = dnew[0] + origin[0];
1459       lines[i].p1[1] = dnew[1] + origin[1];
1460 
1461       d[0] = lines[i].p2[0] - origin[0];
1462       d[1] = lines[i].p2[1] - origin[1];
1463       d[2] = lines[i].p2[2] - origin[2];
1464       MathExtra::matvec(rotmat,d,dnew);
1465       lines[i].p2[0] = dnew[0] + origin[0];
1466       lines[i].p2[1] = dnew[1] + origin[1];
1467     }
1468   } else if (dim == 3) {
1469     Surf::Tri *tris;
1470     if (distributed) tris = surf->mytris;
1471     else tris = surf->tris;
1472 
1473     for (int i = nsurf_old; i < nsurf_new; i++) {
1474       d[0] = tris[i].p1[0] - origin[0];
1475       d[1] = tris[i].p1[1] - origin[1];
1476       d[2] = tris[i].p1[2] - origin[2];
1477       MathExtra::matvec(rotmat,d,dnew);
1478       tris[i].p1[0] = dnew[0] + origin[0];
1479       tris[i].p1[1] = dnew[1] + origin[1];
1480       tris[i].p1[2] = dnew[2] + origin[2];
1481 
1482       d[0] = tris[i].p2[0] - origin[0];
1483       d[1] = tris[i].p2[1] - origin[1];
1484       d[2] = tris[i].p2[2] - origin[2];
1485       MathExtra::matvec(rotmat,d,dnew);
1486       tris[i].p2[0] = dnew[0] + origin[0];
1487       tris[i].p2[1] = dnew[1] + origin[1];
1488       tris[i].p2[2] = dnew[2] + origin[2];
1489 
1490       d[0] = tris[i].p3[0] - origin[0];
1491       d[1] = tris[i].p3[1] - origin[1];
1492       d[2] = tris[i].p3[2] - origin[2];
1493       MathExtra::matvec(rotmat,d,dnew);
1494       tris[i].p3[0] = dnew[0] + origin[0];
1495       tris[i].p3[1] = dnew[1] + origin[1];
1496       tris[i].p3[2] = dnew[2] + origin[2];
1497     }
1498   }
1499 }
1500 
1501 /* ----------------------------------------------------------------------
1502    invert vertex ordering within each line or tri
1503    this flips direction of surface normal
1504 ------------------------------------------------------------------------- */
1505 
invert()1506 void ReadSurf::invert()
1507 {
1508   int tmp;
1509   double x[3];
1510 
1511   if (dim == 2) {
1512     Surf::Line *lines;
1513     if (distributed) lines = surf->mylines;
1514     else lines = surf->lines;
1515 
1516     for (int i = nsurf_old; i < nsurf_new; i++) {
1517       memcpy(x,lines[i].p1,3*sizeof(double));
1518       memcpy(lines[i].p1,lines[i].p2,3*sizeof(double));
1519       memcpy(lines[i].p2,x,3*sizeof(double));
1520     }
1521 
1522   } else if (dim == 3) {
1523     Surf::Tri *tris;
1524     if (distributed) tris = surf->mytris;
1525     else tris = surf->tris;
1526 
1527     for (int i = nsurf_old; i < nsurf_new; i++) {
1528       memcpy(x,tris[i].p2,3*sizeof(double));
1529       memcpy(tris[i].p2,tris[i].p3,3*sizeof(double));
1530       memcpy(tris[i].p3,x,3*sizeof(double));
1531     }
1532   }
1533 }
1534 
1535 /* ----------------------------------------------------------------------
1536    clip all lines so fit inside simulation box
1537    may discard some lines completely
1538 ------------------------------------------------------------------------- */
1539 
clip2d()1540 void ReadSurf::clip2d()
1541 {
1542   int i,dim,side,flag1,flag2;
1543   double value,param;
1544   double x[3];
1545   double *p1,*p2,*inpt,*outpt;
1546 
1547   Surf::Line *lines;
1548   if (distributed) lines = surf->mylines;
1549   else lines = surf->lines;
1550 
1551   int *discard;
1552   memory->create(discard,nsurf_new-nsurf_old,"readsurf:discard");
1553   for (i = nsurf_old; i < nsurf_new; i++) discard[i-nsurf_old] = 0;
1554   int discardflag = 0;
1555 
1556   double *boxlo = domain->boxlo;
1557   double *boxhi = domain->boxhi;
1558 
1559   for (int iface = 0; iface < 4; iface++) {
1560     dim = iface / 2;
1561     side = iface % 2;
1562     if (side == 0) value = boxlo[dim];
1563     else value = boxhi[dim];
1564 
1565     // flag each point as (1,0,-1) = outside,on,inside clipping edge
1566     // keep line unchanged:
1567     //   if both pts are inside or on clipping edge
1568     //   at least one pt is inside clipping edge
1569     // discard line:
1570     //   if both pts are either outside or on clipping edge
1571     // straddle line:
1572     //   one pt is inside, one pt is outside
1573     //   replace outside pt with pt on the clipping edge
1574 
1575     for (i = nsurf_old; i < nsurf_new; i++) {
1576       if (discard[i-nsurf_old]) continue;
1577 
1578       p1 = lines[i].p1;
1579       p2 = lines[i].p2;
1580 
1581       if (side == 0) {
1582         if (p1[dim] < value) flag1 = 1;
1583         else if (p1[dim] > value) flag1 = -1;
1584         else flag1 = 0;
1585         if (p2[dim] < value) flag2 = 1;
1586         else if (p2[dim] > value) flag2 = -1;
1587         else flag2 = 0;
1588       } else {
1589         if (p1[dim] > value) flag1 = 1;
1590         else if (p1[dim] < value) flag1 = -1;
1591         else flag1 = 0;
1592         if (p2[dim] > value) flag2 = 1;
1593         else if (p2[dim] < value) flag2 = -1;
1594         else flag2 = 0;
1595       }
1596 
1597       if (flag1 < 0 && flag2 <= 0) continue;
1598       if (flag1 <= 0 && flag2 < 0) continue;
1599 
1600       if (flag1 >= 0 && flag2 >= 0) {
1601         discard[i-nsurf_old] = 1;
1602         discardflag = 1;
1603         continue;
1604       }
1605 
1606       if (flag1 < 0) {
1607         inpt = p1;
1608         outpt = p2;
1609       } else {
1610         inpt = p2;
1611         outpt = p1;
1612       }
1613 
1614       // reset outpt to intersection of line with clipping edge
1615       // last line insures outpt is exactly on clipping edge
1616 
1617       param = (value-inpt[dim]) / (outpt[dim]-inpt[dim]);
1618       outpt[0] = inpt[0] + param*(outpt[0]-inpt[0]);
1619       outpt[1] = inpt[1] + param*(outpt[1]-inpt[1]);
1620       outpt[dim] = value;
1621     }
1622   }
1623 
1624   // remove deleted lines
1625 
1626   int n = nsurf_old;
1627   for (i = nsurf_old; i < nsurf_new; i++) {
1628     if (!discard[i-nsurf_old]) {
1629       if (n != i) memcpy(&lines[n],&lines[i],sizeof(Surf::Line));
1630       n++;
1631     }
1632   }
1633   nsurf_new = n;
1634 
1635   // clean up
1636 
1637   memory->destroy(discard);
1638 
1639   // reset nsurf,nlocal,nown counts in Surf
1640 
1641   if (!distributed) surf->nsurf = surf->nlocal = nsurf_new;
1642   else {
1643     surf->nown = nsurf_new;
1644     bigint bnown = surf->nown;
1645     MPI_Allreduce(&bnown,&surf->nsurf,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1646   }
1647 
1648   // if discarded any surfs:
1649   // if non-distributed:
1650   //   renumber all IDs for new surfs from nsurf_old to nsurf_new
1651   // if distributed:
1652   //    MPI_Scan to find unique IDs for me,
1653   //    reset IDs for new surfs in mylines,
1654   //    perform rendezvous to send just new surfs to new owners
1655   //    reset nsurf_new, surf->nsurf
1656 
1657   int discardany;
1658   MPI_Allreduce(&discardflag,&discardany,1,MPI_INT,MPI_MAX,world);
1659 
1660   if (discardany) {
1661     if (!distributed) {
1662       for (i = nsurf_old; i < nsurf_new; i++) lines[i].id = i+1;
1663 
1664     } else {
1665       bigint delta = nsurf_new - nsurf_old;
1666       bigint offset;
1667       MPI_Scan(&delta,&offset,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1668       offset -= delta;
1669       for (i = nsurf_old; i < nsurf_new; i++)
1670         lines[i].id = static_cast<surfint>
1671           (nsurf_total_old+offset + i-nsurf_old + 1);
1672 
1673       delta = nsurf_new;
1674       MPI_Allreduce(&delta,&offset,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1675       nsurf_new = offset/nprocs;
1676       if (me < offset % nprocs) nsurf_new++;
1677 
1678       surf->redistribute_lines_clip(nsurf_old,nsurf_new);  // sets surf->nown
1679 
1680       nsurf_new = surf->nown;
1681       bigint bnown = surf->nown;
1682       MPI_Allreduce(&bnown,&surf->nsurf,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1683     }
1684   }
1685 
1686   // check that surf IDs are still 1 to N
1687 
1688   check_consecutive();
1689 
1690   // stats
1691 
1692   bigint delta = surf->nsurf - nsurf_total_old;
1693 
1694   if (me == 0) {
1695     if (screen) fprintf(screen,"  clipped to " BIGINT_FORMAT " lines\n",delta);
1696     if (logfile) fprintf(logfile,"  clipped to " BIGINT_FORMAT " lines\n",delta);
1697   }
1698 }
1699 
1700 /* ----------------------------------------------------------------------
1701    clip all tris so fit inside simulation box
1702    may discard some tris and their points completely
1703    new points and tris may be added which touch box surface
1704    condense data structures by removing deleted points & tris
1705 ------------------------------------------------------------------------- */
1706 
clip3d()1707 void ReadSurf::clip3d()
1708 {
1709   int i,dim,side,flag1,flag2,flag3,nin,ntri_add;
1710   double value,param;
1711   double x1[3],x2[3];
1712   double *p1,*p2,*p3,*in1,*in2,*out1,*out2;
1713 
1714   Surf::Tri *tris;
1715   if (distributed) tris = surf->mytris;
1716   else tris = surf->tris;
1717 
1718   // discard flag for each surf
1719   // will be augmented in loop if tris are added
1720 
1721   int *discard;
1722   int maxdiscard = nsurf_new - nsurf_old;
1723   memory->create(discard,maxdiscard,"readsurf:discard");
1724   for (i = 0; i < maxdiscard; i++) discard[i] = 0;
1725   int discardflag = 0;
1726   int addflag = 0;
1727 
1728   double *boxlo = domain->boxlo;
1729   double *boxhi = domain->boxhi;
1730 
1731   for (int iface = 0; iface < 6; iface++) {
1732     dim = iface / 2;
1733     side = iface % 2;
1734     if (side == 0) value = boxlo[dim];
1735     else value = boxhi[dim];
1736 
1737     // flag each point as (1,0,-1) = outside,on,inside clipping edge
1738     // keep tri unchanged:
1739     //   if all pts are inside or on clipping plane
1740     //   at least one pt is inside clipping plane
1741     // discard tri:
1742     //   if all pts are either outside or on clipping plane
1743     // straddle tri:
1744     //   at least one pt is inside, at least one pt is outside
1745     //   replace outside pts with pts on the clipping plane
1746     //   if 1 pt is inside, triangle remains
1747     //   if 2 pts are inside, trapezoid remains, convert to 2 tris
1748     //   latter requires adding a triangle to Surf data struct
1749 
1750     ntri_add = 0;
1751     for (i = nsurf_old; i < nsurf_new; i++) {
1752       if (discard[i-nsurf_old]) continue;
1753 
1754       p1 = tris[i].p1;
1755       p2 = tris[i].p2;
1756       p3 = tris[i].p3;
1757 
1758       if (side == 0) {
1759         if (p1[dim] < value) flag1 = 1;
1760         else if (p1[dim] > value) flag1 = -1;
1761         else flag1 = 0;
1762         if (p2[dim] < value) flag2 = 1;
1763         else if (p2[dim] > value) flag2 = -1;
1764         else flag2 = 0;
1765         if (p3[dim] < value) flag3 = 1;
1766         else if (p3[dim] > value) flag3 = -1;
1767         else flag3 = 0;
1768       } else {
1769         if (p1[dim] > value) flag1 = 1;
1770         else if (p1[dim] < value) flag1 = -1;
1771         else flag1 = 0;
1772         if (p2[dim] > value) flag2 = 1;
1773         else if (p2[dim] < value) flag2 = -1;
1774         else flag2 = 0;
1775         if (p3[dim] > value) flag3 = 1;
1776         else if (p3[dim] < value) flag3 = -1;
1777         else flag3 = 0;
1778       }
1779 
1780       if (flag1 < 0 && flag2 <= 0 && flag3 <= 0) continue;
1781       if (flag1 <= 0 && flag2 < 0 && flag3 <= 0) continue;
1782       if (flag1 <= 0 && flag2 <= 0 && flag3 < 0) continue;
1783 
1784       if (flag1 >= 0 && flag2 >= 0 && flag3 >= 0) {
1785         discard[i-nsurf_old] = 1;
1786         discardflag += 1;
1787         continue;
1788       }
1789 
1790       // nin = # of inside pts
1791 
1792       nin = 0;
1793       if (flag1 < 0) nin++;
1794       if (flag2 < 0) nin++;
1795       if (flag3 < 0) nin++;
1796 
1797       // one pt is inside
1798       // one or both of other 2 pts are outside
1799       // make a clip with each other pt that is outside
1800       // no new triangle is formed, existing tri is just changed
1801 
1802       if (nin == 1) {
1803         if (flag1 < 0) {
1804           in1 = p1;
1805           out1 = p2;
1806           out2 = p3;
1807         } else if (flag2 < 0) {
1808           in1 = p2;
1809           out1 = p3;
1810           out2 = p1;
1811         } else {
1812           in1 = p3;
1813           out1 = p1;
1814           out2 = p2;
1815         }
1816         if (out1[dim] != value) {
1817           param = (value-in1[dim]) / (out1[dim]-in1[dim]);
1818           out1[0] = in1[0] + param*(out1[0]-in1[0]);
1819           out1[1] = in1[1] + param*(out1[1]-in1[1]);
1820           out1[2] = in1[2] + param*(out1[2]-in1[2]);
1821           out1[dim] = value;
1822         }
1823         if (out2[dim] != value) {
1824           param = (value-in1[dim]) / (out2[dim]-in1[dim]);
1825           out2[0] = in1[0] + param*(out2[0]-in1[0]);
1826           out2[1] = in1[1] + param*(out2[1]-in1[1]);
1827           out2[2] = in1[2] + param*(out2[2]-in1[2]);
1828           out2[dim] = value;
1829         }
1830       }
1831 
1832       // two pts are inside, one pt is outside
1833       // set in1,in2,out1 so all 3 of these tris have same consistent normal
1834       // straddle tri = (in1,in2,out1)
1835       // modified tri = (in1,in2,x2)
1836       // added tri = (in1,x2,x1)
1837       // x1 = clip pt between in1 and out1
1838       // x2 = clip pt between in2 and out1
1839       // x1 and x2 will be computed exactly the same by a tri sharing the edge
1840 
1841       if (nin == 2) {
1842         if (flag1 > 0) {
1843           out1 = p1;
1844           in1 = p2;
1845           in2 = p3;
1846         } else if (flag2 > 0) {
1847           out1 = p2;
1848           in1 = p3;
1849           in2 = p1;
1850         } else {
1851           out1 = p3;
1852           in1 = p1;
1853           in2 = p2;
1854         }
1855 
1856         param = (value-in1[dim]) / (out1[dim]-in1[dim]);
1857         x1[0] = in1[0] + param*(out1[0]-in1[0]);
1858         x1[1] = in1[1] + param*(out1[1]-in1[1]);
1859         x1[2] = in1[2] + param*(out1[2]-in1[2]);
1860         x1[dim] = value;
1861 
1862         param = (value-in2[dim]) / (out1[dim]-in2[dim]);
1863         x2[0] = in2[0] + param*(out1[0]-in2[0]);
1864         x2[1] = in2[1] + param*(out1[1]-in2[1]);
1865         x2[2] = in2[2] + param*(out1[2]-in2[2]);
1866         x2[dim] = value;
1867 
1868         // reset one point in modified tri
1869 
1870         memcpy(out1,x2,3*sizeof(double));
1871 
1872         // add a new tri
1873         // use same ID as modified tri for now, will renumber below
1874 
1875         if (nsurf_new-nsurf_old+ntri_add == maxdiscard) {
1876           maxdiscard += DELTA_DISCARD;
1877           memory->grow(discard,maxdiscard,"readsurf:discard");
1878         }
1879 
1880         // can't use "in1" pointer in surf->add_tri because it points to
1881         //   surf->tris, which may be realloc'd in surf->add_tri
1882 
1883         double in1_copy[3];
1884         in1_copy[0] = in1[0];
1885         in1_copy[1] = in1[1];
1886         in1_copy[2] = in1[2];
1887 
1888         if (distributed) {
1889           surf->add_tri_own_clip(tris[i].id,tris[i].type,in1_copy,x2,x1);
1890           tris = surf->mytris;
1891         } else {
1892           surf->add_tri(tris[i].id,tris[i].type,in1_copy,x2,x1);
1893           tris = surf->tris;
1894         }
1895 
1896         discard[nsurf_new-nsurf_old+ntri_add] = 0;
1897         addflag += 1;
1898         ntri_add++;
1899       }
1900     }
1901 
1902     // increment nsurf_new by triangles added when clipping on one face
1903 
1904     nsurf_new += ntri_add;
1905   }
1906 
1907   // remove deleted tris
1908 
1909   int n = nsurf_old;
1910   for (i = nsurf_old; i < nsurf_new; i++) {
1911     if (!discard[i-nsurf_old]) {
1912       if (n != i) memcpy(&tris[n],&tris[i],sizeof(Surf::Tri));
1913       n++;
1914     }
1915   }
1916   nsurf_new = n;
1917 
1918   // clean up
1919 
1920   memory->destroy(discard);
1921 
1922   // reset nsurf,nlocal,nown counts in Surf
1923 
1924   if (!distributed) surf->nsurf = surf->nlocal = nsurf_new;
1925   else {
1926     surf->nown = nsurf_new;
1927     bigint bnown = surf->nown;
1928     MPI_Allreduce(&bnown,&surf->nsurf,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1929   }
1930 
1931   // if discarded any surfs:
1932   // if non-distributed:
1933   //   renumber all IDs for new surfs from nsurf_old to nsurf_new
1934   // if distributed:
1935   //    MPI_Scan to find unique IDs for mine,
1936   //    reset IDs for new surfs in mytris,
1937   //    perform rendezvous to send just new surfs to new owners
1938   //    reset nsurf_new, surf->nsurf
1939 
1940   int changeflag = discardflag + addflag;
1941   int changeany;
1942   MPI_Allreduce(&changeflag,&changeany,1,MPI_INT,MPI_MAX,world);
1943 
1944   if (changeany) {
1945     if (!distributed) {
1946       for (i = nsurf_old; i < nsurf_new; i++) tris[i].id = i+1;
1947 
1948     } else {
1949       bigint delta = nsurf_new - nsurf_old;
1950       bigint offset;
1951       MPI_Scan(&delta,&offset,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1952       offset -= delta;
1953       for (i = nsurf_old; i < nsurf_new; i++)
1954         tris[i].id = static_cast<surfint>
1955           (nsurf_total_old+offset + i-nsurf_old + 1);
1956 
1957       delta = nsurf_new;
1958       MPI_Allreduce(&delta,&offset,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1959       nsurf_new = offset/nprocs;
1960       if (me < offset % nprocs) nsurf_new++;
1961 
1962       surf->redistribute_tris_clip(nsurf_old,nsurf_new);  // sets surf->nown
1963 
1964       nsurf_new = surf->nown;
1965       bigint bnown = surf->nown;
1966       MPI_Allreduce(&bnown,&surf->nsurf,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
1967     }
1968   }
1969 
1970   // check that surf IDs are still 1 to N
1971 
1972   check_consecutive();
1973 
1974   // stats
1975 
1976   bigint delta = surf->nsurf - nsurf_total_old;
1977 
1978   if (me == 0) {
1979     if (screen) fprintf(screen,"  clipped to " BIGINT_FORMAT " tris\n",delta);
1980     if (logfile) fprintf(logfile,"  clipped to " BIGINT_FORMAT " tris\n",delta);
1981   }
1982 }
1983 
1984 /* ----------------------------------------------------------------------
1985    set transparent flag of all surface elements read in
1986 ------------------------------------------------------------------------- */
1987 
transparent()1988 void ReadSurf::transparent()
1989 {
1990   if (dim == 2) {
1991     Surf::Line *lines;
1992     if (distributed) lines = surf->mylines;
1993     else lines = surf->lines;
1994 
1995     for (int i = nsurf_old; i < nsurf_new; i++)
1996       lines[i].transparent = 1;
1997 
1998   } else if (dim == 3) {
1999     Surf::Tri *tris;
2000     if (distributed) tris = surf->mytris;
2001     else tris = surf->tris;
2002 
2003     for (int i = nsurf_old; i < nsurf_new; i++)
2004       tris[i].transparent = 1;
2005   }
2006 }
2007 
2008 /* ----------------------------------------------------------------------
2009    check that all surf IDs are consecutive from 1 to N
2010    whether distributed or not
2011    only checks if min/max surf IDs are valid, not if truly consecutive
2012 ------------------------------------------------------------------------- */
2013 
check_consecutive()2014 void ReadSurf::check_consecutive()
2015 {
2016   int n;
2017   surfint id;
2018   Surf::Line *lines;
2019   Surf::Tri *tris;
2020 
2021   if (surf->nsurf == 0) return;
2022 
2023   bigint smin = surf->nsurf;
2024   bigint smax = 0;
2025 
2026   if (distributed) n = surf->nown;
2027   else n = surf->nlocal;
2028 
2029   if (dim == 2) {
2030     if (distributed) lines = surf->mylines;
2031     else lines = surf->lines;
2032     for (int i = 0; i < n; i++) {
2033       id = lines[i].id;
2034       smin = MIN(smin,id);
2035       smax = MAX(smax,id);
2036     }
2037   } else {
2038     if (distributed) tris = surf->mytris;
2039     else tris = surf->tris;
2040     for (int i = 0; i < n; i++) {
2041       id = tris[i].id;
2042       smin = MIN(smin,id);
2043       smax = MAX(smax,id);
2044     }
2045   }
2046 
2047   bigint sminall,smaxall;
2048   MPI_Allreduce(&smin,&sminall,1,MPI_SPARTA_BIGINT,MPI_MIN,world);
2049   MPI_Allreduce(&smax,&smaxall,1,MPI_SPARTA_BIGINT,MPI_MAX,world);
2050 
2051   if (sminall != 1) {
2052     char str[128];
2053     sprintf(str,"Read_surf minimum surface ID is " BIGINT_FORMAT,sminall);
2054     error->all(FLERR,str);
2055   }
2056 
2057   if (smaxall != surf->nsurf) {
2058     char str[128];
2059     sprintf(str,"Read_surf maximum surface ID is " BIGINT_FORMAT,smaxall);
2060     error->all(FLERR,str);
2061   }
2062 }
2063 
2064 /* ----------------------------------------------------------------------
2065    push all points to box boundary that are just inside
2066    1Dec20 added just outside logic
2067    delta = user-specified frac * (hi-lo)
2068    this avoids tiny clipped surf elements
2069 ------------------------------------------------------------------------- */
2070 
push_points_to_boundary(double frac)2071 void ReadSurf::push_points_to_boundary(double frac)
2072 {
2073   int i,j;
2074   double *x;
2075 
2076   double *boxlo = domain->boxlo;
2077   double *boxhi = domain->boxhi;
2078 
2079   double xdelta = frac * (boxhi[0]-boxlo[0]);
2080   double ydelta = frac * (boxhi[1]-boxlo[1]);
2081   double zdelta = frac * (boxhi[2]-boxlo[2]);
2082 
2083   if (dim == 2) {
2084     Surf::Line *lines;
2085     if (distributed) lines = surf->mylines;
2086     else lines = surf->lines;
2087 
2088     for (i = nsurf_old; i < nsurf_new; i++) {
2089       for (j = 0; j < 2; j++) {
2090         if (j == 0) x = lines[i].p1;
2091         else x = lines[i].p2;
2092 
2093 	if (fabs(x[0]-boxlo[0]) < xdelta) x[0] = boxlo[0];
2094 	if (fabs(x[0]-boxhi[0]) < xdelta) x[0] = boxhi[0];
2095 
2096 	if (fabs(x[1]-boxlo[1]) < ydelta) x[1] = boxlo[1];
2097 	if (fabs(x[1]-boxhi[1]) < ydelta) x[1] = boxhi[1];
2098 
2099 	/*
2100         if (x[0] >= boxlo[0] && x[0] <= boxhi[0]) {
2101           if (x[0]-boxlo[0] < xdelta) x[0] = boxlo[0];
2102           else if (boxhi[0]-x[0] < xdelta) x[0] = boxhi[0];
2103         }
2104         if (x[1] >= boxlo[1] && x[1] <= boxhi[1]) {
2105           if (x[1]-boxlo[1] < ydelta) x[1] = boxlo[1];
2106           else if (boxhi[1]-x[1] < ydelta) x[1] = boxhi[1];
2107         }
2108 	*/
2109       }
2110     }
2111 
2112   } else if (dim == 3) {
2113     Surf::Tri *tris;
2114     if (distributed) tris = surf->mytris;
2115     else tris = surf->tris;
2116 
2117     for (int i = nsurf_old; i < nsurf_new; i++) {
2118       for (j = 0; j < 3; j++) {
2119         if (j == 0) x = tris[i].p1;
2120         else if (j == 1) x = tris[i].p2;
2121         else x = tris[i].p3;
2122 
2123 	if (fabs(x[0]-boxlo[0]) < xdelta) x[0] = boxlo[0];
2124 	if (fabs(x[0]-boxhi[0]) < xdelta) x[0] = boxhi[0];
2125 
2126 	if (fabs(x[1]-boxlo[1]) < ydelta) x[1] = boxlo[1];
2127 	if (fabs(x[1]-boxhi[1]) < ydelta) x[1] = boxhi[1];
2128 
2129 	if (fabs(x[2]-boxlo[2]) < zdelta) x[2] = boxlo[2];
2130 	if (fabs(x[2]-boxhi[2]) < zdelta) x[2] = boxhi[2];
2131 
2132 	/*
2133         if (x[0] >= boxlo[0] && x[0] <= boxhi[0]) {
2134           if (x[0]-boxlo[0] < xdelta) x[0] = boxlo[0];
2135           else if (boxhi[0]-x[0] < xdelta) x[0] = boxhi[0];
2136         }
2137         if (x[1] >= boxlo[1] && x[1] <= boxhi[1]) {
2138           if (x[1]-boxlo[1] < ydelta) x[1] = boxlo[1];
2139           else if (boxhi[1]-x[1] < ydelta) x[1] = boxhi[1];
2140         }
2141         if (x[2] >= boxlo[2] && x[2] <= boxhi[2]) {
2142           if (x[2]-boxlo[2] < zdelta) x[2] = boxlo[2];
2143           else if (boxhi[2]-x[2] < zdelta) x[2] = boxhi[2];
2144         }
2145 	*/
2146       }
2147     }
2148   }
2149 }
2150 
2151 /* ----------------------------------------------------------------------
2152    check norms of adjacent lines
2153    error if dot product of 2 norms is -1 -> infinitely thin surf
2154    warn if closer than EPSILON_NORM to -1
2155 ------------------------------------------------------------------------- */
2156 
check_neighbor_norm_2d()2157 void ReadSurf::check_neighbor_norm_2d()
2158 {
2159   // NOTE: need to enable this for distributed
2160   // NOTE: and for all, now that surfs are stored with point coords
2161 
2162   if (distributed || !distributed) return;
2163 
2164   int p1,p2;
2165 
2166   // count[I] = # of lines that vertex I is part of
2167 
2168   int *count;
2169   int **p2e;
2170   memory->create(count,npoint_file,"readsurf:count");
2171   memory->create(p2e,npoint_file,2,"readsurf:count");
2172   for (int i = 0; i < npoint_file; i++) count[i] = 0;
2173 
2174   for (int i = 0; i < nsurf_file; i++) {
2175     //p1 = lines[i].p1;
2176     //p2 = lines[i].p2;
2177     p2e[p1][count[p1]++] = i;
2178     p2e[p2][count[p2]++] = i;
2179   }
2180 
2181   // check that norms of adjacent lines are not in opposite directions
2182   // norms are stored in Surf::lines, at end of orignal nsurf_old surfs
2183 
2184   Surf::Line *surflines = surf->lines;
2185 
2186   double dot;
2187   double *norm1,*norm2;
2188 
2189   int nerror = 0;
2190   int nwarn = 0;
2191   for (int i = 0; i < npoint_file; i++) {
2192     if (count[i] == 1) continue;
2193     norm1 = surflines[p2e[i][0] + nsurf_old].norm;
2194     norm2 = surflines[p2e[i][1] + nsurf_old].norm;
2195     dot = MathExtra::dot3(norm1,norm2);
2196     if (dot <= -1.0) nerror++;
2197     else if (dot < -1.0+EPSILON_NORM) nwarn++;
2198   }
2199 
2200   if (nerror) {
2201     char str[128];
2202     sprintf(str,"Surface check failed with %d "
2203             "infinitely thin line pairs",nerror);
2204     error->all(FLERR,str);
2205   }
2206   if (nwarn) {
2207     char str[128];
2208     sprintf(str,"Surface check found %d "
2209             "nearly infinitely thin line pairs",nwarn);
2210     if (me == 0) error->warning(FLERR,str);
2211   }
2212 
2213   // clean up
2214 
2215   memory->destroy(count);
2216   memory->destroy(p2e);
2217 }
2218 
2219 /* ----------------------------------------------------------------------
2220    check norms of new adjacent triangles
2221    error if dot product of 2 norms is -1 -> infinitely thin surf
2222    warn if closer than EPSILON_NORM to -1
2223 ------------------------------------------------------------------------- */
2224 
check_neighbor_norm_3d()2225 void ReadSurf::check_neighbor_norm_3d()
2226 {
2227   // NOTE: need to enable this for distributed
2228   // NOTE: and for all, now that surfs are stored with point coords
2229 
2230   if (distributed || !distributed) return;
2231 
2232   int ntri_file;
2233 
2234   // hash directed edges of all triangles
2235   // key = directed edge, value = triangle it is part of
2236   // NOTE: could prealloc hash to correct size here
2237 
2238   MyHash hash;
2239   MyIterator it;
2240 
2241   // insert each edge into hash with triangle index as value
2242 
2243   bigint p1,p2,p3,key;
2244 
2245   for (int i = 0; i < ntri_file; i++) {
2246     //p1 = tris[i].p1;
2247     //p2 = tris[i].p2;
2248     //p3 = tris[i].p3;
2249     key = (p1 << 32) | p2;
2250     hash[key] = i;
2251     key = (p2 << 32) | p3;
2252     hash[key] = i;
2253     key = (p3 << 32) | p1;
2254     hash[key] = i;
2255   }
2256 
2257   // check that norms of adjacent triangles are not in opposite directions
2258   // norms are stored in Surf::tris, at end of orignal nsurf_old surfs
2259 
2260   Surf::Tri *surftris = surf->tris;
2261 
2262   double dot;
2263   double *norm1,*norm2;
2264 
2265   int nerror = 0;
2266   int nwarn = 0;
2267   for (it = hash.begin(); it != hash.end(); ++it) {
2268     p1 = it->first >> 32;
2269     p2 = it->first & MAXSMALLINT;
2270     key = (p2 << 32) | p1;
2271     if (hash.find(key) == hash.end()) continue;
2272     norm1 = surftris[it->second + nsurf_old].norm;
2273     norm2 = surftris[hash[key] + nsurf_old].norm;
2274     dot = MathExtra::dot3(norm1,norm2);
2275     if (dot <= -1.0) nerror++;
2276     else if (dot < -1.0+EPSILON_NORM) nwarn++;
2277   }
2278 
2279   if (nerror) {
2280     char str[128];
2281     sprintf(str,"Surface check failed with %d "
2282             "infinitely thin triangle pairs",nerror);
2283     error->all(FLERR,str);
2284   }
2285   if (nwarn) {
2286     char str[128];
2287     sprintf(str,"Surface check found %d "
2288             "nearly infinitely thin triangle pairs",nwarn);
2289     if (me == 0) error->warning(FLERR,str);
2290   }
2291 }
2292 
2293 /* ----------------------------------------------------------------------
2294    reading proc opens data file
2295    test if gzipped
2296    sets fp = file pointer
2297 ------------------------------------------------------------------------- */
2298 
open(char * file)2299 void ReadSurf::open(char *file)
2300 {
2301   compressed = 0;
2302   char *suffix = file + strlen(file) - 3;
2303   if (suffix > file && strcmp(suffix,".gz") == 0) compressed = 1;
2304   if (!compressed) fp = fopen(file,"r");
2305   else {
2306 #ifdef SPARTA_GZIP
2307     char gunzip[128];
2308     sprintf(gunzip,"gunzip -c %s",file);
2309     fp = popen(gunzip,"r");
2310 #else
2311     error->one(FLERR,"Cannot open gzipped file");
2312 #endif
2313   }
2314 
2315   if (fp == NULL) {
2316     char str[128];
2317     sprintf(str,"Cannot open file %s",file);
2318     error->one(FLERR,str);
2319   }
2320 }
2321 
2322 /* ----------------------------------------------------------------------
2323    infile contains a "*"
2324    search for all files which match the infile pattern
2325    replace "*" with latest timestep value to create outfile name
2326    search dir referenced by initial pathname of file
2327    if infile also contains "%", use "base" when searching directory
2328    only called by proc 0
2329 ------------------------------------------------------------------------- */
2330 
file_search(char * infile,char * outfile)2331 void ReadSurf::file_search(char *infile, char *outfile)
2332 {
2333   char *ptr;
2334 
2335   // separate infile into dir + filename
2336 
2337   char *dirname = new char[strlen(infile) + 1];
2338   char *filename = new char[strlen(infile) + 1];
2339 
2340   if (strchr(infile,'/')) {
2341     ptr = strrchr(infile,'/');
2342     *ptr = '\0';
2343     strcpy(dirname,infile);
2344     strcpy(filename,ptr+1);
2345     *ptr = '/';
2346   } else {
2347     strcpy(dirname,"./");
2348     strcpy(filename,infile);
2349   }
2350 
2351   // if filename contains "%" replace "%" with "base"
2352 
2353   char *pattern = new char[strlen(filename) + 16];
2354 
2355   if ((ptr = strchr(filename,'%'))) {
2356     *ptr = '\0';
2357     sprintf(pattern,"%s%s%s",filename,"base",ptr+1);
2358     *ptr = '%';
2359   } else strcpy(pattern,filename);
2360 
2361   // scan all files in directory, searching for files that match pattern
2362   // maxnum = largest int that matches "*"
2363 
2364   size_t n = strlen(pattern) + 16;
2365   char *begin = new char[n];
2366   char *middle = new char[n];
2367   char *end = new char[n];
2368 
2369   ptr = strchr(pattern,'*');
2370   *ptr = '\0';
2371   strcpy(begin,pattern);
2372   strcpy(end,ptr+1);
2373   int nbegin = strlen(begin);
2374   bigint maxnum = -1;
2375 
2376   struct dirent *ep;
2377   DIR *dp = opendir(dirname);
2378   if (dp == NULL)
2379     error->one(FLERR,"Cannot open dir to search for restart file");
2380   while ((ep = readdir(dp))) {
2381     if (strstr(ep->d_name,begin) != ep->d_name) continue;
2382     if ((ptr = strstr(&ep->d_name[nbegin],end)) == NULL) continue;
2383     if (strlen(end) == 0) ptr = ep->d_name + strlen(ep->d_name);
2384     *ptr = '\0';
2385     if (strlen(&ep->d_name[nbegin]) < n) {
2386       strcpy(middle,&ep->d_name[nbegin]);
2387       if (ATOBIGINT(middle) > maxnum) maxnum = ATOBIGINT(middle);
2388     }
2389   }
2390   closedir(dp);
2391   if (maxnum < 0) error->one(FLERR,"Found no restart file matching pattern");
2392 
2393   // create outfile with maxint substituted for "*"
2394   // use original infile, not pattern, since need to retain "%" in filename
2395 
2396   ptr = strchr(infile,'*');
2397   *ptr = '\0';
2398   sprintf(outfile,"%s" BIGINT_FORMAT "%s",infile,maxnum,ptr+1);
2399   *ptr = '*';
2400 
2401   // clean up
2402 
2403   delete [] dirname;
2404   delete [] filename;
2405   delete [] pattern;
2406   delete [] begin;
2407   delete [] middle;
2408   delete [] end;
2409 }
2410 
2411 /* ----------------------------------------------------------------------
2412    grab next keyword
2413    if first = 1, line holds non-blank line that ended header
2414    else read lines until one is non-blank
2415    keyword is all text on line w/out leading & trailing white space
2416    read one additional line (assumed blank)
2417    if any read hits EOF, set keyword to empty string
2418 ------------------------------------------------------------------------- */
2419 
parse_keyword(int first)2420 void ReadSurf::parse_keyword(int first)
2421 {
2422   int eof = 0;
2423 
2424   // filereader reads upto non-blank line plus 1 following line
2425   // eof is set to 1 if any read hits end-of-file
2426 
2427   if (filereader) {
2428     if (!first) {
2429       if (fgets(line,MAXLINE,fp) == NULL) eof = 1;
2430     }
2431     while (eof == 0 && strspn(line," \t\n\r") == strlen(line)) {
2432       if (fgets(line,MAXLINE,fp) == NULL) eof = 1;
2433     }
2434     if (fgets(buffer,MAXLINE,fp) == NULL) eof = 1;
2435   }
2436 
2437   // if eof, set keyword empty and return
2438 
2439   MPI_Bcast(&eof,1,MPI_INT,0,filecomm);
2440   if (eof) {
2441     keyword[0] = '\0';
2442     return;
2443   }
2444 
2445   // bcast keyword line to all procs
2446 
2447   int n;
2448   if (filereader) n = strlen(line) + 1;
2449   MPI_Bcast(&n,1,MPI_INT,0,filecomm);
2450   MPI_Bcast(line,n,MPI_CHAR,0,filecomm);
2451 
2452   // copy non-whitespace portion of line into keyword
2453 
2454   int start = strspn(line," \t\n\r");
2455   int stop = strlen(line) - 1;
2456   while (line[stop] == ' ' || line[stop] == '\t'
2457 	 || line[stop] == '\n' || line[stop] == '\r') stop--;
2458   line[stop+1] = '\0';
2459   strcpy(keyword,&line[start]);
2460 }
2461 
2462 /* ----------------------------------------------------------------------
2463    unneeded code for now
2464 ------------------------------------------------------------------------- */
2465 
2466 /* ----------------------------------------------------------------------
2467    check if any pair of points is closer than epsilon
2468    done in O(N) manner by binning twice with offset bins
2469   //NOTE: check if bins allow for surf points
2470   //NOTE: or maybe should ignore surf points in this test, since clip
2471   //      could put them very close together
2472 ------------------------------------------------------------------------- */
2473 
2474 /*
2475 
2476 void ReadSurf::check_point_pairs()
2477 {
2478   int i,j,k,m,n,ix,iy,iz;
2479   double dx,dy,dz,rsq;
2480   double origin[3];
2481 
2482   double *boxlo = domain->boxlo;
2483   double *boxhi = domain->boxhi;
2484 
2485   // epsilon = EPSILON fraction of shortest box length
2486   // epssq = epsilon squared
2487 
2488   double epsilon = MIN(domain->xprd,domain->yprd);
2489   if (dim == 3) epsilon = MIN(epsilon,domain->zprd);
2490   epsilon *= EPSILON;
2491   double epssq = epsilon * epsilon;
2492 
2493   // goal: N roughly cubic bins where N = # of new points
2494   // nbinxyz = # of bins in each dim
2495   // xyzbin = bin size in each dim
2496   // for 2d, nbinz = 1
2497   // after setting bin size, add 1 to nbinxyz
2498   // this allows for 2nd binning via offset origin
2499 
2500   int nbinx,nbiny,nbinz;
2501   double xbin,ybin,zbin;
2502   double xbininv,ybininv,zbininv;
2503 
2504   if (dim == 2) {
2505     double vol_per_point = domain->xprd * domain->yprd / npoint_new;
2506     xbin = ybin = sqrt(vol_per_point);
2507     nbinx = static_cast<int> (domain->xprd / xbin);
2508     nbiny = static_cast<int> (domain->yprd / ybin);
2509     if (nbinx == 0) nbinx = 1;
2510     if (nbiny == 0) nbiny = 1;
2511     nbinz = 1;
2512   } else {
2513     double vol_per_point = domain->xprd * domain->yprd * domain->zprd /
2514       npoint_new;
2515     xbin = ybin = zbin = pow(vol_per_point,1.0/3.0);
2516     nbinx = static_cast<int> (domain->xprd / xbin);
2517     nbiny = static_cast<int> (domain->yprd / ybin);
2518     nbinz = static_cast<int> (domain->zprd / zbin);
2519     if (nbinx == 0) nbinx = 1;
2520     if (nbiny == 0) nbiny = 1;
2521     if (nbinz == 0) nbinz = 1;
2522   }
2523 
2524   xbin = domain->xprd / nbinx;
2525   ybin = domain->yprd / nbiny;
2526   zbin = domain->zprd / nbinz;
2527   xbininv = 1.0/xbin;
2528   ybininv = 1.0/ybin;
2529   zbininv = 1.0/zbin;
2530 
2531   if (nbinx > 1) nbinx++;
2532   if (nbiny > 1) nbiny++;
2533   if (nbinz > 1) nbinz++;
2534 
2535   // binhead[I][J][K] = point index of 1st point in bin IJK, -1 if none
2536   // bin[I] = index of next point in same bin as point I, -1 if last
2537 
2538   int ***binhead;
2539   memory->create(binhead,nbinx,nbiny,nbinz,"readsurf:binhead");
2540   int *bin;
2541   memory->create(bin,npoint_new,"readsurf:bin");
2542 
2543   // 1st binning = bins aligned with global box boundaries
2544 
2545   for (i = 0; i < nbinx; i++)
2546     for (j = 0; j < nbiny; j++)
2547       for (k = 0; k < nbinz; k++)
2548 	binhead[i][j][k] = -1;
2549 
2550   origin[0] = boxlo[0];
2551   origin[1] = boxlo[1];
2552   origin[2] = boxlo[2];
2553 
2554   m = npoint_old;
2555   for (i = 0; i < npoint_new; i++) {
2556     ix = static_cast<int> ((pts[m].x[0] - origin[0]) * xbininv);
2557     iy = static_cast<int> ((pts[m].x[1] - origin[1]) * ybininv);
2558     iz = static_cast<int> ((pts[m].x[2] - origin[2]) * zbininv);
2559     bin[m] = binhead[ix][iy][iz];
2560     binhead[ix][iy][iz] = m;
2561     m++;
2562   }
2563 
2564   // check distances for all pairs of particles in same bin
2565 
2566   int nbad = 0;
2567 
2568   for (i = 0; i < nbinx; i++)
2569     for (j = 0; j < nbiny; j++)
2570       for (k = 0; k < nbinz; k++) {
2571 	m = binhead[i][j][k];
2572 	while (m >= 0) {
2573 	  n = bin[m];
2574 	  while (n >= 0) {
2575 	    dx = pts[m].x[0] - pts[n].x[0];
2576 	    dy = pts[m].x[1] - pts[n].x[1];
2577 	    dz = pts[m].x[2] - pts[n].x[2];
2578 	    rsq = dx*dx + dy*dy + dz*dz;
2579 	    if (rsq < epssq) nbad++;
2580 	    n = bin[n];
2581 	  }
2582 	  m = bin[m];
2583 	}
2584       }
2585 
2586   if (nbad) {
2587     char str[128];
2588     sprintf(str,"%d read_surf point pairs are too close",nbad);
2589     error->all(FLERR,str);
2590   }
2591 
2592   // 2nd binning = bins offset by 1/2 binsize wrt global box boundaries
2593   // do not offset bin origin in a dim with only 1 bin
2594 
2595   for (i = 0; i < nbinx; i++)
2596     for (j = 0; j < nbiny; j++)
2597       for (k = 0; k < nbinz; k++)
2598 	binhead[i][j][k] = -1;
2599 
2600   origin[0] = boxlo[0] - 0.5*xbin;
2601   origin[1] = boxlo[1] - 0.5*ybin;
2602   origin[2] = boxlo[2] - 0.5*zbin;
2603   if (nbinx == 1) origin[0] = boxlo[0];
2604   if (nbiny == 1) origin[1] = boxlo[1];
2605   if (nbinz == 1) origin[2] = boxlo[2];
2606 
2607   m = npoint_old;
2608   for (i = 0; i < npoint_new; i++) {
2609     ix = static_cast<int> ((pts[m].x[0] - origin[0]) * xbininv);
2610     iy = static_cast<int> ((pts[m].x[1] - origin[1]) * ybininv);
2611     iz = static_cast<int> ((pts[m].x[2] - origin[2]) * zbininv);
2612     bin[m] = binhead[ix][iy][iz];
2613     binhead[ix][iy][iz] = m;
2614     m++;
2615   }
2616 
2617   // check distances for all pairs of particles in same bin
2618 
2619   nbad = 0;
2620 
2621   for (i = 0; i < nbinx; i++)
2622     for (j = 0; j < nbiny; j++)
2623       for (k = 0; k < nbinz; k++) {
2624 	m = binhead[i][j][k];
2625 	while (m >= 0) {
2626 	  n = bin[m];
2627 	  while (n >= 0) {
2628 	    dx = pts[m].x[0] - pts[n].x[0];
2629 	    dy = pts[m].x[1] - pts[n].x[1];
2630 	    dz = pts[m].x[2] - pts[n].x[2];
2631 	    rsq = dx*dx + dy*dy + dz*dz;
2632 	    if (rsq < epssq) nbad++;
2633 	    n = bin[n];
2634 	  }
2635 	  m = bin[m];
2636 	}
2637       }
2638 
2639   if (nbad) {
2640     char str[128];
2641     sprintf(str,"%d read_surf point pairs are too close",nbad);
2642     error->all(FLERR,str);
2643   }
2644 
2645   // clean up
2646 
2647   memory->destroy(binhead);
2648   memory->destroy(bin);
2649 }
2650 
2651 */
2652