diff --git a/hdr/create.hpp b/hdr/create.hpp index 3c7214c9..9e8827fa 100644 --- a/hdr/create.hpp +++ b/hdr/create.hpp @@ -179,6 +179,8 @@ int create_crystal_structure(std::vector &); int voronoi_film(std::vector &); +int voronoi_radical_film(std::vector &); + void generate_multilayers(std::vector & catom_array); } diff --git a/hdr/voronoi_radical.hpp b/hdr/voronoi_radical.hpp new file mode 100644 index 00000000..8fe42c7d --- /dev/null +++ b/hdr/voronoi_radical.hpp @@ -0,0 +1,11 @@ +#ifndef CREATE_VORONOI_RADICAL_H_ +#define CREATE_VORONOI_RADICAL_H_ +//========================================================== +// Namespace create_radical_voronoi +//========================================================== +namespace create_radical_voronoi{ + extern bool rounded; + extern double voronoi_sd;/// Standard Deviation of voronoi grains +} + +#endif //CREATE_VORONOI_RADICAL_H_ diff --git a/makefile b/makefile index 718a0026..4bf3412f 100644 --- a/makefile +++ b/makefile @@ -29,8 +29,8 @@ MPIICC=mpiicpc -DMPICF LIBS= -lstdc++ #-lm $(FFTLIBS) -L/opt/local/lib/ -CCC_CFLAGS=-I./hdr -I./src/qvoronoi -O0 -CCC_LDFLAGS=-I./hdr -I./src/qvoronoi -O0 +CCC_CFLAGS=-I./hdr -I./src/qvoronoi -I./src/voro++ -O0 +CCC_LDFLAGS=-I./hdr -I./src/qvoronoi -I./src/voro++ -O0 export LANG=C export LC_ALL=C @@ -40,41 +40,41 @@ export LC_ALL=C CUDALIBS=-L/usr/local/cuda/lib64/ -lcuda -lcudart # Debug Flags -ICC_DBCFLAGS= -O0 -C -I./hdr -I./src/qvoronoi -ICC_DBLFLAGS= -C -I./hdr -I./src/qvoronoi +ICC_DBCFLAGS= -O0 -C -I./hdr -I./src/qvoronoi -I./src/voro++ +ICC_DBLFLAGS= -C -I./hdr -I./src/qvoronoi -I./src/voro++ -GCC_DBCFLAGS= -g -pg -fprofile-arcs -ftest-coverage -Wall -Wextra -O0 -fbounds-check -pedantic -std=c++0x -Wno-long-long -I./hdr -I./src/qvoronoi -Wsign-compare -GCC_DBLFLAGS= -g -pg -fprofile-arcs -ftest-coverage -lstdc++ -std=c++0x -fbounds-check -I./hdr -I./src/qvoronoi -Wsign-compare +GCC_DBCFLAGS= -g -pg -fprofile-arcs -ftest-coverage -Wall -Wextra -O0 -fbounds-check -pedantic -std=c++0x -Wno-long-long -I./hdr -I./src/qvoronoi -I./src/voro++ -Wsign-compare +GCC_DBLFLAGS= -g -pg -fprofile-arcs -ftest-coverage -lstdc++ -std=c++0x -fbounds-check -I./hdr -I./src/qvoronoi -I./src/voro++ -Wsign-compare -PCC_DBCFLAGS= -O0 -I./hdr -I./src/qvoronoi -PCC_DBLFLAGS= -O0 -I./hdr -I./src/qvoronoi -IBM_DBCFLAGS= -O0 -Wall -pedantic -Wextra -I./hdr -I./src/qvoronoi -IBM_DBLFLAGS= -O0 -Wall -pedantic -Wextra -I./hdr -I./src/qvoronoi +PCC_DBCFLAGS= -O0 -I./hdr -I./src/qvoronoi -I./src/voro++ +PCC_DBLFLAGS= -O0 -I./hdr -I./src/qvoronoi -I./src/voro++ +IBM_DBCFLAGS= -O0 -Wall -pedantic -Wextra -I./hdr -I./src/qvoronoi -I./src/voro++ +IBM_DBLFLAGS= -O0 -Wall -pedantic -Wextra -I./hdr -I./src/qvoronoi -I./src/voro++ -LLVM_DBCFLAGS= -Wall -Wextra -O0 -pedantic -std=c++11 -Wno-long-long -I./hdr -I./src/qvoronoi -Wsign-compare -LLVM_DBLFLAGS= -Wall -Wextra -O0 -lstdc++ -I./hdr -I./src/qvoronoi -Wsign-compare +LLVM_DBCFLAGS= -Wall -Wextra -O0 -pedantic -std=c++11 -Wno-long-long -I./hdr -I./src/qvoronoi -I./src/voro++ -Wsign-compare +LLVM_DBLFLAGS= -Wall -Wextra -O0 -lstdc++ -I./hdr -I./src/qvoronoi -I./src/voro++ -Wsign-compare # Performance Flags -ICC_CFLAGS= -O3 -axCORE-AVX2 -fno-alias -align -falign-functions -I./hdr -I./src/qvoronoi -ICC_LDFLAGS= -I./hdr -I./src/qvoronoi -axCORE-AVX2 +ICC_CFLAGS= -O3 -axCORE-AVX2 -fno-alias -align -falign-functions -I./hdr -I./src/qvoronoi -I./src/voro++ +ICC_LDFLAGS= -I./hdr -I./src/qvoronoi -I./src/voro++ -axCORE-AVX2 #ICC_CFLAGS= -O3 -xT -ipo -static -fno-alias -align -falign-functions -vec-report -I./hdr #ICC_LDFLAGS= -lstdc++ -ipo -I./hdr -xT -vec-report -LLVM_CFLAGS= -Wall -pedantic -O3 -mtune=native -funroll-loops -I./hdr -I./src/qvoronoi -LLVM_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi +LLVM_CFLAGS= -Wall -pedantic -O3 -mtune=native -funroll-loops -I./hdr -I./src/qvoronoi -I./src/voro++ +LLVM_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi -I./src/voro++ -GCC_CFLAGS=-O3 -mtune=native -funroll-all-loops -fexpensive-optimizations -funroll-loops -I./hdr -I./src/qvoronoi -std=c++11 -Wsign-compare -GCC_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi -Wsign-compare +GCC_CFLAGS=-O3 -mtune=native -funroll-all-loops -fexpensive-optimizations -funroll-loops -I./hdr -I./src/qvoronoi -I./src/voro++ -std=c++11 -Wsign-compare +GCC_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi -I./src/voro++ -Wsign-compare -PCC_CFLAGS=-O2 -march=barcelona -ipa -I./hdr -I./src/qvoronoi -PCC_LDFLAGS= -I./hdr -I./src/qvoronoi -O2 -march=barcelona -ipa +PCC_CFLAGS=-O2 -march=barcelona -ipa -I./hdr -I./src/qvoronoi -I./src/voro++ +PCC_LDFLAGS= -I./hdr -I./src/qvoronoi -I./src/voro++ -O2 -march=barcelona -ipa -IBM_CFLAGS=-O5 -qarch=450 -qtune=450 -I./hdr -I./src/qvoronoi -IBM_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi -O5 -qarch=450 -qtune=450 +IBM_CFLAGS=-O5 -qarch=450 -qtune=450 -I./hdr -I./src/qvoronoi -I./src/voro++ +IBM_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi -I./src/voro++ -O5 -qarch=450 -qtune=450 -CRAY_CFLAGS= -O3 -hfp3 -I./hdr -I./src/qvoronoi -CRAY_LDFLAGS= -I./hdr -I./src/qvoronoi +CRAY_CFLAGS= -O3 -hfp3 -I./hdr -I./src/qvoronoi -I./src/voro++ +CRAY_LDFLAGS= -I./hdr -I./src/qvoronoi -I./src/voro++ # Save git commit in simple function @@ -153,6 +153,7 @@ include src/statistics/makefile include src/unitcell/makefile include src/vio/makefile include src/environment/makefile +include src/voro++/makefile # Cuda must be last for some odd reason include src/cuda/makefile diff --git a/src/create/data.cpp b/src/create/data.cpp index f3b1ceb0..f062094e 100644 --- a/src/create/data.cpp +++ b/src/create/data.cpp @@ -44,6 +44,8 @@ namespace create{ double voronoi_grain_size = 50.0; double voronoi_grain_spacing = 10.0; + double voronoi_tiny_grain_chance = 0.0; + double bubble_radius = 0.3333; double bubble_nucleation_height = 0.0; diff --git a/src/create/interface.cpp b/src/create/interface.cpp index 6ce07ad0..e5eb4a09 100644 --- a/src/create/interface.cpp +++ b/src/create/interface.cpp @@ -14,6 +14,7 @@ #include "create.hpp" #include "vio.hpp" #include "voronoi.hpp" +#include "voronoi_radical.hpp" #include "random.hpp" // Internal sim header #include "internal.hpp" @@ -141,6 +142,11 @@ namespace create{ cs::system_creation_flags[2]=3; return true; } + test="laguerre-voronoi-film"; + if(word==test){ + cs::system_creation_flags[2]=5; + return true; + } test="voronoi-grain-substructure"; if(word==test){ create::internal::generate_voronoi_substructure = true; @@ -156,6 +162,7 @@ namespace create{ double vsd=atof(value.c_str()); vin::check_for_valid_value(vsd, word, line, prefix, unit, "none", 0.0, 1.0,"input","0.0 - 1.0"); create_voronoi::voronoi_sd=vsd; + create_radical_voronoi::voronoi_sd=vsd; return true; } //-------------------------------------------------------------------- @@ -175,6 +182,7 @@ namespace create{ test="voronoi-rounded-grains"; if(word==test || word == "rounded-grains"){ create_voronoi::rounded=true; + create_radical_voronoi::rounded=true; return true; } test="voronoi-include-boundary-grains"; @@ -444,6 +452,14 @@ namespace create{ return true; } //-------------------------------------------------------------------- + test="voronoi-tiny-grain-chance"; + if(word==test){ + double tgchance=atof(value.c_str()); + vin::check_for_valid_value(tgchance, word, line, prefix, unit, "none", 0.0, 1.0,"input","0.0 - 1.0"); + create::internal::voronoi_tiny_grain_chance=tgchance; + return true; + } + //-------------------------------------------------------------------- test="cone"; if(word==test){ cs::system_creation_flags[1]=8; diff --git a/src/create/internal.hpp b/src/create/internal.hpp index 5b32beb8..62abf66e 100644 --- a/src/create/internal.hpp +++ b/src/create/internal.hpp @@ -132,6 +132,8 @@ namespace create{ extern double voronoi_grain_size; extern double voronoi_grain_spacing; + extern double voronoi_tiny_grain_chance; + extern double bubble_radius; extern double bubble_nucleation_height; diff --git a/src/create/makefile b/src/create/makefile index 96354d04..fee107eb 100644 --- a/src/create/makefile +++ b/src/create/makefile @@ -40,6 +40,7 @@ voronoi.o \ voronoi_grain_rounding.o \ voronoi_substructure.o \ voronoi_vertex_points.o \ +voronoi_radical.o \ write_grain_vertices.o # Append module objects to global tree diff --git a/src/create/system_type.cpp b/src/create/system_type.cpp index 3d69f151..6d5f9a19 100644 --- a/src/create/system_type.cpp +++ b/src/create/system_type.cpp @@ -115,6 +115,10 @@ int create_system_type(std::vector & catom_array){ err::vexit(); break; + case 5: // Radical Voronoi Granular Film + voronoi_radical_film(catom_array); + break; + default:{ std::cerr << "Unknown system type requested, exiting" << std::endl; err::vexit(); diff --git a/src/create/voronoi_radical.cpp b/src/create/voronoi_radical.cpp new file mode 100644 index 00000000..2b2ee384 --- /dev/null +++ b/src/create/voronoi_radical.cpp @@ -0,0 +1,757 @@ +#include "create.hpp" +#include "errors.hpp" +#include "grains.hpp" +#include "material.hpp" +#include "qvoronoi.hpp" +#include "random.hpp" +#include "vio.hpp" +#include "vmpi.hpp" +#include "vmath.hpp" +#include "voro++.hh" + +#include +#include +#include +#include +#include +#include +#include + +#include "internal.hpp" + +namespace create_radical_voronoi{ + bool rounded=false; + double voronoi_sd=0.15; /// Standard Deviation of voronoi grains +} + +namespace cs{ + +bool Drop_and_Roll(double HozMin,double HozMax,double VerMin,double VerMax, + std::mt19937_64 VoroGen, + std::vector*Hoz,std::vector*Ver,std::vector*r){ + + // TODO: Implement AABB tree to improve performance + + double dTh = ((2.0*M_PI)/1000.0); + double Boundary_check = (HozMax-HozMin)*0.5; + double dVert= ((VerMax-VerMin)/1000.0); + std::uniform_real_distribution UNI(0.0,HozMax); + double HozPos=UNI(VoroGen); + double VerPos=VerMax+r->back(); + // Force particle within a box and set starting height + if(HozPos-r->back()back();} + else if(HozPos+r->back()>HozMax){HozPos=HozMax-r->back();} + bool placed=false; + bool falling=true; + bool rolling=false; + unsigned int contact=0; + + while(!placed){ + double HozCont=0.0; + double VerCont=0.0; + double rCont=0.0; + while(falling){ + // Drop + VerPos -= dVert; + // Check -- NB: for improved efficiency split box into grid for coarse check before fine + for(unsigned int neigh=0; neigh<(Ver->size()-1);++neigh){ + double HozNeigh=Hoz->at(neigh); + double VerNeigh=Ver->at(neigh); + double RNeigh=r->at(neigh); + double Hdist = (HozPos-HozNeigh); + double radSumSq = (r->back()+RNeigh)*(r->back()+RNeigh); + + //########### Periodic Boundary Checks ###########// + if(Hdist>Boundary_check){HozNeigh+=HozMax;}// Move neigh to the right + else if(Hdist<-Boundary_check){HozNeigh-=HozMax;}// Move neigh to the left + //################################################// + + double distSq = (HozPos-HozNeigh)*(HozPos-HozNeigh) + + (VerPos-VerNeigh)*(VerPos-VerNeigh); + + if(distSq==radSumSq || distSq<(radSumSq-r->back()*0.1)){ // Touching or intersecting + HozCont=HozNeigh;VerCont=VerNeigh;rCont=RNeigh; + falling=false; + rolling=true; + contact=neigh; + goto Rolling_label; + } + } + // Hit bottom + if((VerPos-r->back())<=VerMin){ // Good improvement here would be maybe round up the comparison so it counts as touching when ~1e-12 + placed=true; + falling=false; + } + } +Rolling_label: + while(rolling){ + // Particle exactly on top + if(HozPos==HozCont){ + placed=true; + falling=true; + break; + } + // Particle grazing past + if(VerPos<=VerCont){ + rolling=false; + falling=true; + break; + } + // Roll the particle + // Determine incident angle + double angle = atan2((VerPos-VerCont),(HozPos-HozCont)); + if(angle>1.570796){ //Roll left + while(angle<=M_PI && !placed){ + HozPos = HozCont + (r->back()+rCont)*cos(angle); + VerPos = VerCont + (r->back()+rCont)*sin(angle); + // Check contact + for(unsigned int neigh=0; neigh<(Ver->size()-1);++neigh){ + if(neigh==contact){continue;} + double HozNeigh=Hoz->at(neigh); + double VerNeigh=Ver->at(neigh); + double RNeigh=r->at(neigh); + + double Hdist = (HozPos-HozNeigh); + double radSumSq = (r->back()+RNeigh)*(r->back()+RNeigh); + + //########### Periodic Boundary Checks ###########// + if(Hdist>Boundary_check){HozNeigh+=HozMax;}// Move neigh to the right + else if(Hdist<-Boundary_check){HozNeigh-=HozMax;}// Move neigh to the left + //################################################// + + double distSq = (HozPos-HozNeigh)*(HozPos-HozNeigh) + + (VerPos-VerNeigh)*(VerPos-VerNeigh); + + if(distSqback())<=VerMin){ + placed=true; + rolling=false; + break; + } + angle += dTh; + } + } + if(!placed){ + rolling=false; + falling=true; + } + } + else{ // Roll right + while(angle>=0.0 && !placed){ + HozPos = HozCont + (r->back()+rCont)*cos(angle); + VerPos = VerCont + (r->back()+rCont)*sin(angle); + for(unsigned int neigh=0; neigh<(Ver->size()-1);++neigh){ + if(neigh==contact){continue;} + double HozNeigh=Hoz->at(neigh); + double VerNeigh=Ver->at(neigh); + double RNeigh=r->at(neigh); + + double Hdist = (HozPos-HozNeigh); + double radSumSq = (r->back()+RNeigh)*(r->back()+RNeigh); + + //########### Periodic Boundary Checks ###########// + if(Hdist>Boundary_check){HozNeigh+=HozMax;}// Move neigh to the right + else if(Hdist<-Boundary_check){HozNeigh-=HozMax;}// Move neigh to the left + //################################################// + + double distSq = (HozPos-HozNeigh)*(HozPos-HozNeigh) + + (VerPos-VerNeigh)*(VerPos-VerNeigh); + + if(distSqback())<=VerMin){ + placed=true; + rolling=false; + break; + } + angle -= dTh; + } + } + if(!placed){ + rolling=false; + falling=true; + } + } + } + // Check that we aren't full + if(VerPos+r->back()>VerMax){ + placed=false; + break; + } + } + + Hoz->back()=HozPos, Ver->back()=VerPos; + return placed; +} + +int voronoi_radical_film(std::vector & catom_array){ + + // check calling of routine if error checking is activated + if(err::check==true){ + terminaltextcolor(RED); + std::cerr << "cs::voronoi_radical_film has been called" << std::endl; + terminaltextcolor(WHITE); + } + // We start by packing our system with circles + // TODO: Implement AABB Tree to improve performance + + double grain_sd=create_radical_voronoi::voronoi_sd; + std::vector X, Y, r; + std::mt19937_64 VoroGen; + VoroGen.seed(mtrandom::voronoi_seed); + double TinyGrainChance = create::internal::voronoi_tiny_grain_chance; + //double Max_width_limit = 100; // Used to prevent rare occurance of very large grains + double Grain_width = create::internal::voronoi_grain_size; + double mulg = log(create::internal::voronoi_grain_size); + double Grain_spacing = create::internal::voronoi_grain_spacing; + double Packing_fraction = 1.0 - Grain_spacing/Grain_width; + double Dim_x=cs::system_dimensions[0]; + double Dim_y=cs::system_dimensions[1]; + double Max_x=Dim_x; + double Max_y=Dim_y; + + if(grain_sd > 0){ + bool filled=false; + int attempt=0; + int attempt_limit=100; + std::uniform_real_distribution Tiny(0,1.0); + std::uniform_real_distribution TinyGrainSize(0.0,Grain_width); + std::lognormal_distribution LN(mulg,grain_sd); + + unsigned int Disc=0; + while(!filled){ + if(attempt==attempt_limit){filled=true;} + + X.push_back(0.0); + Y.push_back(0.0); + double radius=0.0; + if(Tiny(VoroGen) < TinyGrainChance){ + radius = TinyGrainSize(VoroGen); + } + else{ + do{radius=LN(VoroGen)*0.5;} + while(/*radius > Max_width_limit*(Grain_width*0.5) ||*/ radius < 0.0); + } + + r.push_back(radius); + bool Placed=false; + std::vector Hoz(X.size(),0.0); + std::vector Ver(X.size(),0.0); + + Hoz=X; + Ver=Y; + Placed = Drop_and_Roll(0.0,Max_x,0.0,Max_y+Grain_width*10,VoroGen,&Hoz,&Ver,&r); + + if(Placed){ + attempt=0; + X.back()=Hoz.back(); + Y.back()=Ver.back(); + }else{ + --Disc; + ++attempt; + X.pop_back(); + Y.pop_back(); + r.pop_back(); + } + ++Disc; + } + // Cut into system + std::vector Xtmp; + std::vector Ytmp; + std::vector rtmp; + + for(size_t i=0; iGrain_width*5.0 && Y[i]<(Max_y+Grain_width*5.0)){ + Xtmp.push_back(X[i]); + Ytmp.emplace_back(Y[i]-Grain_width*5.0); + rtmp.push_back(r[i]); + } + } + X=Xtmp; + Y=Ytmp; + r=rtmp; + + }else{ + double Local_Grain_width = Grain_width; + double Local_Grain_height = (2.0/sqrt(3.0))/Local_Grain_width; + int Bx=int(Dim_x/(Local_Grain_width)); + int By=int(Dim_y/(1.75*Local_Grain_height)); + Max_x = Bx*Local_Grain_width; + Max_y = 1.5*By*Local_Grain_height; + for(int j=0;j FailedGrains; // Store the grains which cannot be computed + double x, y, z; + std::vector tmp_vertices; + std::vector tmp_vx_hold, tmp_vy_hold; + std::vector Grain_ID; + std::vector tmp_x_hold, tmp_y_hold; + std::vector> tmp_Vertex_X, tmp_Vertex_Y; + std::vector tmp_Grain_neighbours; + std::vector> tmp_Neighbour; + + if(Container_loop.start()){ + do{ + if(Container.compute_cell(Cell_with_neighbour_info,Container_loop)){ + Container_loop.pos(x,y,z); + Grain_ID.emplace_back(Container_loop.pid()-FailedGrains.size()); + + tmp_x_hold.push_back(x);tmp_y_hold.push_back(y); + Cell_with_neighbour_info.vertices(x,y,z,tmp_vertices); + // Voro++ works in 3D so here we want to set the z-coord to 1 (flatten the system to 2D) + for(size_t i=0; i Neighbours; + Cell_with_neighbour_info.neighbors(tmp_Grain_neighbours); + for(int Gneigh : tmp_Grain_neighbours){ + if(Gneigh>=0){ + Neighbours.push_back(Gneigh); + } + } + tmp_Neighbour.push_back(Neighbours); + } + else{ + FailedGrains.emplace_back(Container_loop.pid()); + } + } + while(Container_loop.inc()); + } + +// ############################################ + size_t NumGrains = tmp_x_hold.size(); + tmp_Neighbour.resize(NumGrains); + std::vector> Grain_Pos(NumGrains, std::vector(2)); + std::vector>> Grain_Vertices(NumGrains); + std::vector> Grain_neigh_final(NumGrains); + +//########################REORDER LISTS SO INDEX MATCHES GRAIN ID######################// + for(size_t g=0; g(2)); + for(size_t v=0; v angle_hold; + for(size_t g=0; g SystemCentre(2); + for(size_t grain=0; grainVxMAX){VxMAX=Vx;} + else if(VxVyMAX){VyMAX=Vy;} + else if(Vy Grain_Area(NumGrains); + std::vector> Grain_Geo_Pos(NumGrains, std::vector(2)); + for(size_t grain=0; grain GrainToDelete; + for(int g=NumGrains-1; g>=0; --g){ + // If grain pos beyond system then remove and skip next check + if(Grain_Pos[g][0]>Dim_x || Grain_Pos[g][1]>Dim_y){ + GrainToDelete.push_back(g); + continue; + } + // Check if any vertices are outside the system dimensions + for(size_t v=0; vDim_x || Grain_Vertices[g][v][1]>Dim_y){ + GrainToDelete.push_back(g); + break; + } + } + } + // Ensure we have no duplicates + GrainToDelete.erase(std::unique(GrainToDelete.begin(), GrainToDelete.end()), GrainToDelete.end()); + + for(size_t id : GrainToDelete){ + Grain_Pos.erase(Grain_Pos.begin()+id); + Grain_Vertices.erase(Grain_Vertices.begin()+id); + Grain_neigh_final.erase(Grain_neigh_final.begin()+id); + } + NumGrains = Grain_Pos.size(); + + if(NumGrains==0){ + std::cerr << "No grains generated! Exiting..." << std::endl; + exit(1); + } + + // Organise data in the way that VAMPIRE expects + // Reserve space for pointers + std::vector> grain_coord_array(NumGrains, std::vector(2)); + std::vector>> grain_vertices_array(NumGrains); + + grain_coord_array = Grain_Pos; + grain_vertices_array = Grain_Vertices; + // Convert vertices to be relative to grain centre - required for rounding and supercell code + for(size_t grain=0; grain>> supercell_array; + + int min_bounds[3]; + int max_bounds[3]; + + min_bounds[0]=0; + min_bounds[1]=0; + min_bounds[2]=0; + max_bounds[0]=cs::total_num_unit_cells[0]; + max_bounds[1]=cs::total_num_unit_cells[1]; + max_bounds[2]=cs::total_num_unit_cells[2]; + + // allocate supercell array + int dx = max_bounds[0]-min_bounds[0]; + int dy = max_bounds[1]-min_bounds[1]; + + supercell_array.resize(dx); + for(int i=0;i(catom_array[atom].x/unit_cell.dimensions[0]); + int cy = static_cast(catom_array[atom].y/unit_cell.dimensions[1]); + supercell_array.at(cx).at(cy).push_back(atom); + } + + // Determine order for core-shell grains + std::list material_order(0); + for(int mat=0;mat maxx) maxx = x; + if(y < miny) miny = y; + if(y > maxy) maxy = y; + } + + // determine coordinate offset for grains + const double x0 = grain_coord_array[grain][0]; + const double y0 = grain_coord_array[grain][1]; + + // loop over cells + for(int i=minx;i<=maxx;i++){ + for(int j=miny;j<=maxy;j++){ + // loop over atoms in cells; + for(unsigned int id=0;id0.0){ + // Iterate over materials + for(std::list::iterator it = material_order.begin(); it != material_order.end(); it++){ + int mat = (it)->mat; + double factor = mp::material[mat].core_shell_size; + double maxz=create::internal::mp[mat].max*cs::system_dimensions[2]; + double minz=create::internal::mp[mat].min*cs::system_dimensions[2]; + double cz=catom_array[atom].z; + const int atom_uc_cat = catom_array[atom].uc_category; + const int mat_uc_cat = create::internal::mp[mat].unit_cell_category; + if(vmath::point_in_polygon_factor(x-x0,y-y0,factor, tmp_grain_pointx_array,tmp_grain_pointy_array,num_vertices)==true){ + if((cz>=minz) && (cz()); + grain_coord_array[grain_coord_array.size()-1].push_back(0.0); // x + grain_coord_array[grain_coord_array.size()-1].push_back(0.0); // y + + // check for continuous layer + for(unsigned int atom=0; atom < catom_array.size(); atom++){ + if(mp::material[catom_array[atom].material].continuous==true && catom_array[atom].include == false ){ + catom_array[atom].include=true; + catom_array[atom].grain=int(grain_coord_array.size()-1); + } + } + + // set number of grains + grains::num_grains = int(grain_coord_array.size()); + + // sort atoms by grain number + create::internal::sort_atoms_by_grain(catom_array); + + /* + // Used to print out grain vertices for checking + std::ofstream VERT_GNU_FILE("gnuplot_vert_file_3.dat"); + for(unsigned int i=0;i=nx) {bi=nx-1;if(ai>=nx) ai=nx-1;} + } + if(!yperiodic) { + if(aj<0) {aj=0;if(bj<0) bj=0;} + if(bj>=ny) {bj=ny-1;if(aj>=ny) aj=ny-1;} + } + if(!zperiodic) { + if(ak<0) {ak=0;if(bk<0) bk=0;} + if(bk>=nz) {bk=nz-1;if(ak>=nz) ak=nz-1;} + } + ci=ai;cj=aj;ck=ak; + di=i=step_mod(ci,nx);apx=px=step_div(ci,nx)*sx; + dj=j=step_mod(cj,ny);apy=py=step_div(cj,ny)*sy; + dk=k=step_mod(ck,nz);apz=pz=step_div(ck,nz)*sz; + inc1=di-step_mod(bi,nx); + inc2=nx*(ny+dj-step_mod(bj,ny))+inc1; + inc1+=nx; + ijk=di+nx*(dj+ny*dk); + q=0; +} + +/** Starts the loop by finding the first particle within the container to + * consider. + * \return True if there is any particle to consider, false otherwise. */ +bool c_loop_subset::start() { + while(co[ijk]==0) {if(!next_block()) return false;} + while(mode!=no_check&&out_of_bounds()) { + q++; + while(q>=co[ijk]) {q=0;if(!next_block()) return false;} + } + return true; +} + +/** Initializes the class to loop over all particles in a rectangular box. + * \param[in] (xmin,xmax) the minimum and maximum x coordinates of the box. + * \param[in] (ymin,ymax) the minimum and maximum y coordinates of the box. + * \param[in] (zmin,zmax) the minimum and maximum z coordinates of the box. + * \param[in] bounds_test whether to do detailed bounds checking. If this is + * false then the class will loop over all particles in + * blocks that overlap the given box. If it is true, the + * particle will only loop over the particles which + * actually lie within the box. + * \return True if there is any valid point to loop over, false otherwise. */ +void c_loop_subset::setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test) { + if(bounds_test) {mode=box;v0=xmin;v1=xmax;v2=ymin;v3=ymax;v4=zmin;v5=zmax;} else mode=no_check; + ai=step_int((xmin-ax)*xsp); + bi=step_int((xmax-ax)*xsp); + aj=step_int((ymin-ay)*ysp); + bj=step_int((ymax-ay)*ysp); + ak=step_int((zmin-az)*zsp); + bk=step_int((zmax-az)*zsp); + setup_common(); +} + +/** Computes whether the current point is out of bounds, relative to the + * current loop setup. + * \return True if the point is out of bounds, false otherwise. */ +bool c_loop_subset::out_of_bounds() { + double *pp=p[ijk]+ps*q; + if(mode==sphere) { + double fx(*pp+px-v0),fy(pp[1]+py-v1),fz(pp[2]+pz-v2); + return fx*fx+fy*fy+fz*fz>v3; + } else { + double f(*pp+px);if(fv1) return true; + f=pp[1]+py;if(fv3) return true; + f=pp[2]+pz;return fv5; + } +} + +/** Returns the next block to be tested in a loop, and updates the periodicity + * vector if necessary. */ +bool c_loop_subset::next_block() { + if(i + c_loop_base(c_class &con) : nx(con.nx), ny(con.ny), nz(con.nz), + nxy(con.nxy), nxyz(con.nxyz), ps(con.ps), + p(con.p), id(con.id), co(con.co) {} + /** Returns the position vector of the particle currently being + * considered by the loop. + * \param[out] (x,y,z) the position vector of the particle. */ + inline void pos(double &x,double &y,double &z) { + double *pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + } + /** Returns the ID, position vector, and radius of the particle + * currently being considered by the loop. + * \param[out] pid the particle ID. + * \param[out] (x,y,z) the position vector of the particle. + * \param[out] r the radius of the particle. If no radius + * information is available the default radius + * value is returned. */ + inline void pos(int &pid,double &x,double &y,double &z,double &r) { + pid=id[ijk][q]; + double *pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + r=ps==3?default_radius:*(++pp); + } + /** Returns the x position of the particle currently being + * considered by the loop. */ + inline double x() {return p[ijk][ps*q];} + /** Returns the y position of the particle currently being + * considered by the loop. */ + inline double y() {return p[ijk][ps*q+1];} + /** Returns the z position of the particle currently being + * considered by the loop. */ + inline double z() {return p[ijk][ps*q+2];} + /** Returns the ID of the particle currently being considered + * by the loop. */ + inline int pid() {return id[ijk][q];} +}; + +/** \brief Class for looping over all of the particles in a container. + * + * This is one of the simplest loop classes, that scans the computational + * blocks in order, and scans all the particles within each block in order. */ +class c_loop_all : public c_loop_base { + public: + /** The constructor copies several necessary constants from the + * base container class. + * \param[in] con the container class to use. */ + template + c_loop_all(c_class &con) : c_loop_base(con) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + i=j=k=ijk=q=0; + while(co[ijk]==0) if(!next_block()) return false; + return true; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + q++; + if(q>=co[ijk]) { + q=0; + do { + if(!next_block()) return false; + } while(co[ijk]==0); + } + return true; + } + private: + /** Updates the internal variables to find the next + * computational block with any particles. + * \return True if another block is found, false if there are + * no more blocks. */ + inline bool next_block() { + ijk++; + i++; + if(i==nx) { + i=0;j++; + if(j==ny) { + j=0;k++; + if(ijk==nxyz) return false; + } + } + return true; + } +}; + +/** \brief Class for looping over a subset of particles in a container. + * + * This class can loop over a subset of particles in a certain geometrical + * region within the container. The class can be set up to loop over a + * rectangular box or sphere. It can also rectangular group of internal + * computational blocks. */ +class c_loop_subset : public c_loop_base { + public: + /** The current mode of operation, determining whether tests + * should be applied to particles to ensure they are within a + * certain geometrical object. */ + c_loop_subset_mode mode; + /** The constructor copies several necessary constants from the + * base container class. + * \param[in] con the container class to use. */ + template + c_loop_subset(c_class &con) : c_loop_base(con), ax(con.ax), ay(con.ay), az(con.az), + sx(con.bx-ax), sy(con.by-ay), sz(con.bz-az), xsp(con.xsp), ysp(con.ysp), zsp(con.zsp), + xperiodic(con.xperiodic), yperiodic(con.yperiodic), zperiodic(con.zperiodic) {} + void setup_sphere(double vx,double vy,double vz,double r,bool bounds_test=true); + void setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test=true); + void setup_intbox(int ai_,int bi_,int aj_,int bj_,int ak_,int bk_); + bool start(); + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + do { + q++; + while(q>=co[ijk]) {q=0;if(!next_block()) return false;} + } while(mode!=no_check&&out_of_bounds()); + return true; + } + private: + const double ax,ay,az,sx,sy,sz,xsp,ysp,zsp; + const bool xperiodic,yperiodic,zperiodic; + double px,py,pz,apx,apy,apz; + double v0,v1,v2,v3,v4,v5; + int ai,bi,aj,bj,ak,bk; + int ci,cj,ck,di,dj,dk,inc1,inc2; + inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;} + inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;} + inline int step_int(double a) {return a<0?int(a)-1:int(a);} + void setup_common(); + bool next_block(); + bool out_of_bounds(); +}; + +/** \brief Class for looping over all of the particles specified in a + * pre-assembled particle_order class. + * + * The particle_order class can be used to create a specific order of particles + * within the container. This class can then loop over these particles in this + * order. The class is particularly useful in cases where the ordering of the + * output must match the ordering of particles as they were inserted into the + * container. */ +class c_loop_order : public c_loop_base { + public: + /** A reference to the ordering class to use. */ + particle_order &vo; + /** A pointer to the current position in the ordering class. */ + int *cp; + /** A pointer to the end position in the ordering class. */ + int *op; + /** The constructor copies several necessary constants from the + * base class, and sets up a reference to the ordering class to + * use. + * \param[in] con the container class to use. + * \param[in] vo_ the ordering class to use. */ + template + c_loop_order(c_class &con,particle_order &vo_) + : c_loop_base(con), vo(vo_), nx(con.nx), nxy(con.nxy) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + cp=vo.o;op=vo.op; + if(cp!=op) { + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } else return false; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + if(cp==op) return false; + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } + private: + /** The number of computational blocks in the x direction. */ + const int nx; + /** The number of computational blocks in a z-slice. */ + const int nxy; + /** Takes the current block index and computes indices in the + * x, y, and z directions. */ + inline void decode() { + k=ijk/nxy; + int ijkt=ijk-nxy*k; + j=ijkt/nx; + i=ijkt-j*nx; + } +}; + +/** \brief A class for looping over all particles in a container_periodic or + * container_periodic_poly class. + * + * Since the container_periodic and container_periodic_poly classes have a + * fundamentally different memory organization, the regular loop classes cannot + * be used with them. */ +class c_loop_all_periodic : public c_loop_base { + public: + /** The constructor copies several necessary constants from the + * base periodic container class. + * \param[in] con the periodic container class to use. */ + template + c_loop_all_periodic(c_class &con) : c_loop_base(con), ey(con.ey), ez(con.ez), wy(con.wy), wz(con.wz), + ijk0(nx*(ey+con.oy*ez)), inc2(2*nx*con.ey+1) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + i=0; + j=ey; + k=ez; + ijk=ijk0; + q=0; + while(co[ijk]==0) if(!next_block()) return false; + return true; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + q++; + if(q>=co[ijk]) { + q=0; + do { + if(!next_block()) return false; + } while(co[ijk]==0); + } + return true; + } + private: + /** The lower y index (inclusive) of the primary domain within + * the block structure. */ + int ey; + /** The lower y index (inclusive) of the primary domain within + * the block structure. */ + int ez; + /** The upper y index (exclusive) of the primary domain within + * the block structure. */ + int wy; + /** The upper z index (exclusive) of the primary domain within + * the block structure. */ + int wz; + /** The index of the (0,0,0) block within the block structure. + */ + int ijk0; + /** A value to increase ijk by when the z index is increased. + */ + int inc2; + /** Updates the internal variables to find the next + * computational block with any particles. + * \return True if another block is found, false if there are + * no more blocks. */ + inline bool next_block() { + i++; + if(i==nx) { + i=0;j++; + if(j==wy) { + j=ey;k++; + if(k==wz) return false; + ijk+=inc2; + } else ijk++; + } else ijk++; + return true; + } +}; + +/** \brief Class for looping over all of the particles specified in a + * pre-assembled particle_order class, for use with container_periodic classes. + * + * The particle_order class can be used to create a specific order of particles + * within the container. This class can then loop over these particles in this + * order. The class is particularly useful in cases where the ordering of the + * output must match the ordering of particles as they were inserted into the + * container. */ +class c_loop_order_periodic : public c_loop_base { + public: + /** A reference to the ordering class to use. */ + particle_order &vo; + /** A pointer to the current position in the ordering class. */ + int *cp; + /** A pointer to the end position in the ordering class. */ + int *op; + /** The constructor copies several necessary constants from the + * base class, and sets up a reference to the ordering class to + * use. + * \param[in] con the container class to use. + * \param[in] vo_ the ordering class to use. */ + template + c_loop_order_periodic(c_class &con,particle_order &vo_) + : c_loop_base(con), vo(vo_), nx(con.nx), oxy(con.nx*con.oy) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + cp=vo.o;op=vo.op; + if(cp!=op) { + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } else return false; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + if(cp==op) return false; + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } + private: + /** The number of computational blocks in the x direction. */ + const int nx; + /** The number of computational blocks in a z-slice. */ + const int oxy; + /** Takes the current block index and computes indices in the + * x, y, and z directions. */ + inline void decode() { + k=ijk/oxy; + int ijkt=ijk-oxy*k; + j=ijkt/nx; + i=ijkt-j*nx; + } +}; + +} + +#endif diff --git a/src/voro++/cell.cpp b/src/voro++/cell.cpp new file mode 100644 index 00000000..12816354 --- /dev/null +++ b/src/voro++/cell.cpp @@ -0,0 +1,2252 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file cell.cpp + * \brief Function implementations for the voronoicell and related classes. */ + +#include +#include + +#include "config.hh" +#include "common.hh" +#include "cell.hh" + +namespace voro { + +/** Constructs a Voronoi cell and sets up the initial memory. */ +voronoicell_base::voronoicell_base() : + current_vertices(init_vertices), current_vertex_order(init_vertex_order), + current_delete_size(init_delete_size), current_delete2_size(init_delete2_size), + ed(new int*[current_vertices]), nu(new int[current_vertices]), + pts(new double[3*current_vertices]), mem(new int[current_vertex_order]), + mec(new int[current_vertex_order]), mep(new int*[current_vertex_order]), + ds(new int[current_delete_size]), stacke(ds+current_delete_size), + ds2(new int[current_delete2_size]), stacke2(ds2+current_delete_size), + current_marginal(init_marginal), marg(new int[current_marginal]) { + int i; + for(i=0;i<3;i++) { + mem[i]=init_n_vertices;mec[i]=0; + mep[i]=new int[init_n_vertices*((i<<1)+1)]; + } + mem[3]=init_3_vertices;mec[3]=0; + mep[3]=new int[init_3_vertices*7]; + for(i=4;i=0;i--) if(mem[i]>0) delete [] mep[i]; + delete [] marg; + delete [] ds2;delete [] ds; + delete [] mep;delete [] mec; + delete [] mem;delete [] pts; + delete [] nu;delete [] ed; +} + +/** Ensures that enough memory is allocated prior to carrying out a copy. + * \param[in] vc a reference to the specialized version of the calling class. + * \param[in] vb a pointered to the class to be copied. */ +template +void voronoicell_base::check_memory_for_copy(vc_class &vc,voronoicell_base* vb) { + while(current_vertex_ordercurrent_vertex_order) add_memory_vorder(vc); + for(int i=0;imec[i]) add_memory(vc,i,ds2); + while(current_verticesp) add_memory_vertices(vc); +} + +/** Copies the vertex and edge information from another class. The routine + * assumes that enough memory is available for the copy. + * \param[in] vb a pointer to the class to copy. */ +void voronoicell_base::copy(voronoicell_base* vb) { + int i,j; + p=vb->p;up=0; + for(i=0;imec[i]; + for(j=0;jmep[i][j]; + for(j=0;jnu[i]; + for(i=0;i<3*p;i++) pts[i]=vb->pts[i]; +} + +/** Copies the information from another voronoicell class into this + * class, extending memory allocation if necessary. + * \param[in] c the class to copy. */ +void voronoicell_neighbor::operator=(voronoicell &c) { + voronoicell_base *vb=((voronoicell_base*) &c); + check_memory_for_copy(*this,vb);copy(vb); + int i,j; + for(i=0;i +void voronoicell_base::add_memory(vc_class &vc,int i,int *stackp2) { + int s=(i<<1)+1; + if(mem[i]==0) { + vc.n_allocate(i,init_n_vertices); + mep[i]=new int[init_n_vertices*s]; + mem[i]=init_n_vertices; +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Order %d vertex memory created\n",i); +#endif + } else { + int j=0,k,*l; + mem[i]<<=1; + if(mem[i]>max_n_vertices) voro_fatal_error("Point memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Order %d vertex memory scaled up to %d\n",i,mem[i]); +#endif + l=new int[s*mem[i]]; + int m=0; + vc.n_allocate_aux1(i); + while(j=0) { + ed[k]=l+j; + vc.n_set_to_aux1_offset(k,m); + } else { + int *dsp; + for(dsp=ds2;dsp=3 + fputs("Relocated dangling pointer",stderr); +#endif + } + for(k=0;k +void voronoicell_base::add_memory_vertices(vc_class &vc) { + int i=(current_vertices<<1),j,**pp,*pnu; + if(i>max_vertices) voro_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Vertex memory scaled up to %d\n",i); +#endif + double *ppts; + pp=new int*[i]; + for(j=0;j +void voronoicell_base::add_memory_vorder(vc_class &vc) { + int i=(current_vertex_order<<1),j,*p1,**p2; + if(i>max_vertex_order) voro_fatal_error("Vertex order memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Vertex order memory scaled up to %d\n",i); +#endif + p1=new int[i]; + for(j=0;jmax_delete_size) voro_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size); +#endif + int *dsn=new int[current_delete_size],*dsnp=dsn,*dsp=ds; + while(dspmax_delete2_size) voro_fatal_error("Delete stack 2 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Delete stack 2 memory scaled up to %d\n",current_delete2_size); +#endif + int *dsn=new int[current_delete2_size],*dsnp=dsn,*dsp=ds2; + while(dsp +inline bool voronoicell_base::search_for_outside_edge(vc_class &vc,int &up) { + int i,lp,lw,*j(ds2),*stackp2(ds2); + double l; + *(stackp2++)=up; + while(j +inline void voronoicell_base::add_to_stack(vc_class &vc,int lp,int *&stackp2) { + for(int *k(ds2);k +bool voronoicell_base::nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id) { + int count=0,i,j,k,lp=up,cp,qp,rp,*stackp(ds),*stackp2(ds2),*dsp; + int us=0,ls=0,qs,iqs,cs,uw,qw,lw; + int *edp,*edd; + double u,l,r,q;bool complicated_setup=false,new_double_edge=false,double_edge=false; + + // Initialize the safe testing routine + n_marg=0;px=x;py=y;pz=z;prsq=rsq; + + // Test approximately sqrt(n)/4 points for their proximity to the plane + // and keep the one which is closest + uw=m_test(up,u); + + // Starting from an initial guess, we now move from vertex to vertex, + // to try and find an edge which intersects the cutting plane, + // or a vertex which is on the plane + try { + if(uw==1) { + + // The test point is inside the cutting plane. + us=0; + do { + lp=ed[up][us]; + lw=m_test(lp,l); + if(l=p) throw true; + u=l;up=lp; + for(us=0;us=p) throw true; + u=q;up=qp; + for(us=0;us=1 + fputs("Bailed out of convex calculation\n",stderr); +#endif + qw=1;lw=0; + for(qp=0;qp=current_vertex_order) add_memory_vorder(vc); + if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2); + vc.n_set_pointer(p,nu[p]); + ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; + ed[p][nu[p]<<1]=p; + + // Copy the edges of the original vertex into the new + // one. Delete the edges of the original vertex, and + // update the relational table. + us=cycle_down(i,up); + while(i=current_vertex_order) add_memory_vorder(vc); + if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2); + + // Copy the edges of the original vertex into the new + // one. Delete the edges of the original vertex, and + // update the relational table. + vc.n_set_pointer(p,nu[p]); + ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; + ed[p][nu[p]<<1]=p; + us=i++; + while(i0) k+=nu[j]; + } else { + if(j>0) { + + // This vertex was visited before, so + // count those vertices to the ones we + // already have. + k+=nu[j]; + + // The only time when we might make a + // duplicate edge is if the point we're + // going to move to next is also a + // marginal point, so test for that + // first. + if(lw==0) { + + // Now see whether this marginal point + // has been visited before. + i=-ed[lp][nu[lp]<<1]; + if(i>0) { + + // Now see if the last edge of that other + // marginal point actually ends up here. + if(ed[i][nu[i]-1]==j) { + new_double_edge=true; + k-=1; + } else new_double_edge=false; + } else { + + // That marginal point hasn't been visited + // before, so we probably don't have to worry + // about duplicate edges, except in the + // case when that's the way into the end + // of the facet, because that way always creates + // an edge. + if(j==rp&&lp==up&&ed[qp][nu[qp]+qs]==us) { + new_double_edge=true; + k-=1; + } else new_double_edge=false; + } + } else new_double_edge=false; + } else { + + // The vertex hasn't been visited + // before, but let's see if it's + // marginal + if(lw==0) { + + // If it is, we need to check + // for the case that it's a + // small branch, and that we're + // heading right back to where + // we came from + i=-ed[lp][nu[lp]<<1]; + if(i==cp) { + new_double_edge=true; + k-=1; + } else new_double_edge=false; + } else new_double_edge=false; + } + } + + // k now holds the number of edges of the new vertex + // we are forming. Add memory for it if it doesn't exist + // already. + while(k>=current_vertex_order) add_memory_vorder(vc); + if(mec[k]==mem[k]) add_memory(vc,k,stackp2); + + // Now create a new vertex with order k, or augment + // the existing one + if(j>0) { + + // If we're augmenting a vertex but we don't + // actually need any more edges, just skip this + // routine to avoid memory confusion + if(nu[j]!=k) { + // Allocate memory and copy the edges + // of the previous instance into it + vc.n_set_aux1(k); + edp=mep[k]+((k<<1)+1)*mec[k]++; + i=0; + while(ids) { + --p; + while(ed[p][nu[p]]==-1) { + j=nu[p]; + edp=ed[p];edd=(mep[j]+((j<<1)+1)*--mec[j]); + while(edp0) voro_fatal_error("Zero order vertex formed",VOROPP_INTERNAL_ERROR); + + // Collapse any order 2 vertices and exit + return collapse_order2(vc); +} + +/** During the creation of a new facet in the plane routine, it is possible + * that some order two vertices may arise. This routine removes them. + * Suppose an order two vertex joins c and d. If there's a edge between + * c and d already, then the order two vertex is just removed; otherwise, + * the order two vertex is removed and c and d are joined together directly. + * It is possible this process will create order two or order one vertices, + * and the routine is continually run until all of them are removed. + * \return False if the vertex removal was unsuccessful, indicative of the cell + * reducing to zero volume and disappearing; true if the vertex removal + * was successful. */ +template +inline bool voronoicell_base::collapse_order2(vc_class &vc) { + if(!collapse_order1(vc)) return false; + int a,b,i,j,k,l; + while(mec[2]>0) { + + // Pick a order 2 vertex and read in its edges + i=--mec[2]; + j=mep[2][5*i];k=mep[2][5*i+1]; + if(j==k) { +#if VOROPP_VERBOSE >=1 + fputs("Order two vertex joins itself",stderr); +#endif + return false; + } + + // Scan the edges of j to see if joins k + for(l=0;l +inline bool voronoicell_base::collapse_order1(vc_class &vc) { + int i,j,k; + while(mec[1]>0) { + up=0; +#if VOROPP_VERBOSE >=1 + fputs("Order one collapse\n",stderr); +#endif + i=--mec[1]; + j=mep[1][3*i];k=mep[1][3*i+1]; + i=mep[1][3*i+2]; + if(!delete_connection(vc,j,k,false)) return false; + --p; + if(up==i) up=0; + if(p!=i) { + if(up==p) up=i; + pts[3*i]=pts[3*p]; + pts[3*i+1]=pts[3*p+1]; + pts[3*i+2]=pts[3*p+2]; + for(k=0;k +inline bool voronoicell_base::delete_connection(vc_class &vc,int j,int k,bool hand) { + int q=hand?k:cycle_up(k,j); + int i=nu[j]-1,l,*edp,*edd,m; +#if VOROPP_VERBOSE >=1 + if(i<1) { + fputs("Zero order vertex formed\n",stderr); + return false; + } +#endif + if(mec[i]==mem[i]) add_memory(vc,i,ds2); + vc.n_set_aux1(i); + for(l=0;l=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + vx=pts[3*k]-*pts; + vy=pts[3*k+1]-pts[1]; + vz=pts[3*k+2]-pts[2]; + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + wx=pts[3*m]-*pts; + wy=pts[3*m+1]-pts[1]; + wz=pts[3*m+2]-pts[2]; + vol+=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; + k=m;l=n;vx=wx;vy=wy;vz=wz; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + } + reset_edges(); + return vol*fe; +} + +/** Calculates the areas of each face of the Voronoi cell and prints the + * results to an output stream. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_areas(std::vector &v) { + double area; + v.clear(); + int i,j,k,l,m,n; + double ux,uy,uz,vx,vy,vz,wx,wy,wz; + for(i=1;i=0) { + area=0; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + ux=pts[3*k]-pts[3*i]; + uy=pts[3*k+1]-pts[3*i+1]; + uz=pts[3*k+2]-pts[3*i+2]; + vx=pts[3*m]-pts[3*i]; + vy=pts[3*m+1]-pts[3*i+1]; + vz=pts[3*m+2]-pts[3*i+2]; + wx=uy*vz-uz*vy; + wy=uz*vx-ux*vz; + wz=ux*vy-uy*vx; + area+=sqrt(wx*wx+wy*wy+wz*wz); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + v.push_back(0.125*area); + } + } + reset_edges(); +} + + +/** Calculates the total surface area of the Voronoi cell. + * \return The computed area. */ +double voronoicell_base::surface_area() { + double area=0; + int i,j,k,l,m,n; + double ux,uy,uz,vx,vy,vz,wx,wy,wz; + for(i=1;i=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + ux=pts[3*k]-pts[3*i]; + uy=pts[3*k+1]-pts[3*i+1]; + uz=pts[3*k+2]-pts[3*i+2]; + vx=pts[3*m]-pts[3*i]; + vy=pts[3*m+1]-pts[3*i+1]; + vz=pts[3*m+2]-pts[3*i+2]; + wx=uy*vz-uz*vy; + wy=uz*vx-ux*vz; + wz=ux*vy-uy*vx; + area+=sqrt(wx*wx+wy*wy+wz*wz); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + reset_edges(); + return 0.125*area; +} + + +/** Calculates the centroid of the Voronoi cell, by decomposing the cell into + * tetrahedra extending outward from the zeroth vertex. + * \param[out] (cx,cy,cz) references to floating point numbers in which to + * pass back the centroid vector. */ +void voronoicell_base::centroid(double &cx,double &cy,double &cz) { + double tvol,vol=0;cx=cy=cz=0; + int i,j,k,l,m,n; + double ux,uy,uz,vx,vy,vz,wx,wy,wz; + for(i=1;i=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + vx=pts[3*k]-*pts; + vy=pts[3*k+1]-pts[1]; + vz=pts[3*k+2]-pts[2]; + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + wx=pts[3*m]-*pts; + wy=pts[3*m+1]-pts[1]; + wz=pts[3*m+2]-pts[2]; + tvol=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; + vol+=tvol; + cx+=(wx+vx-ux)*tvol; + cy+=(wy+vy-uy)*tvol; + cz+=(wz+vz-uz)*tvol; + k=m;l=n;vx=wx;vy=wy;vz=wz; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + } + reset_edges(); + if(vol>tolerance_sq) { + vol=0.125/vol; + cx=cx*vol+0.5*(*pts); + cy=cy*vol+0.5*pts[1]; + cz=cz*vol+0.5*pts[2]; + } else cx=cy=cz=0; +} + +/** Computes the maximum radius squared of a vertex from the center of the + * cell. It can be used to determine when enough particles have been testing an + * all planes that could cut the cell have been considered. + * \return The maximum radius squared of a vertex.*/ +double voronoicell_base::max_radius_squared() { + double r,s,*ptsp=pts+3,*ptse=pts+3*p; + r=*pts*(*pts)+pts[1]*pts[1]+pts[2]*pts[2]; + while(ptspr) r=s; + } + return r; +} + +/** Calculates the total edge distance of the Voronoi cell. + * \return A floating point number holding the calculated distance. */ +double voronoicell_base::total_edge_distance() { + int i,j,k; + double dis=0,dx,dy,dz; + for(i=0;ii) { + dx=pts[3*k]-pts[3*i]; + dy=pts[3*k+1]-pts[3*i+1]; + dz=pts[3*k+2]-pts[3*i+2]; + dis+=sqrt(dx*dx+dy*dy+dz*dz); + } + } + return 0.5*dis; +} + +/** Outputs the edges of the Voronoi cell in POV-Ray format to an open file + * stream, displacing the cell by given vector. + * \param[in] (x,y,z) a displacement vector to be added to the cell's position. + * \param[in] fp a file handle to write to. */ +void voronoicell_base::draw_pov(double x,double y,double z,FILE* fp) { + int i,j,k;double *ptsp=pts,*pt2; + char posbuf1[128],posbuf2[128]; + for(i=0;i,r}\n",posbuf1); + for(j=0;j,<%s>,r}\n",posbuf1,posbuf2); + } + } + } +} + +/** Outputs the edges of the Voronoi cell in gnuplot format to an output stream. + * \param[in] (x,y,z) a displacement vector to be added to the cell's position. + * \param[in] fp a file handle to write to. */ +void voronoicell_base::draw_gnuplot(double x,double y,double z,FILE *fp) { + int i,j,k,l,m; + for(i=1;i=0) { + fprintf(fp,"%g %g %g\n",x+0.5*pts[3*i],y+0.5*pts[3*i+1],z+0.5*pts[3*i+2]); + l=i;m=j; + do { + ed[k][ed[l][nu[l]+m]]=-1-l; + ed[l][m]=-1-k; + l=k; + fprintf(fp,"%g %g %g\n",x+0.5*pts[3*k],y+0.5*pts[3*k+1],z+0.5*pts[3*k+2]); + } while (search_edge(l,m,k)); + fputs("\n\n",fp); + } + } + reset_edges(); +} + +inline bool voronoicell_base::search_edge(int l,int &m,int &k) { + for(m=0;m=0) return true; + } + return false; +} + +/** Outputs the Voronoi cell in the POV mesh2 format, described in section + * 1.3.2.2 of the POV-Ray documentation. The mesh2 output consists of a list of + * vertex vectors, followed by a list of triangular faces. The routine also + * makes use of the optional inside_vector specification, which makes the mesh + * object solid, so the the POV-Ray Constructive Solid Geometry (CSG) can be + * applied. + * \param[in] (x,y,z) a displacement vector to be added to the cell's position. + * \param[in] fp a file handle to write to. */ +void voronoicell_base::draw_pov_mesh(double x,double y,double z,FILE *fp) { + int i,j,k,l,m,n; + double *ptsp=pts; + fprintf(fp,"mesh2 {\nvertex_vectors {\n%d\n",p); + for(i=0;i\n",x+*ptsp*0.5,y+ptsp[1]*0.5,z+ptsp[2]*0.5); + fprintf(fp,"}\nface_indices {\n%d\n",(p-2)<<1); + for(i=1;i=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + fprintf(fp,",<%d,%d,%d>\n",i,k,m); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + fputs("}\ninside_vector <0,0,1>\n}\n",fp); + reset_edges(); +} + +/** Several routines in the class that gather cell-based statistics internally + * track their progress by flipping edges to negative so that they know what + * parts of the cell have already been tested. This function resets them back + * to positive. When it is called, it assumes that every edge in the routine + * should have already been flipped to negative, and it bails out with an + * internal error if it encounters a positive edge. */ +inline void voronoicell_base::reset_edges() { + int i,j; + for(i=0;i=0) voro_fatal_error("Edge reset routine found a previously untested edge",VOROPP_INTERNAL_ERROR); + ed[i][j]=-1-ed[i][j]; + } +} + +/** Checks to see if a given vertex is inside, outside or within the test + * plane. If the point is far away from the test plane, the routine immediately + * returns whether it is inside or outside. If the routine is close the the + * plane and within the specified tolerance, then the special check_marginal() + * routine is called. + * \param[in] n the vertex to test. + * \param[out] ans the result of the scalar product used in evaluating the + * location of the point. + * \return -1 if the point is inside the plane, 1 if the point is outside the + * plane, or 0 if the point is within the plane. */ +inline int voronoicell_base::m_test(int n,double &ans) { + double *pp=pts+n+(n<<1); + ans=*(pp++)*px; + ans+=*(pp++)*py; + ans+=*pp*pz-prsq; + if(ans<-tolerance2) { + return -1; + } else if(ans>tolerance2) { + return 1; + } + return check_marginal(n,ans); +} + +/** Checks to see if a given vertex is inside, outside or within the test + * plane, for the case when the point has been detected to be very close to the + * plane. The routine ensures that the returned results are always consistent + * with previous tests, by keeping a table of any marginal results. The routine + * first sees if the vertex is in the table, and if it finds a previously + * computed result it uses that. Otherwise, it computes a result for this + * vertex and adds it the table. + * \param[in] n the vertex to test. + * \param[in] ans the result of the scalar product used in evaluating + * the location of the point. + * \return -1 if the point is inside the plane, 1 if the point is outside the + * plane, or 0 if the point is within the plane. */ +int voronoicell_base::check_marginal(int n,double &ans) { + int i; + for(i=0;imax_marginal) + voro_fatal_error("Marginal case buffer allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Marginal cases buffer scaled up to %d\n",i); +#endif + int *pmarg=new int[current_marginal]; + for(int j=0;jtolerance?1:(ans<-tolerance?-1:0); + return marg[n_marg-1]; +} + +/** For each face of the Voronoi cell, this routine prints the out the normal + * vector of the face, and scales it to the distance from the cell center to + * that plane. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::normals(std::vector &v) { + int i,j,k; + v.clear(); + for(i=1;i=0) normals_search(v,i,j,k); + } + reset_edges(); +} + +/** This inline routine is called by normals(). It attempts to construct a + * single normal vector that is associated with a particular face. It first + * traces around the face, trying to find two vectors along the face edges + * whose vector product is above the numerical tolerance. It then constructs + * the normal vector using this product. If the face is too small, and none of + * the vector products are large enough, the routine may return (0,0,0) as the + * normal vector. + * \param[in] v the vector to store the results in. + * \param[in] i the initial vertex of the face to test. + * \param[in] j the index of an edge of the vertex. + * \param[in] k the neighboring vertex of i, set to ed[i][j]. */ +inline void voronoicell_base::normals_search(std::vector &v,int i,int j,int k) { + ed[i][j]=-1-k; + int l=cycle_up(ed[i][nu[i]+j],k),m; + double ux,uy,uz,vx,vy,vz,wx,wy,wz,wmag; + do { + m=ed[k][l];ed[k][l]=-1-m; + ux=pts[3*m]-pts[3*k]; + uy=pts[3*m+1]-pts[3*k+1]; + uz=pts[3*m+2]-pts[3*k+2]; + + // Test to see if the length of this edge is above the tolerance + if(ux*ux+uy*uy+uz*uz>tolerance_sq) { + while(m!=i) { + l=cycle_up(ed[k][nu[k]+l],m); + k=m;m=ed[k][l];ed[k][l]=-1-m; + vx=pts[3*m]-pts[3*k]; + vy=pts[3*m+1]-pts[3*k+1]; + vz=pts[3*m+2]-pts[3*k+2]; + + // Construct the vector product of this edge with + // the previous one + wx=uz*vy-uy*vz; + wy=ux*vz-uz*vx; + wz=uy*vx-ux*vy; + wmag=wx*wx+wy*wy+wz*wz; + + // Test to see if this vector product of the + // two edges is above the tolerance + if(wmag>tolerance_sq) { + + // Construct the normal vector and print it + wmag=1/sqrt(wmag); + v.push_back(wx*wmag); + v.push_back(wy*wmag); + v.push_back(wz*wmag); + + // Mark all of the remaining edges of this + // face and exit + while(m!=i) { + l=cycle_up(ed[k][nu[k]+l],m); + k=m;m=ed[k][l];ed[k][l]=-1-m; + } + return; + } + } + v.push_back(0); + v.push_back(0); + v.push_back(0); + return; + } + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + v.push_back(0); + v.push_back(0); + v.push_back(0); +} + + +/** Returns the number of faces of a computed Voronoi cell. + * \return The number of faces. */ +int voronoicell_base::number_of_faces() { + int i,j,k,l,m,s=0; + for(i=1;i=0) { + s++; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + + } + } + reset_edges(); + return s; +} + +/** Returns a vector of the vertex orders. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::vertex_orders(std::vector &v) { + v.resize(p); + for(int i=0;i0) { + fprintf(fp,"%d",*nu); + for(int *nup=nu+1;nup &v) { + v.resize(3*p); + double *ptsp=pts; + for(int i=0;i<3*p;i+=3) { + v[i]=*(ptsp++)*0.5; + v[i+1]=*(ptsp++)*0.5; + v[i+2]=*(ptsp++)*0.5; + } +} + +/** Outputs the vertex vectors using the local coordinate system. + * \param[out] fp the file handle to write to. */ +void voronoicell_base::output_vertices(FILE *fp) { + if(p>0) { + fprintf(fp,"(%g,%g,%g)",*pts*0.5,pts[1]*0.5,pts[2]*0.5); + for(double *ptsp=pts+3;ptsp &v) { + v.resize(3*p); + double *ptsp=pts; + for(int i=0;i<3*p;i+=3) { + v[i]=x+*(ptsp++)*0.5; + v[i+1]=y+*(ptsp++)*0.5; + v[i+2]=z+*(ptsp++)*0.5; + } +} + +/** Outputs the vertex vectors using the global coordinate system. + * \param[out] fp the file handle to write to. + * \param[in] (x,y,z) the position vector of the particle in the global + * coordinate system. */ +void voronoicell_base::output_vertices(double x,double y,double z,FILE *fp) { + if(p>0) { + fprintf(fp,"(%g,%g,%g)",x+*pts*0.5,y+pts[1]*0.5,z+pts[2]*0.5); + for(double *ptsp=pts+3;ptsp &v) { + v.clear(); + int i,j,k,l,m; + double dx,dy,dz,perim; + for(i=1;i=0) { + dx=pts[3*k]-pts[3*i]; + dy=pts[3*k+1]-pts[3*i+1]; + dz=pts[3*k+2]-pts[3*i+2]; + perim=sqrt(dx*dx+dy*dy+dz*dz); + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + dx=pts[3*m]-pts[3*k]; + dy=pts[3*m+1]-pts[3*k+1]; + dz=pts[3*m+2]-pts[3*k+2]; + perim+=sqrt(dx*dx+dy*dy+dz*dz); + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + v.push_back(0.5*perim); + } + } + reset_edges(); +} + +/** For each face, this routine outputs a bracketed sequence of numbers + * containing a list of all the vertices that make up that face. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_vertices(std::vector &v) { + int i,j,k,l,m,vp(0),vn; + v.clear(); + for(i=1;i=0) { + v.push_back(0); + v.push_back(i); + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + v.push_back(k); + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + vn=v.size(); + v[vp]=vn-vp-1; + vp=vn; + } + } + reset_edges(); +} + +/** Outputs a list of the number of edges in each face. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_orders(std::vector &v) { + int i,j,k,l,m,q; + v.clear(); + for(i=1;i=0) { + q=1; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + q++; + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + v.push_back(q);; + } + } + reset_edges(); +} + +/** Computes the number of edges that each face has and outputs a frequency + * table of the results. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_freq_table(std::vector &v) { + int i,j,k,l,m,q; + v.clear(); + for(i=1;i=0) { + q=1; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + q++; + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + if((unsigned int) q>=v.size()) v.resize(q+1,0); + v[q]++; + } + } + reset_edges(); +} + +/** This routine tests to see whether the cell intersects a plane by starting + * from the guess point up. If up intersects, then it immediately returns true. + * Otherwise, it calls the plane_intersects_track() routine. + * \param[in] (x,y,z) the normal vector to the plane. + * \param[in] rsq the distance along this vector of the plane. + * \return False if the plane does not intersect the plane, true if it does. */ +bool voronoicell_base::plane_intersects(double x,double y,double z,double rsq) { + double g=x*pts[3*up]+y*pts[3*up+1]+z*pts[3*up+2]; + if(g>3,mp=1; + double m; + while(cag) { + if(m>rsq) return true; + g=m;up=mp; + } + ca+=mp++; + } + return plane_intersects_track(x,y,z,rsq,g); + } + return true; +} + +/* This routine tests to see if a cell intersects a plane, by tracing over the cell from + * vertex to vertex, starting at up. It is meant to be called either by plane_intersects() + * or plane_intersects_track(), when those routines cannot immediately resolve the case. + * \param[in] (x,y,z) the normal vector to the plane. + * \param[in] rsq the distance along this vector of the plane. + * \param[in] g the distance of up from the plane. + * \return False if the plane does not intersect the plane, true if it does. */ +inline bool voronoicell_base::plane_intersects_track(double x,double y,double z,double rsq,double g) { + int count=0,ls,us,tp; + double t; + + // The test point is outside of the cutting space + for(us=0;usg) { + ls=ed[up][nu[up]+us]; + up=tp; + while (t=p) { +#if VOROPP_VERBOSE >=1 + fputs("Bailed out of convex calculation",stderr); +#endif + for(tp=0;tprsq) return true; + return false; + } + + // Test all the neighbors of the current point + // and find the one which is closest to the + // plane + for(us=0;ust) break; + } + if(us==ls) { + us++; + while(ust) break; + us++; + } + if(us==nu[up]) return false; + } + ls=ed[up][nu[up]+us];up=tp;t=g; + } + return true; + } + } + return false; +} + +/** Counts the number of edges of the Voronoi cell. + * \return the number of edges. */ +int voronoicell_base::number_of_edges() { + int edges=0,*nup=nu; + while(nup>1; +} + +/** Outputs a custom string of information about the Voronoi cell. The string + * of information follows a similar style as the C printf command, and detailed + * information about its format is available at + * http://math.lbl.gov/voro++/doc/custom.html. + * \param[in] format the custom string to print. + * \param[in] i the ID of the particle associated with this Voronoi cell. + * \param[in] (x,y,z) the position of the particle associated with this Voronoi + * cell. + * \param[in] r a radius associated with the particle. + * \param[in] fp the file handle to write to. */ +void voronoicell_base::output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp) { + char *fmp=(const_cast(format)); + std::vector vi; + std::vector vd; + while(*fmp!=0) { + if(*fmp=='%') { + fmp++; + switch(*fmp) { + + // Particle-related output + case 'i': fprintf(fp,"%d",i);break; + case 'x': fprintf(fp,"%g",x);break; + case 'y': fprintf(fp,"%g",y);break; + case 'z': fprintf(fp,"%g",z);break; + case 'q': fprintf(fp,"%g %g %g",x,y,z);break; + case 'r': fprintf(fp,"%g",r);break; + + // Vertex-related output + case 'w': fprintf(fp,"%d",p);break; + case 'p': output_vertices(fp);break; + case 'P': output_vertices(x,y,z,fp);break; + case 'o': output_vertex_orders(fp);break; + case 'm': fprintf(fp,"%g",0.25*max_radius_squared());break; + + // Edge-related output + case 'g': fprintf(fp,"%d",number_of_edges());break; + case 'E': fprintf(fp,"%g",total_edge_distance());break; + case 'e': face_perimeters(vd);voro_print_vector(vd,fp);break; + + // Face-related output + case 's': fprintf(fp,"%d",number_of_faces());break; + case 'F': fprintf(fp,"%g",surface_area());break; + case 'A': { + face_freq_table(vi); + voro_print_vector(vi,fp); + } break; + case 'a': face_orders(vi);voro_print_vector(vi,fp);break; + case 'f': face_areas(vd);voro_print_vector(vd,fp);break; + case 't': { + face_vertices(vi); + voro_print_face_vertices(vi,fp); + } break; + case 'l': normals(vd); + voro_print_positions(vd,fp); + break; + case 'n': neighbors(vi); + voro_print_vector(vi,fp); + break; + + // Volume-related output + case 'v': fprintf(fp,"%g",volume());break; + case 'c': { + double cx,cy,cz; + centroid(cx,cy,cz); + fprintf(fp,"%g %g %g",cx,cy,cz); + } break; + case 'C': { + double cx,cy,cz; + centroid(cx,cy,cz); + fprintf(fp,"%g %g %g",x+cx,y+cy,z+cz); + } break; + + // End-of-string reached + case 0: fmp--;break; + + // The percent sign is not part of a + // control sequence + default: putc('%',fp);putc(*fmp,fp); + } + } else putc(*fmp,fp); + fmp++; + } + fputs("\n",fp); +} + +/** This initializes the class to be a rectangular box. It calls the base class + * initialization routine to set up the edge and vertex information, and then + * sets up the neighbor information, with initial faces being assigned ID + * numbers from -1 to -6. + * \param[in] (xmin,xmax) the minimum and maximum x coordinates. + * \param[in] (ymin,ymax) the minimum and maximum y coordinates. + * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ +void voronoicell_neighbor::init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { + init_base(xmin,xmax,ymin,ymax,zmin,zmax); + int *q=mne[3]; + *q=-5;q[1]=-3;q[2]=-1; + q[3]=-5;q[4]=-2;q[5]=-3; + q[6]=-5;q[7]=-1;q[8]=-4; + q[9]=-5;q[10]=-4;q[11]=-2; + q[12]=-6;q[13]=-1;q[14]=-3; + q[15]=-6;q[16]=-3;q[17]=-2; + q[18]=-6;q[19]=-4;q[20]=-1; + q[21]=-6;q[22]=-2;q[23]=-4; + *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; + ne[4]=q+12;ne[5]=q+15;ne[6]=q+18;ne[7]=q+21; +} + +/** This initializes the class to be an octahedron. It calls the base class + * initialization routine to set up the edge and vertex information, and then + * sets up the neighbor information, with the initial faces being assigned ID + * numbers from -1 to -8. + * \param[in] l The distance from the octahedron center to a vertex. Six + * vertices are initialized at (-l,0,0), (l,0,0), (0,-l,0), + * (0,l,0), (0,0,-l), and (0,0,l). */ +void voronoicell_neighbor::init_octahedron(double l) { + init_octahedron_base(l); + int *q=mne[4]; + *q=-5;q[1]=-6;q[2]=-7;q[3]=-8; + q[4]=-1;q[5]=-2;q[6]=-3;q[7]=-4; + q[8]=-6;q[9]=-5;q[10]=-2;q[11]=-1; + q[12]=-8;q[13]=-7;q[14]=-4;q[15]=-3; + q[16]=-5;q[17]=-8;q[18]=-3;q[19]=-2; + q[20]=-7;q[21]=-6;q[22]=-1;q[23]=-4; + *ne=q;ne[1]=q+4;ne[2]=q+8;ne[3]=q+12;ne[4]=q+16;ne[5]=q+20; +} + +/** This initializes the class to be a tetrahedron. It calls the base class + * initialization routine to set up the edge and vertex information, and then + * sets up the neighbor information, with the initial faces being assigned ID + * numbers from -1 to -4. + * \param (x0,y0,z0) a position vector for the first vertex. + * \param (x1,y1,z1) a position vector for the second vertex. + * \param (x2,y2,z2) a position vector for the third vertex. + * \param (x3,y3,z3) a position vector for the fourth vertex. */ +void voronoicell_neighbor::init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { + init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3); + int *q=mne[3]; + *q=-4;q[1]=-3;q[2]=-2; + q[3]=-3;q[4]=-4;q[5]=-1; + q[6]=-4;q[7]=-2;q[8]=-1; + q[9]=-2;q[10]=-3;q[11]=-1; + *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; +} + +/** This routine checks to make sure the neighbor information of each face is + * consistent. */ +void voronoicell_neighbor::check_facets() { + int i,j,k,l,m,q; + for(i=1;i=0) { + ed[i][j]=-1-k; + q=ne[i][j]; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + ed[k][l]=-1-m; + if(ne[k][l]!=q) fprintf(stderr,"Facet error at (%d,%d)=%d, started from (%d,%d)=%d\n",k,l,ne[k][l],i,j,q); + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + } + } + reset_edges(); +} + +/** The class constructor allocates memory for storing neighbor information. */ +voronoicell_neighbor::voronoicell_neighbor() { + int i; + mne=new int*[current_vertex_order]; + ne=new int*[current_vertices]; + for(i=0;i<3;i++) mne[i]=new int[init_n_vertices*i]; + mne[3]=new int[init_3_vertices*3]; + for(i=4;i=0;i--) if(mem[i]>0) delete [] mne[i]; + delete [] mne; + delete [] ne; +} + +/** Computes a vector list of neighbors. */ +void voronoicell_neighbor::neighbors(std::vector &v) { + v.clear(); + int i,j,k,l,m; + for(i=1;i=0) { + v.push_back(ne[i][j]); + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + } + } + reset_edges(); +} + +/** Prints the vertices, their edges, the relation table, and also notifies if + * any memory errors are visible. */ +void voronoicell_base::print_edges() { + int j; + double *ptsp=pts; + for(int i=0;i=mep[nu[i]]+mec[nu[i]]*((nu[i]<<1)+1)) puts(" Memory error"); + else puts(""); + } +} + +/** This prints out the neighbor information for vertex i. */ +void voronoicell_neighbor::print_edges_neighbors(int i) { + if(nu[i]>0) { + int j=0; + printf(" ("); + while(j + +#include "config.hh" +#include "common.hh" + +namespace voro { + +/** \brief A class representing a single Voronoi cell. + * + * This class represents a single Voronoi cell, as a collection of vertices + * that are connected by edges. The class contains routines for initializing + * the Voronoi cell to be simple shapes such as a box, tetrahedron, or octahedron. + * It the contains routines for recomputing the cell based on cutting it + * by a plane, which forms the key routine for the Voronoi cell computation. + * It contains numerous routine for computing statistics about the Voronoi cell, + * and it can output the cell in several formats. + * + * This class is not intended for direct use, but forms the base of the + * voronoicell and voronoicell_neighbor classes, which extend it based on + * whether neighboring particle ID information needs to be tracked. */ +class voronoicell_base { + public: + /** This holds the current size of the arrays ed and nu, which + * hold the vertex information. If more vertices are created + * than can fit in this array, then it is dynamically extended + * using the add_memory_vertices routine. */ + int current_vertices; + /** This holds the current maximum allowed order of a vertex, + * which sets the size of the mem, mep, and mec arrays. If a + * vertex is created with more vertices than this, the arrays + * are dynamically extended using the add_memory_vorder routine. + */ + int current_vertex_order; + /** This sets the size of the main delete stack. */ + int current_delete_size; + /** This sets the size of the auxiliary delete stack. */ + int current_delete2_size; + /** This sets the total number of vertices in the current cell. + */ + int p; + /** This is the index of particular point in the cell, which is + * used to start the tracing routines for plane intersection + * and cutting. These routines will work starting from any + * point, but it's often most efficient to start from the last + * point considered, since in many cases, the cell construction + * algorithm may consider many planes with similar vectors + * concurrently. */ + int up; + /** This is a two dimensional array that holds information + * about the edge connections of the vertices that make up the + * cell. The two dimensional array is not allocated in the + * usual method. To account for the fact the different vertices + * have different orders, and thus require different amounts of + * storage, the elements of ed[i] point to one-dimensional + * arrays in the mep[] array of different sizes. + * + * More specifically, if vertex i has order m, then ed[i] + * points to a one-dimensional array in mep[m] that has 2*m+1 + * entries. The first m elements hold the neighboring edges, so + * that the jth edge of vertex i is held in ed[i][j]. The next + * m elements hold a table of relations which is redundant but + * helps speed up the computation. It satisfies the relation + * ed[ed[i][j]][ed[i][m+j]]=i. The final entry holds a back + * pointer, so that ed[i+2*m]=i. The back pointers are used + * when rearranging the memory. */ + int **ed; + /** This array holds the order of the vertices in the Voronoi + * cell. This array is dynamically allocated, with its current + * size held by current_vertices. */ + int *nu; + /** This in an array with size 3*current_vertices for holding + * the positions of the vertices. */ + double *pts; + voronoicell_base(); + ~voronoicell_base(); + void init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax); + void init_octahedron_base(double l); + void init_tetrahedron_base(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3); + void translate(double x,double y,double z); + void draw_pov(double x,double y,double z,FILE *fp=stdout); + /** Outputs the cell in POV-Ray format, using cylinders for edges + * and spheres for vertices, to a given file. + * \param[in] (x,y,z) a displacement to add to the cell's + * position. + * \param[in] filename the name of the file to write to. */ + inline void draw_pov(double x,double y,double z,const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_pov(x,y,z,fp); + fclose(fp); + }; + void draw_pov_mesh(double x,double y,double z,FILE *fp=stdout); + /** Outputs the cell in POV-Ray format as a mesh2 object to a + * given file. + * \param[in] (x,y,z) a displacement to add to the cell's + * position. + * \param[in] filename the name of the file to write to. */ + inline void draw_pov_mesh(double x,double y,double z,const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_pov_mesh(x,y,z,fp); + fclose(fp); + } + void draw_gnuplot(double x,double y,double z,FILE *fp=stdout); + /** Outputs the cell in Gnuplot format a given file. + * \param[in] (x,y,z) a displacement to add to the cell's + * position. + * \param[in] filename the name of the file to write to. */ + inline void draw_gnuplot(double x,double y,double z,const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_gnuplot(x,y,z,fp); + fclose(fp); + } + double volume(); + double max_radius_squared(); + double total_edge_distance(); + double surface_area(); + void centroid(double &cx,double &cy,double &cz); + int number_of_faces(); + int number_of_edges(); + void vertex_orders(std::vector &v); + void output_vertex_orders(FILE *fp=stdout); + void vertices(std::vector &v); + void output_vertices(FILE *fp=stdout); + void vertices(double x,double y,double z,std::vector &v); + void output_vertices(double x,double y,double z,FILE *fp=stdout); + void face_areas(std::vector &v); + /** Outputs the areas of the faces. + * \param[in] fp the file handle to write to. */ + inline void output_face_areas(FILE *fp=stdout) { + std::vector v;face_areas(v); + voro_print_vector(v,fp); + } + void face_orders(std::vector &v); + /** Outputs a list of the number of sides of each face. + * \param[in] fp the file handle to write to. */ + inline void output_face_orders(FILE *fp=stdout) { + std::vector v;face_orders(v); + voro_print_vector(v,fp); + } + void face_freq_table(std::vector &v); + /** Outputs a */ + inline void output_face_freq_table(FILE *fp=stdout) { + std::vector v;face_freq_table(v); + voro_print_vector(v,fp); + } + void face_vertices(std::vector &v); + /** Outputs the */ + inline void output_face_vertices(FILE *fp=stdout) { + std::vector v;face_vertices(v); + voro_print_face_vertices(v,fp); + } + void face_perimeters(std::vector &v); + /** Outputs a list of the perimeters of each face. + * \param[in] fp the file handle to write to. */ + inline void output_face_perimeters(FILE *fp=stdout) { + std::vector v;face_perimeters(v); + voro_print_vector(v,fp); + } + void normals(std::vector &v); + /** Outputs a list of the perimeters of each face. + * \param[in] fp the file handle to write to. */ + inline void output_normals(FILE *fp=stdout) { + std::vector v;normals(v); + voro_print_positions(v,fp); + } + /** Outputs a custom string of information about the Voronoi + * cell to a file. It assumes the cell is at (0,0,0) and has a + * the default_radius associated with it. + * \param[in] format the custom format string to use. + * \param[in] fp the file handle to write to. */ + inline void output_custom(const char *format,FILE *fp=stdout) {output_custom(format,0,0,0,0,default_radius,fp);} + void output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp=stdout); + template + bool nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id); + bool plane_intersects(double x,double y,double z,double rsq); + bool plane_intersects_guess(double x,double y,double z,double rsq); + void construct_relations(); + void check_relations(); + void check_duplicates(); + void print_edges(); + /** Returns a list of IDs of neighboring particles + * corresponding to each face. + * \param[out] v a reference to a vector in which to return the + * results. If no neighbor information is + * available, a blank vector is returned. */ + virtual void neighbors(std::vector &v) {v.clear();} + /** This is a virtual function that is overridden by a routine + * to print a list of IDs of neighboring particles + * corresponding to each face. By default, when no neighbor + * information is available, the routine does nothing. + * \param[in] fp the file handle to write to. */ + virtual void output_neighbors(FILE *fp=stdout) {} + /** This a virtual function that is overridden by a routine to + * print the neighboring particle IDs for a given vertex. By + * default, when no neighbor information is available, the + * routine does nothing. + * \param[in] i the vertex to consider. */ + virtual void print_edges_neighbors(int i) {}; + /** This is a simple inline function for picking out the index + * of the next edge counterclockwise at the current vertex. + * \param[in] a the index of an edge of the current vertex. + * \param[in] p the number of the vertex. + * \return 0 if a=nu[p]-1, or a+1 otherwise. */ + inline int cycle_up(int a,int p) {return a==nu[p]-1?0:a+1;} + /** This is a simple inline function for picking out the index + * of the next edge clockwise from the current vertex. + * \param[in] a the index of an edge of the current vertex. + * \param[in] p the number of the vertex. + * \return nu[p]-1 if a=0, or a-1 otherwise. */ + inline int cycle_down(int a,int p) {return a==0?nu[p]-1:a-1;} + protected: + /** This a one dimensional array that holds the current sizes + * of the memory allocations for them mep array.*/ + int *mem; + /** This is a one dimensional array that holds the current + * number of vertices of order p that are stored in the mep[p] + * array. */ + int *mec; + /** This is a two dimensional array for holding the information + * about the edges of the Voronoi cell. mep[p] is a + * one-dimensional array for holding the edge information about + * all vertices of order p, with each vertex holding 2*p+1 + * integers of information. The total number of vertices held + * on mep[p] is stored in mem[p]. If the space runs out, the + * code allocates more using the add_memory() routine. */ + int **mep; + inline void reset_edges(); + template + void check_memory_for_copy(vc_class &vc,voronoicell_base* vb); + void copy(voronoicell_base* vb); + private: + /** This is the delete stack, used to store the vertices which + * are going to be deleted during the plane cutting procedure. + */ + int *ds,*stacke; + /** This is the auxiliary delete stack, which has size set by + * current_delete2_size. */ + int *ds2,*stacke2; + /** This stores the current memory allocation for the marginal + * cases. */ + int current_marginal; + /** This stores the total number of marginal points which are + * currently in the buffer. */ + int n_marg; + /** This array contains a list of the marginal points, and also + * the outcomes of the marginal tests. */ + int *marg; + /** The x coordinate of the normal vector to the test plane. */ + double px; + /** The y coordinate of the normal vector to the test plane. */ + double py; + /** The z coordinate of the normal vector to the test plane. */ + double pz; + /** The magnitude of the normal vector to the test plane. */ + double prsq; + template + void add_memory(vc_class &vc,int i,int *stackp2); + template + void add_memory_vertices(vc_class &vc); + template + void add_memory_vorder(vc_class &vc); + void add_memory_ds(int *&stackp); + void add_memory_ds2(int *&stackp2); + template + inline bool collapse_order1(vc_class &vc); + template + inline bool collapse_order2(vc_class &vc); + template + inline bool delete_connection(vc_class &vc,int j,int k,bool hand); + template + inline bool search_for_outside_edge(vc_class &vc,int &up); + template + inline void add_to_stack(vc_class &vc,int lp,int *&stackp2); + inline bool plane_intersects_track(double x,double y,double z,double rs,double g); + inline void normals_search(std::vector &v,int i,int j,int k); + inline bool search_edge(int l,int &m,int &k); + inline int m_test(int n,double &ans); + int check_marginal(int n,double &ans); + friend class voronoicell; + friend class voronoicell_neighbor; +}; + +/** \brief Extension of the voronoicell_base class to represent a Voronoi + * cell without neighbor information. + * + * This class is an extension of the voronoicell_base class, in cases when + * is not necessary to track the IDs of neighboring particles associated + * with each face of the Voronoi cell. */ +class voronoicell : public voronoicell_base { + public: + using voronoicell_base::nplane; + /** Copies the information from another voronoicell class into + * this class, extending memory allocation if necessary. + * \param[in] c the class to copy. */ + inline void operator=(voronoicell &c) { + voronoicell_base* vb((voronoicell_base*) &c); + check_memory_for_copy(*this,vb);copy(vb); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \param[in] rsq the modulus squared of the vector. + * \param[in] p_id the plane ID, ignored for this case where no + * neighbor tracking is enabled. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,double rsq,int p_id) { + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \param[in] p_id the plane ID, ignored for this case where no + * neighbor tracking is enabled. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,int p_id) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \param[in] rsq the modulus squared of the vector. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z,double rsq) { + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,0); + } + /** Initializes the Voronoi cell to be rectangular box with the + * given dimensions. + * \param[in] (xmin,xmax) the minimum and maximum x coordinates. + * \param[in] (ymin,ymax) the minimum and maximum y coordinates. + * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ + inline void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { + init_base(xmin,xmax,ymin,ymax,zmin,zmax); + } + /** Initializes the cell to be an octahedron with vertices at + * (l,0,0), (-l,0,0), (0,l,0), (0,-l,0), (0,0,l), and (0,0,-l). + * \param[in] l a parameter setting the size of the octahedron. + */ + inline void init_octahedron(double l) { + init_octahedron_base(l); + } + /** Initializes the cell to be a tetrahedron. + * \param[in] (x0,y0,z0) the coordinates of the first vertex. + * \param[in] (x1,y1,z1) the coordinates of the second vertex. + * \param[in] (x2,y2,z2) the coordinates of the third vertex. + * \param[in] (x3,y3,z3) the coordinates of the fourth vertex. + */ + inline void init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { + init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3); + } + private: + inline void n_allocate(int i,int m) {}; + inline void n_add_memory_vertices(int i) {}; + inline void n_add_memory_vorder(int i) {}; + inline void n_set_pointer(int p,int n) {}; + inline void n_copy(int a,int b,int c,int d) {}; + inline void n_set(int a,int b,int c) {}; + inline void n_set_aux1(int k) {}; + inline void n_copy_aux1(int a,int b) {}; + inline void n_copy_aux1_shift(int a,int b) {}; + inline void n_set_aux2_copy(int a,int b) {}; + inline void n_copy_pointer(int a,int b) {}; + inline void n_set_to_aux1(int j) {}; + inline void n_set_to_aux2(int j) {}; + inline void n_allocate_aux1(int i) {}; + inline void n_switch_to_aux1(int i) {}; + inline void n_copy_to_aux1(int i,int m) {}; + inline void n_set_to_aux1_offset(int k,int m) {}; + inline void n_neighbors(std::vector &v) {v.clear();}; + friend class voronoicell_base; +}; + +/** \brief Extension of the voronoicell_base class to represent a Voronoi cell + * with neighbor information. + * + * This class is an extension of the voronoicell_base class, in cases when the + * IDs of neighboring particles associated with each face of the Voronoi cell. + * It contains additional data structures mne and ne for storing this + * information. */ +class voronoicell_neighbor : public voronoicell_base { + public: + using voronoicell_base::nplane; + /** This two dimensional array holds the neighbor information + * associated with each vertex. mne[p] is a one dimensional + * array which holds all of the neighbor information for + * vertices of order p. */ + int **mne; + /** This is a two dimensional array that holds the neighbor + * information associated with each vertex. ne[i] points to a + * one-dimensional array in mne[nu[i]]. ne[i][j] holds the + * neighbor information associated with the jth edge of vertex + * i. It is set to the ID number of the plane that made the + * face that is clockwise from the jth edge. */ + int **ne; + voronoicell_neighbor(); + ~voronoicell_neighbor(); + void operator=(voronoicell &c); + void operator=(voronoicell_neighbor &c); + /** Cuts the Voronoi cell by a particle whose center is at a + * separation of (x,y,z) from the cell center. The value of rsq + * should be initially set to \f$x^2+y^2+z^2\f$. + * \param[in] (x,y,z) the normal vector to the plane. + * \param[in] rsq the distance along this vector of the plane. + * \param[in] p_id the plane ID (for neighbor tracking only). + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,double rsq,int p_id) { + return nplane(*this,x,y,z,rsq,p_id); + } + /** This routine calculates the modulus squared of the vector + * before passing it to the main nplane() routine with full + * arguments. + * \param[in] (x,y,z) the vector to cut the cell by. + * \param[in] p_id the plane ID (for neighbor tracking only). + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,int p_id) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,p_id); + } + /** This version of the plane routine just makes up the plane + * ID to be zero. It will only be referenced if neighbor + * tracking is enabled. + * \param[in] (x,y,z) the vector to cut the cell by. + * \param[in] rsq the modulus squared of the vector. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z,double rsq) { + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using the influence of a particle at + * (x,y,z), first calculating the modulus squared of this + * vector before passing it to the main nplane() routine. Zero + * is supplied as the plane ID, which will be ignored unless + * neighbor tracking is enabled. + * \param[in] (x,y,z) the vector to cut the cell by. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,0); + } + void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax); + void init_octahedron(double l); + void init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3); + void check_facets(); + virtual void neighbors(std::vector &v); + virtual void print_edges_neighbors(int i); + virtual void output_neighbors(FILE *fp=stdout) { + std::vector v;neighbors(v); + voro_print_vector(v,fp); + } + private: + int *paux1; + int *paux2; + inline void n_allocate(int i,int m) {mne[i]=new int[m*i];} + inline void n_add_memory_vertices(int i) { + int **pp=new int*[i]; + for(int j=0;j + +#include "voro++.hh" +using namespace voro; + +enum blocks_mode { + none, + length_scale, + specified +}; + +// A maximum allowed number of regions, to prevent enormous amounts of memory +// being allocated +const int max_regions=16777216; + +// This message gets displayed if the user requests the help flag +void help_message() { + puts("Voro++ version 0.4.5, by Chris H. Rycroft (UC Berkeley/LBL)\n\n" + "Syntax: voro++ [options] \n" + " \n\n" + "By default, the utility reads in the input file of particle IDs and positions,\n" + "computes the Voronoi cell for each, and then creates with an\n" + "additional column containing the volume of each Voronoi cell.\n\n" + "Available options:\n" + " -c : Specify a custom output string\n" + " -g : Turn on the gnuplot output to \n" + " -h/--help : Print this information\n" + " -hc : Print information about custom output\n" + " -l : Manually specify a length scale to configure the internal\n" + " computational grid\n" + " -m : Manually choose the memory allocation per grid block\n" + " (default 8)\n" + " -n [3] : Manually specify the internal grid size\n" + " -o : Ensure that the output file has the same order as the input\n" + " file\n" + " -p : Make container periodic in all three directions\n" + " -px : Make container periodic in the x direction\n" + " -py : Make container periodic in the y direction\n" + " -pz : Make container periodic in the z direction\n" + " -r : Assume the input file has an extra coordinate for radii\n" + " -v : Verbose output\n" + " --version : Print version information\n" + " -wb [6] : Add six plane wall objects to make rectangular box containing\n" + " the space x1 and POV-Ray Voronoi\n" + " cells to \n" + " -yp : Save only POV-Ray particles to \n" + " -yv : Save only POV-Ray Voronoi cells to "); +} + +// This message gets displayed if the user requests information about doing +// custom output +void custom_output_message() { + puts("The \"-c\" option allows a string to be specified that will customize the output\n" + "file to contain a variety of statistics about each computed Voronoi cell. The\n" + "string is similar to the standard C printf() function, made up of text with\n" + "additional control sequences that begin with percentage signs that are expanded\n" + "to different statistics. See http://math.lbl.gov/voro++/doc/custom.html for more\n" + "information.\n" + "\nParticle-related:\n" + " %i The particle ID number\n" + " %x The x coordinate of the particle\n" + " %y The y coordinate of the particle\n" + " %z The z coordinate of the particle\n" + " %q The position vector of the particle, short for \"%x %y %z\"\n" + " %r The radius of the particle (only printed if -p enabled)\n" + "\nVertex-related:\n" + " %w The number of vertices in the Voronoi cell\n" + " %p A list of the vertices of the Voronoi cell in the format (x,y,z),\n" + " relative to the particle center\n" + " %P A list of the vertices of the Voronoi cell in the format (x,y,z),\n" + " relative to the global coordinate system\n" + " %o A list of the orders of each vertex\n" + " %m The maximum radius squared of a vertex position, relative to the\n" + " particle center\n" + "\nEdge-related:\n" + " %g The number of edges of the Voronoi cell\n" + " %E The total edge distance\n" + " %e A list of perimeters of each face\n" + "\nFace-related:\n" + " %s The number of faces of the Voronoi cell\n" + " %F The total surface area of the Voronoi cell\n" + " %A A frequency table of the number of edges for each face\n" + " %a A list of the number of edges for each face\n" + " %f A list of areas of each face\n" + " %t A list of bracketed sequences of vertices that make up each face\n" + " %l A list of normal vectors for each face\n" + " %n A list of neighboring particle or wall IDs corresponding to each face\n" + "\nVolume-related:\n" + " %v The volume of the Voronoi cell\n" + " %c The centroid of the Voronoi cell, relative to the particle center\n" + " %C The centroid of the Voronoi cell, in the global coordinate system"); +} + +// Ths message is displayed if the user requests version information +void version_message() { + puts("Voro++ version 0.4.5 (July 27th 2012)"); +} + +// Prints an error message. This is called when the program is unable to make +// sense of the command-line options. +void error_message() { + fputs("voro++: Unrecognized command-line options; type \"voro++ -h\" for more\ninformation.\n",stderr); +} + +// Carries out the Voronoi computation and outputs the results to the requested +// files +template +void cmd_line_output(c_loop &vl,c_class &con,const char* format,FILE* outfile,FILE* gnu_file,FILE* povp_file,FILE* povv_file,bool verbose,double &vol,int &vcc,int &tp) { + int pid,ps=con.ps;double x,y,z,r; + if(con.contains_neighbor(format)) { + voronoicell_neighbor c; + if(vl.start()) do if(con.compute_cell(c,vl)) { + vl.pos(pid,x,y,z,r); + if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile); + if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file); + if(povp_file!=NULL) { + fprintf(povp_file,"// id %d\n",pid); + if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r); + else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z); + } + if(povv_file!=NULL) { + fprintf(povv_file,"// cell %d\n",pid); + c.draw_pov(x,y,z,povv_file); + } + if(verbose) {vol+=c.volume();vcc++;} + } while(vl.inc()); + } else { + voronoicell c; + if(vl.start()) do if(con.compute_cell(c,vl)) { + vl.pos(pid,x,y,z,r); + if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile); + if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file); + if(povp_file!=NULL) { + fprintf(povp_file,"// id %d\n",pid); + if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r); + else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z); + } + if(povv_file!=NULL) { + fprintf(povv_file,"// cell %d\n",pid); + c.draw_pov(x,y,z,povv_file); + } + if(verbose) {vol+=c.volume();vcc++;} + } while(vl.inc()); + } + if(verbose) tp=con.total_particles(); +} + +int main(int argc,char **argv) { + int i=1,j=-7,custom_output=0,nx,ny,nz,init_mem(8); + double ls=0; + blocks_mode bm=none; + bool gnuplot_output=false,povp_output=false,povv_output=false,polydisperse=false; + bool xperiodic=false,yperiodic=false,zperiodic=false,ordered=false,verbose=false; + pre_container *pcon=NULL;pre_container_poly *pconp=NULL; + wall_list wl; + + // If there's one argument, check to see if it's requesting help. + // Otherwise, bail out with an error. + if(argc==2) { + if(strcmp(argv[1],"-h")==0||strcmp(argv[1],"--help")==0) { + help_message();return 0; + } else if(strcmp(argv[1],"-hc")==0) { + custom_output_message();return 0; + } else if(strcmp(argv[1],"--version")==0) { + version_message();return 0; + } else { + error_message(); + return VOROPP_CMD_LINE_ERROR; + } + } + + // If there aren't enough command-line arguments, then bail out + // with an error. + if(argc<7) { + error_message(); + return VOROPP_CMD_LINE_ERROR; + } + + // We have enough arguments. Now start searching for command-line + // options. + while(i=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + if(custom_output==0) { + custom_output=++i; + } else { + fputs("voro++: multiple custom output strings detected\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + } else if(strcmp(argv[i],"-g")==0) { + gnuplot_output=true; + } else if(strcmp(argv[i],"-h")==0||strcmp(argv[i],"--help")==0) { + help_message();wl.deallocate();return 0; + } else if(strcmp(argv[i],"-hc")==0) { + custom_output_message();wl.deallocate();return 0; + } else if(strcmp(argv[i],"-l")==0) { + if(i>=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + if(bm!=none) { + fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + bm=length_scale; + i++;ls=atof(argv[i]); + } else if(strcmp(argv[i],"-m")==0) { + i++;init_mem=atoi(argv[i]); + } else if(strcmp(argv[i],"-n")==0) { + if(i>=argc-10) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + if(bm!=none) { + fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + bm=specified; + i++; + nx=atoi(argv[i++]); + ny=atoi(argv[i++]); + nz=atoi(argv[i]); + if(nx<=0||ny<=0||nz<=0) { + fputs("voro++: Computational grid specified with -n must be greater than one\n" + "in each direction\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + } else if(strcmp(argv[i],"-o")==0) { + ordered=true; + } else if(strcmp(argv[i],"-p")==0) { + xperiodic=yperiodic=zperiodic=true; + } else if(strcmp(argv[i],"-px")==0) { + xperiodic=true; + } else if(strcmp(argv[i],"-py")==0) { + yperiodic=true; + } else if(strcmp(argv[i],"-pz")==0) { + zperiodic=true; + } else if(strcmp(argv[i],"-r")==0) { + polydisperse=true; + } else if(strcmp(argv[i],"-v")==0) { + verbose=true; + } else if(strcmp(argv[i],"--version")==0) { + version_message(); + wl.deallocate(); + return 0; + } else if(strcmp(argv[i],"-wb")==0) { + if(i>=argc-13) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i++]); + double w4=atof(argv[i++]),w5=atof(argv[i]); + wl.add_wall(new wall_plane(-1,0,0,-w0,j));j--; + wl.add_wall(new wall_plane(1,0,0,w1,j));j--; + wl.add_wall(new wall_plane(0,-1,0,-w2,j));j--; + wl.add_wall(new wall_plane(0,1,0,w3,j));j--; + wl.add_wall(new wall_plane(0,0,-1,-w4,j));j--; + wl.add_wall(new wall_plane(0,0,1,w5,j));j--; + } else if(strcmp(argv[i],"-ws")==0) { + if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i]); + wl.add_wall(new wall_sphere(w0,w1,w2,w3,j)); + j--; + } else if(strcmp(argv[i],"-wp")==0) { + if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i]); + wl.add_wall(new wall_plane(w0,w1,w2,w3,j)); + j--; + } else if(strcmp(argv[i],"-wc")==0) { + if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i++]); + double w4=atof(argv[i++]),w5=atof(argv[i++]); + double w6=atof(argv[i]); + wl.add_wall(new wall_cylinder(w0,w1,w2,w3,w4,w5,w6,j)); + j--; + } else if(strcmp(argv[i],"-wo")==0) { + if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i++]); + double w4=atof(argv[i++]),w5=atof(argv[i++]); + double w6=atof(argv[i]); + wl.add_wall(new wall_cone(w0,w1,w2,w3,w4,w5,w6,j)); + j--; + } else if(strcmp(argv[i],"-y")==0) { + povp_output=povv_output=true; + } else if(strcmp(argv[i],"-yp")==0) { + povp_output=true; + } else if(strcmp(argv[i],"-yv")==0) { + povv_output=true; + } else { + wl.deallocate(); + error_message(); + return VOROPP_CMD_LINE_ERROR; + } + i++; + } + + // Check the memory guess is positive + if(init_mem<=0) { + fputs("voro++: The memory allocation must be positive\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + + // Read in the dimensions of the test box, and estimate the number of + // boxes to divide the region up into + double ax=atof(argv[i]),bx=atof(argv[i+1]); + double ay=atof(argv[i+2]),by=atof(argv[i+3]); + double az=atof(argv[i+4]),bz=atof(argv[i+5]); + + // Check that for each coordinate, the minimum value is smaller + // than the maximum value + if(bximport(argv[i+6]); + pconp->guess_optimal(nx,ny,nz); + } else { + pcon=new pre_container(ax,bx,ay,by,az,bz,xperiodic,yperiodic,zperiodic); + pcon->import(argv[i+6]); + pcon->guess_optimal(nx,ny,nz); + } + } else { + double nxf,nyf,nzf; + if(bm==length_scale) { + + // Check that the length scale is positive and + // reasonably large + if(lsmax_regions) { + fprintf(stderr,"voro++: Number of computational blocks exceeds the maximum allowed of %d.\n" + "Either increase the particle length scale, or recompile with an increased\nmaximum.",max_regions); + wl.deallocate(); + return VOROPP_MEMORY_ERROR; + } + } + + // Check that the output filename is a sensible length + int flen=strlen(argv[i+6]); + if(flen>4096) { + fputs("voro++: Filename too long\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + + // Open files for output + char *buffer=new char[flen+7]; + sprintf(buffer,"%s.vol",argv[i+6]); + FILE *outfile=safe_fopen(buffer,"w"),*gnu_file,*povp_file,*povv_file; + if(gnuplot_output) { + sprintf(buffer,"%s.gnu",argv[i+6]); + gnu_file=safe_fopen(buffer,"w"); + } else gnu_file=NULL; + if(povp_output) { + sprintf(buffer,"%s_p.pov",argv[i+6]); + povp_file=safe_fopen(buffer,"w"); + } else povp_file=NULL; + if(povv_output) { + sprintf(buffer,"%s_v.pov",argv[i+6]); + povv_file=safe_fopen(buffer,"w"); + } else povv_file=NULL; + delete [] buffer; + + const char *c_str=(custom_output==0?(polydisperse?"%i %q %v %r":"%i %q %v"):argv[custom_output]); + + // Now switch depending on whether polydispersity was enabled, and + // whether output ordering is requested + double vol=0;int tp=0,vcc=0; + if(polydisperse) { + if(ordered) { + particle_order vo; + container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + if(bm==none) { + pconp->setup(vo,con);delete pconp; + } else con.import(vo,argv[i+6]); + + c_loop_order vlo(con,vo); + cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } else { + container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + + if(bm==none) { + pconp->setup(con);delete pconp; + } else con.import(argv[i+6]); + + c_loop_all vla(con); + cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } + } else { + if(ordered) { + particle_order vo; + container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + if(bm==none) { + pcon->setup(vo,con);delete pcon; + } else con.import(vo,argv[i+6]); + + c_loop_order vlo(con,vo); + cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } else { + container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + if(bm==none) { + pcon->setup(con);delete pcon; + } else con.import(argv[i+6]); + c_loop_all vla(con); + cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } + } + + // Print information if verbose output requested + if(verbose) { + printf("Container geometry : [%g:%g] [%g:%g] [%g:%g]\n" + "Computational grid size : %d by %d by %d (%s)\n" + "Filename : %s\n" + "Output string : %s%s\n",ax,bx,ay,by,az,bz,nx,ny,nz, + bm==none?"estimated from file":(bm==length_scale? + "estimated using length scale":"directly specified"), + argv[i+6],c_str,custom_output==0?" (default)":""); + printf("Total imported particles : %d (%.2g per grid block)\n" + "Total V. cells computed : %d\n" + "Total container volume : %g\n" + "Total V. cell volume : %g\n",tp,((double) tp)/(nx*ny*nz), + vcc,(bx-ax)*(by-ay)*(bz-az),vol); + } + + // Close output files + fclose(outfile); + if(gnu_file!=NULL) fclose(gnu_file); + if(povp_file!=NULL) fclose(povp_file); + if(povv_file!=NULL) fclose(povv_file); + return 0; +} + diff --git a/src/voro++/common.cpp b/src/voro++/common.cpp new file mode 100644 index 00000000..9e3dfc59 --- /dev/null +++ b/src/voro++/common.cpp @@ -0,0 +1,90 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file common.cpp + * \brief Implementations of the small helper functions. */ + +#include "common.hh" + +namespace voro { + +/** \brief Prints a vector of integers. + * + * Prints a vector of integers. + * \param[in] v the vector to print. + * \param[in] fp the file stream to print to. */ +void voro_print_vector(std::vector &v,FILE *fp) { + int k=0,s=v.size(); + while(k+4 &v,FILE *fp) { + int k=0,s=v.size(); + while(k+4 &v,FILE *fp) { + int j,k=0,l; + if(v.size()>0) { + l=v[k++]; + if(l<=1) { + if(l==1) fprintf(fp,"(%d)",v[k++]); + else fputs("()",fp); + } else { + j=k+l; + fprintf(fp,"(%d",v[k++]); + while(k +#include +#include + +#include "config.hh" + +namespace voro { + +/** \brief Function for printing fatal error messages and exiting. + * + * Function for printing fatal error messages and exiting. + * \param[in] p a pointer to the message to print. + * \param[in] status the status code to return with. */ +inline void voro_fatal_error(const char *p,int status) { + fprintf(stderr,"voro++: %s\n",p); + exit(status); +} + +/** \brief Prints a vector of positions. + * + * Prints a vector of positions as bracketed triplets. + * \param[in] v the vector to print. + * \param[in] fp the file stream to print to. */ +inline void voro_print_positions(std::vector &v,FILE *fp=stdout) { + if(v.size()>0) { + fprintf(fp,"(%g,%g,%g)",v[0],v[1],v[2]); + for(int k=3;(unsigned int) k &v,FILE *fp=stdout); +void voro_print_vector(std::vector &v,FILE *fp=stdout); +void voro_print_face_vertices(std::vector &v,FILE *fp=stdout); + +} + +#endif diff --git a/src/voro++/config.hh b/src/voro++/config.hh new file mode 100644 index 00000000..093cd76c --- /dev/null +++ b/src/voro++/config.hh @@ -0,0 +1,127 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file config.hh + * \brief Master configuration file for setting various compile-time options. */ + +#ifndef VOROPP_CONFIG_HH +#define VOROPP_CONFIG_HH + +namespace voro { + +// These constants set the initial memory allocation for the Voronoi cell +/** The initial memory allocation for the number of vertices. */ +const int init_vertices=256; +/** The initial memory allocation for the maximum vertex order. */ +const int init_vertex_order=64; +/** The initial memory allocation for the number of regular vertices of order + * 3. */ +const int init_3_vertices=256; +/** The initial memory allocation for the number of vertices of higher order. + */ +const int init_n_vertices=8; +/** The initial buffer size for marginal cases used by the suretest class. */ +const int init_marginal=64; +/** The initial size for the delete stack. */ +const int init_delete_size=256; +/** The initial size for the auxiliary delete stack. */ +const int init_delete2_size=256; +/** The initial size for the wall pointer array. */ +const int init_wall_size=32; +/** The default initial size for the ordering class. */ +const int init_ordering_size=4096; +/** The initial size of the pre_container chunk index. */ +const int init_chunk_size=256; + +// If the initial memory is too small, the program dynamically allocates more. +// However, if the limits below are reached, then the program bails out. +/** The maximum memory allocation for the number of vertices. */ +const int max_vertices=16777216; +/** The maximum memory allocation for the maximum vertex order. */ +const int max_vertex_order=2048; +/** The maximum memory allocation for the any particular order of vertex. */ +const int max_n_vertices=16777216; +/** The maximum buffer size for marginal cases used by the suretest class. */ +const int max_marginal=16777216; +/** The maximum size for the delete stack. */ +const int max_delete_size=16777216; +/** The maximum size for the auxiliary delete stack. */ +const int max_delete2_size=16777216; +/** The maximum amount of particle memory allocated for a single region. */ +const int max_particle_memory=16777216; +/** The maximum size for the wall pointer array. */ +const int max_wall_size=2048; +/** The maximum size for the ordering class. */ +const int max_ordering_size=67108864; +/** The maximum size for the pre_container chunk index. */ +const int max_chunk_size=65536; + +/** The chunk size in the pre_container classes. */ +const int pre_container_chunk_size=1024; + +#ifndef VOROPP_VERBOSE +/** Voro++ can print a number of different status and debugging messages to + * notify the user of special behavior, and this macro sets the amount which + * are displayed. At level 0, no messages are printed. At level 1, messages + * about unusual cases during cell construction are printed, such as when the + * plane routine bails out due to floating point problems. At level 2, general + * messages about memory expansion are printed. At level 3, technical details + * about memory management are printed. */ +#define VOROPP_VERBOSE 0 +#endif + +/** If a point is within this distance of a cutting plane, then the code + * assumes that point exactly lies on the plane. */ +const double tolerance=1e-11; + +/** If a point is within this distance of a cutting plane, then the code stores + * whether this point is inside, outside, or exactly on the cutting plane in + * the marginal cases buffer, to prevent the test giving a different result on + * a subsequent evaluation due to floating point rounding errors. */ +const double tolerance2=2e-11; + +/** The square of the tolerance, used when deciding whether some squared + * quantities are large enough to be used. */ +const double tolerance_sq=tolerance*tolerance; + +/** A large number that is used in the computation. */ +const double large_number=1e30; + +/** A radius to use as a placeholder when no other information is available. */ +const double default_radius=0.5; + +/** The maximum number of shells of periodic images to test over. */ +const int max_unit_voro_shells=10; + +/** A guess for the optimal number of particles per block, used to set up the + * container grid. */ +const double optimal_particles=5.6; + +/** If this is set to 1, then the code reports any instances of particles being + * put outside of the container geometry. */ +#define VOROPP_REPORT_OUT_OF_BOUNDS 0 + +/** Voro++ returns this status code if there is a file-related error, such as + * not being able to open file. */ +#define VOROPP_FILE_ERROR 1 + +/** Voro++ returns this status code if there is a memory allocation error, if + * one of the safe memory limits is exceeded. */ +#define VOROPP_MEMORY_ERROR 2 + +/** Voro++ returns this status code if there is any type of internal error, if + * it detects that representation of the Voronoi cell is inconsistent. This + * status code will generally indicate a bug, and the developer should be + * contacted. */ +#define VOROPP_INTERNAL_ERROR 3 + +/** Voro++ returns this status code if it could not interpret the command line + * arguments passed to the command line utility. */ +#define VOROPP_CMD_LINE_ERROR 4 + +} + +#endif diff --git a/src/voro++/container.cpp b/src/voro++/container.cpp new file mode 100644 index 00000000..3d22c1ee --- /dev/null +++ b/src/voro++/container.cpp @@ -0,0 +1,549 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file container.cpp + * \brief Function implementations for the container and related classes. */ + +#include "container.hh" + +namespace voro { + +/** The class constructor sets up the geometry of container, initializing the + * minimum and maximum coordinates in each direction, and setting whether each + * direction is periodic or not. It divides the container into a rectangular + * grid of blocks, and allocates memory for each of these for storing particle + * positions and IDs. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] (xperiodic_,yperiodic_,zperiodic_) flags setting whether the + * container is periodic in each + * coordinate direction. + * \param[in] init_mem the initial memory allocation for each block. + * \param[in] ps_ the number of floating point entries to store for each + * particle. */ +container_base::container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem,int ps_) + : voro_base(nx_,ny_,nz_,(bx_-ax_)/nx_,(by_-ay_)/ny_,(bz_-az_)/nz_), + ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_), + xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_), + id(new int*[nxyz]), p(new double*[nxyz]), co(new int[nxyz]), mem(new int[nxyz]), ps(ps_) { + int l; + for(l=0;l=nx) return false; + + int j=step_int((y-ay)*ysp); + if(yperiodic) {l=step_mod(j,ny);y+=boxy*(l-j);j=l;} + else if(j<0||j>=ny) return false; + + int k=step_int((z-az)*zsp); + if(zperiodic) {l=step_mod(k,nz);z+=boxz*(l-k);k=l;} + else if(k<0||k>=nz) return false; + + ijk+=nx*j+nxy*k; + return true; +} + +/** Takes a position vector and attempts to remap it into the primary domain. + * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in, + * with (0,0,0) corresponding to the primary domain. + * \param[out] (ci,cj,ck) the index of the block that the position vector is + * within, once it has been remapped. + * \param[in,out] (x,y,z) the position vector to consider, which is remapped + * into the primary domain during the routine. + * \param[out] ijk the block index that the vector is within. + * \return True if the particle is within the container or can be remapped into + * it, false if it lies outside of the container bounds. */ +inline bool container_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) { + ci=step_int((x-ax)*xsp); + if(ci<0||ci>=nx) { + if(xperiodic) {ai=step_div(ci,nx);x-=ai*(bx-ax);ci-=ai*nx;} + else return false; + } else ai=0; + + cj=step_int((y-ay)*ysp); + if(cj<0||cj>=ny) { + if(yperiodic) {aj=step_div(cj,ny);y-=aj*(by-ay);cj-=aj*ny;} + else return false; + } else aj=0; + + ck=step_int((z-az)*zsp); + if(ck<0||ck>=nz) { + if(zperiodic) {ak=step_div(ck,nz);z-=ak*(bz-az);ck-=ak*nz;} + else return false; + } else ak=0; + + ijk=ci+nx*cj+nxy*ck; + return true; +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. This is equivalent to finding the particle which is nearest to the + * vector. Additional wall classes are not considered by this routine. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. If the container is periodic, + * this may point to a particle in a periodic image of + * the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // If the given vector lies outside the domain, but the container + // is periodic, then remap it back into the domain + if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false; + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);} + if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);} + if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);} + rx=p[w.ijk][3*w.l]+ai*(bx-ax); + ry=p[w.ijk][3*w.l+1]+aj*(by-ay); + rz=p[w.ijk][3*w.l+2]+ak*(bz-az); + pid=id[w.ijk][w.l]; + return true; + } + + // If no particle if found then just return false + return false; +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. Additional wall classes are not considered by this routine. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. If the container is periodic, + * this may point to a particle in a periodic image of + * the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // If the given vector lies outside the domain, but the container + // is periodic, then remap it back into the domain + if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false; + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);} + if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);} + if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);} + rx=p[w.ijk][4*w.l]+ai*(bx-ax); + ry=p[w.ijk][4*w.l+1]+aj*(by-ay); + rz=p[w.ijk][4*w.l+2]+ak*(bz-az); + pid=id[w.ijk][w.l]; + return true; + } + + // If no particle if found then just return false + return false; +} + +/** Increase memory for a particular region. + * \param[in] i the index of the region to reallocate. */ +void container_base::add_particle_memory(int i) { + int l,nmem=mem[i]<<1; + + // Carry out a check on the memory allocation size, and + // print a status message if requested + if(nmem>max_particle_memory) + voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=3 + fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem); +#endif + + // Allocate new memory and copy in the contents of the old arrays + int *idp=new int[nmem]; + for(l=0;lbx||yby||zbz) return false; + return point_inside_walls(x,y,z); +} + +/** Draws an outline of the domain in gnuplot format. + * \param[in] fp the file handle to write to. */ +void container_base::draw_domain_gnuplot(FILE *fp) { + fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,ay,az,bx,ay,az,bx,by,az,ax,by,az); + fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,by,bz,bx,by,bz,bx,ay,bz,ax,ay,bz); + fprintf(fp,"%g %g %g\n\n%g %g %g\n%g %g %g\n\n",ax,by,bz,ax,ay,az,ax,ay,bz); + fprintf(fp,"%g %g %g\n%g %g %g\n\n%g %g %g\n%g %g %g\n\n",bx,ay,az,bx,ay,bz,bx,by,az,bx,by,bz); +} + +/** Draws an outline of the domain in POV-Ray format. + * \param[in] fp the file handle to write to. */ +void container_base::draw_domain_pov(FILE *fp) { + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,by,bz,bx,by,bz,ax,ay,bz,bx,ay,bz); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,by,az,bx,ay,az,bx,by,az); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,ay,bz,bx,by,bz,ax,ay,bz,ax,by,bz); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,ay,bz,bx,ay,az,bx,ay,bz); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,by,az,bx,by,bz,ax,by,az,ax,by,bz); + fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" + "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az); + fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" + "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,bz,bx,ay,bz,ax,by,bz,bx,by,bz); +} + + +/** The wall_list constructor sets up an array of pointers to wall classes. */ +wall_list::wall_list() : walls(new wall*[init_wall_size]), wep(walls), wel(walls+init_wall_size), + current_wall_size(init_wall_size) {} + +/** The wall_list destructor frees the array of pointers to the wall classes. + */ +wall_list::~wall_list() { + delete [] walls; +} + +/** Adds all of the walls on another wall_list to this class. + * \param[in] wl a reference to the wall class. */ +void wall_list::add_wall(wall_list &wl) { + for(wall **wp=wl.walls;wpmax_wall_size) + voro_fatal_error("Wall memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); + wall **nwalls=new wall*[current_wall_size],**nwp=nwalls,**wp=walls; + while(wp +#include + +#include "config.hh" +#include "common.hh" +#include "v_base.hh" +#include "cell.hh" +#include "c_loops.hh" +#include "v_compute.hh" +#include "rad_option.hh" + +namespace voro { + +/** \brief Pure virtual class from which wall objects are derived. + * + * This is a pure virtual class for a generic wall object. A wall object + * can be specified by deriving a new class from this and specifying the + * functions.*/ +class wall { + public: + virtual ~wall() {} + /** A pure virtual function for testing whether a point is + * inside the wall object. */ + virtual bool point_inside(double x,double y,double z) = 0; + /** A pure virtual function for cutting a cell without + * neighbor-tracking with a wall. */ + virtual bool cut_cell(voronoicell &c,double x,double y,double z) = 0; + /** A pure virtual function for cutting a cell with + * neighbor-tracking enabled with a wall. */ + virtual bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) = 0; +}; + +/** \brief A class for storing a list of pointers to walls. + * + * This class stores a list of pointers to wall classes. It contains several + * simple routines that make use of the wall classes (such as telling whether a + * given position is inside all of the walls or not). It can be used by itself, + * but also forms part of container_base, for associating walls with this + * class. */ +class wall_list { + public: + /** An array holding pointers to wall objects. */ + wall **walls; + /** A pointer to the next free position to add a wall pointer. + */ + wall **wep; + wall_list(); + ~wall_list(); + /** Adds a wall to the list. + * \param[in] w the wall to add. */ + inline void add_wall(wall *w) { + if(wep==wel) increase_wall_memory(); + *(wep++)=w; + } + /** Adds a wall to the list. + * \param[in] w a reference to the wall to add. */ + inline void add_wall(wall &w) {add_wall(&w);} + void add_wall(wall_list &wl); + /** Determines whether a given position is inside all of the + * walls on the list. + * \param[in] (x,y,z) the position to test. + * \return True if it is inside, false if it is outside. */ + inline bool point_inside_walls(double x,double y,double z) { + for(wall **wp=walls;wppoint_inside(x,y,z))) return false; + return true; + } + /** Cuts a Voronoi cell by all of the walls currently on + * the list. + * \param[in] c a reference to the Voronoi cell class. + * \param[in] (x,y,z) the position of the cell. + * \return True if the cell still exists, false if the cell is + * deleted. */ + template + bool apply_walls(c_class &c,double x,double y,double z) { + for(wall **wp=walls;wpcut_cell(c,x,y,z))) return false; + return true; + } + void deallocate(); + protected: + void increase_wall_memory(); + /** A pointer to the limit of the walls array, used to + * determine when array is full. */ + wall **wel; + /** The current amount of memory allocated for walls. */ + int current_wall_size; +}; + +/** \brief Class for representing a particle system in a three-dimensional + * rectangular box. + * + * This class represents a system of particles in a three-dimensional + * rectangular box. Any combination of non-periodic and periodic coordinates + * can be used in the three coordinate directions. The class is not intended + * for direct use, but instead forms the base of the container and + * container_poly classes that add specialized routines for computing the + * regular and radical Voronoi tessellations respectively. It contains routines + * that are commonly between these two classes, such as those for drawing the + * domain, and placing particles within the internal data structure. + * + * The class is derived from the wall_list class, which encapsulates routines + * for associating walls with the container, and the voro_base class, which + * encapsulates routines about the underlying computational grid. */ +class container_base : public voro_base, public wall_list { + public: + /** The minimum x coordinate of the container. */ + const double ax; + /** The maximum x coordinate of the container. */ + const double bx; + /** The minimum y coordinate of the container. */ + const double ay; + /** The maximum y coordinate of the container. */ + const double by; + /** The minimum z coordinate of the container. */ + const double az; + /** The maximum z coordinate of the container. */ + const double bz; + /** A boolean value that determines if the x coordinate in + * periodic or not. */ + const bool xperiodic; + /** A boolean value that determines if the y coordinate in + * periodic or not. */ + const bool yperiodic; + /** A boolean value that determines if the z coordinate in + * periodic or not. */ + const bool zperiodic; + /** This array holds the numerical IDs of each particle in each + * computational box. */ + int **id; + /** A two dimensional array holding particle positions. For the + * derived container_poly class, this also holds particle + * radii. */ + double **p; + /** This array holds the number of particles within each + * computational box of the container. */ + int *co; + /** This array holds the maximum amount of particle memory for + * each computational box of the container. If the number of + * particles in a particular box ever approaches this limit, + * more is allocated using the add_particle_memory() function. + */ + int *mem; + /** The amount of memory in the array structure for each + * particle. This is set to 3 when the basic class is + * initialized, so that the array holds (x,y,z) positions. If + * the container class is initialized as part of the derived + * class container_poly, then this is set to 4, to also hold + * the particle radii. */ + const int ps; + container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_, + int init_mem,int ps_); + ~container_base(); + bool point_inside(double x,double y,double z); + void region_count(); + /** Initializes the Voronoi cell prior to a compute_cell + * operation for a specific particle being carried out by a + * voro_compute class. The cell is initialized to fill the + * entire container. For non-periodic coordinates, this is set + * by the position of the walls. For periodic coordinates, the + * space is equally divided in either direction from the + * particle's initial position. Plane cuts made by any walls + * that have been added are then applied to the cell. + * \param[in,out] c a reference to a voronoicell object. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within its block. + * \param[in] (ci,cj,ck) the coordinates of the block in the + * container coordinate system. + * \param[out] (i,j,k) the coordinates of the test block + * relative to the voro_compute + * coordinate system. + * \param[out] (x,y,z) the position of the particle. + * \param[out] disp a block displacement used internally by the + * compute_cell routine. + * \return False if the plane cuts applied by walls completely + * removed the cell, true otherwise. */ + template + inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck, + int &i,int &j,int &k,double &x,double &y,double &z,int &disp) { + double x1,x2,y1,y2,z1,z2,*pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + if(xperiodic) {x1=-(x2=0.5*(bx-ax));i=nx;} else {x1=ax-x;x2=bx-x;i=ci;} + if(yperiodic) {y1=-(y2=0.5*(by-ay));j=ny;} else {y1=ay-y;y2=by-y;j=cj;} + if(zperiodic) {z1=-(z2=0.5*(bz-az));k=nz;} else {z1=az-z;z2=bz-z;k=ck;} + c.init(x1,x2,y1,y2,z1,z2); + if(!apply_walls(c,x,y,z)) return false; + disp=ijk-i-nx*(j+ny*k); + return true; + } + /** Initializes parameters for a find_voronoi_cell call within + * the voro_compute template. + * \param[in] (ci,cj,ck) the coordinates of the test block in + * the container coordinate system. + * \param[in] ijk the index of the test block + * \param[out] (i,j,k) the coordinates of the test block + * relative to the voro_compute + * coordinate system. + * \param[out] disp a block displacement used internally by the + * find_voronoi_cell routine. */ + inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) { + i=xperiodic?nx:ci; + j=yperiodic?ny:cj; + k=zperiodic?nz:ck; + disp=ijk-i-nx*(j+ny*k); + } + /** Returns the position of a particle currently being computed + * relative to the computational block that it is within. It is + * used to select the optimal worklist entry to use. + * \param[in] (x,y,z) the position of the particle. + * \param[in] (ci,cj,ck) the block that the particle is within. + * \param[out] (fx,fy,fz) the position relative to the block. + */ + inline void frac_pos(double x,double y,double z,double ci,double cj,double ck, + double &fx,double &fy,double &fz) { + fx=x-ax-boxx*ci; + fy=y-ay-boxy*cj; + fz=z-az-boxz*ck; + } + /** Calculates the index of block in the container structure + * corresponding to given coordinates. + * \param[in] (ci,cj,ck) the coordinates of the original block + * in the current computation, relative + * to the container coordinate system. + * \param[in] (ei,ej,ek) the displacement of the current block + * from the original block. + * \param[in,out] (qx,qy,qz) the periodic displacement that + * must be added to the particles + * within the computed block. + * \param[in] disp a block displacement used internally by the + * find_voronoi_cell and compute_cell routines. + * \return The block index. */ + inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) { + if(xperiodic) {if(ci+ei=(nx<<1)) {ei-=nx;qx=bx-ax;} else qx=0;} + if(yperiodic) {if(cj+ej=(ny<<1)) {ej-=ny;qy=by-ay;} else qy=0;} + if(zperiodic) {if(ck+ek=(nz<<1)) {ek-=nz;qz=bz-az;} else qz=0;} + return disp+ei+nx*(ej+ny*ek); + } + void draw_domain_gnuplot(FILE *fp=stdout); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_gnuplot(const char* filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_domain_gnuplot(fp); + fclose(fp); + } + void draw_domain_pov(FILE *fp=stdout); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_pov(const char* filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_domain_pov(fp); + fclose(fp); + } + /** Sums up the total number of stored particles. + * \return The number of particles. */ + inline int total_particles() { + int tp=*co; + for(int *cop=co+1;cop + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Computes all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } else { + voronoicell c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx; + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z) { + int ijk; + if(put_locate_block(ijk,x,y,z)) { + double *pp=p[ijk]+3*co[ijk]++; + *(pp++)=x;*(pp++)=y;*pp=z; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--; + return q; + } + return false; + } + private: + voro_compute vc; + friend class voro_compute; +}; + +/** \brief Extension of the container_base class for computing radical Voronoi + * tessellations. + * + * This class is an extension of container_base class that has routines + * specifically for computing the radical Voronoi tessellation that depends on + * the particle radii. */ +class container_poly : public container_base, public radius_poly { + public: + container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem); + void clear(); + void put(int n,double x,double y,double z,double r); + void put(particle_order &vo,int n,double x,double y,double z,double r); + void import(FILE *fp=stdin); + void import(particle_order &vo,FILE *fp=stdin); + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. If the file cannot be successfully read, then the + * routine causes a fatal error. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. In addition, the order in which particles are read is + * saved into an ordering class. If the file cannot be + * successfully read, then the routine causes a fatal error. + * \param[in,out] vo the ordering class to use. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(particle_order &vo,const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(vo,fp); + fclose(fp); + } + void compute_all_cells(); + double sum_cell_volumes(); + /** Dumps particle IDs, positions and radii to a file. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } else { + voronoicell c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } + } + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx; + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \param[in] r the radius of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z,double r) { + int ijk; + if(put_locate_block(ijk,x,y,z)) { + double *pp=p[ijk]+4*co[ijk]++,tm=max_radius; + *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r; + if(r>max_radius) max_radius=r; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--;max_radius=tm; + return q; + } + return false; + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + private: + voro_compute vc; + friend class voro_compute; +}; + +} + +#endif diff --git a/src/voro++/container_prd.cpp b/src/voro++/container_prd.cpp new file mode 100644 index 00000000..c47c5c27 --- /dev/null +++ b/src/voro++/container_prd.cpp @@ -0,0 +1,768 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file container_prd.cpp + * \brief Function implementations for the container_periodic_base and + * related classes. */ + +#include "container_prd.hh" + +namespace voro { + +/** The class constructor sets up the geometry of container, initializing the + * minimum and maximum coordinates in each direction, and setting whether each + * direction is periodic or not. It divides the container into a rectangular + * grid of blocks, and allocates memory for each of these for storing particle + * positions and IDs. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] init_mem_ the initial memory allocation for each block. + * \param[in] ps_ the number of floating point entries to store for each + * particle. */ +container_periodic_base::container_periodic_base(double bx_,double bxy_,double by_, + double bxz_,double byz_,double bz_,int nx_,int ny_,int nz_,int init_mem_,int ps_) + : unitcell(bx_,bxy_,by_,bxz_,byz_,bz_), voro_base(nx_,ny_,nz_,bx_/nx_,by_/ny_,bz_/nz_), + ey(int(max_uv_y*ysp+1)), ez(int(max_uv_z*zsp+1)), wy(ny+ey), wz(nz+ez), + oy(ny+2*ey), oz(nz+2*ez), oxyz(nx*oy*oz), id(new int*[oxyz]), p(new double*[oxyz]), + co(new int[oxyz]), mem(new int[oxyz]), img(new char[oxyz]), init_mem(init_mem_), ps(ps_) { + int i,j,k,l; + + // Clear the global arrays + int *pp=co;while(pp=0;l--) if(mem[l]>0) { + delete [] p[l]; + delete [] id[l]; + } + delete [] img; + delete [] mem; + delete [] co; + delete [] id; + delete [] p; +} + +/** The class constructor sets up the geometry of container. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] init_mem_ the initial memory allocation for each block. */ +container_periodic::container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_) + : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,3), + vc(*this,2*nx_+1,2*ey+1,2*ez+1) {} + +/** The class constructor sets up the geometry of container. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] init_mem_ the initial memory allocation for each block. */ +container_periodic_poly::container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_) + : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,4), + vc(*this,2*nx_+1,2*ey+1,2*ez+1) {ppr=p;} + +/** Put a particle into the correct region of the container. + * \param[in] n the numerical ID of the inserted particle. + * \param[in] (x,y,z) the position vector of the inserted particle. */ +void container_periodic::put(int n,double x,double y,double z) { + int ijk; + put_locate_block(ijk,x,y,z); + id[ijk][co[ijk]]=n; + double *pp=p[ijk]+3*co[ijk]++; + *(pp++)=x;*(pp++)=y;*pp=z; +} + +/** Put a particle into the correct region of the container. + * \param[in] n the numerical ID of the inserted particle. + * \param[in] (x,y,z) the position vector of the inserted particle. + * \param[in] r the radius of the particle. */ +void container_periodic_poly::put(int n,double x,double y,double z,double r) { + int ijk; + put_locate_block(ijk,x,y,z); + id[ijk][co[ijk]]=n; + double *pp=p[ijk]+4*co[ijk]++; + *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r; + if(max_radius=nz) { + int ak=step_div(k,nz); + z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz; + } + + // Remap particle in the y direction if necessary + int j=step_int(y*ysp); + if(j<0||j>=ny) { + int aj=step_div(j,ny); + y-=aj*by;x-=aj*bxy;j-=aj*ny; + } + + // Remap particle in the x direction if necessary + ijk=step_int(x*xsp); + if(ijk<0||ijk>=nx) { + int ai=step_div(ijk,nx); + x-=ai*bx;ijk-=ai*nx; + } + + // Compute the block index and check memory allocation + j+=ey;k+=ez; + ijk+=nx*(j+oy*k); + if(co[ijk]==mem[ijk]) add_particle_memory(ijk); +} + +/** Takes a particle position vector and computes the region index into which + * it should be stored. If the container is periodic, then the routine also + * maps the particle position to ensure it is in the primary domain. If the + * container is not periodic, the routine bails out. + * \param[out] ijk the region index. + * \param[in,out] (x,y,z) the particle position, remapped into the primary + * domain if necessary. + * \param[out] (ai,aj,ak) the periodic image displacement that the particle is + * in, with (0,0,0) corresponding to the primary domain. + * \return True if the particle can be successfully placed into the container, + * false otherwise. */ +void container_periodic_base::put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak) { + + // Remap particle in the z direction if necessary + int k=step_int(z*zsp); + if(k<0||k>=nz) { + ak=step_div(k,nz); + z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz; + } else ak=0; + + // Remap particle in the y direction if necessary + int j=step_int(y*ysp); + if(j<0||j>=ny) { + aj=step_div(j,ny); + y-=aj*by;x-=aj*bxy;j-=aj*ny; + } else aj=0; + + // Remap particle in the x direction if necessary + ijk=step_int(x*xsp); + if(ijk<0||ijk>=nx) { + ai=step_div(ijk,nx); + x-=ai*bx;ijk-=ai*nx; + } else ai=0; + + // Compute the block index and check memory allocation + j+=ey;k+=ez; + ijk+=nx*(j+oy*k); + if(co[ijk]==mem[ijk]) add_particle_memory(ijk); +} + +/** Takes a position vector and remaps it into the primary domain. + * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in, + * with (0,0,0) corresponding to the primary domain. + * \param[out] (ci,cj,ck) the index of the block that the position vector is + * within, once it has been remapped. + * \param[in,out] (x,y,z) the position vector to consider, which is remapped + * into the primary domain during the routine. + * \param[out] ijk the block index that the vector is within. */ +inline void container_periodic_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) { + + // Remap particle in the z direction if necessary + ck=step_int(z*zsp); + if(ck<0||ck>=nz) { + ak=step_div(ck,nz); + z-=ak*bz;y-=ak*byz;x-=ak*bxz;ck-=ak*nz; + } else ak=0; + + // Remap particle in the y direction if necessary + cj=step_int(y*ysp); + if(cj<0||cj>=ny) { + aj=step_div(cj,ny); + y-=aj*by;x-=aj*bxy;cj-=aj*ny; + } else aj=0; + + // Remap particle in the x direction if necessary + ci=step_int(x*xsp); + if(ci<0||ci>=nx) { + ai=step_div(ci,nx); + x-=ai*bx;ci-=ai*nx; + } else ai=0; + + cj+=ey;ck+=ez; + ijk=ci+nx*(cj+oy*ck); +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. This is equivalent to finding the particle which is nearest to the + * vector. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. This may point to a particle in + * a periodic image of the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container_periodic::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // Remap the vector into the primary domain and then search for the + // Voronoi cell that it is within + remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk); + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx); + rx=p[w.ijk][3*w.l]+ak*bxz+aj*bxy+ai*bx; + ry=p[w.ijk][3*w.l+1]+ak*byz+aj*by; + rz=p[w.ijk][3*w.l+2]+ak*bz; + pid=id[w.ijk][w.l]; + return true; + } + return false; +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. Additional wall classes are not considered by this routine. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. If the container is periodic, + * this may point to a particle in a periodic image of + * the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container_periodic_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // Remap the vector into the primary domain and then search for the + // Voronoi cell that it is within + remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk); + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx); + rx=p[w.ijk][4*w.l]+ak*bxz+aj*bxy+ai*bx; + ry=p[w.ijk][4*w.l+1]+ak*byz+aj*by; + rz=p[w.ijk][4*w.l+2]+ak*bz; + pid=id[w.ijk][w.l]; + return true; + } + return false; +} + +/** Increase memory for a particular region. + * \param[in] i the index of the region to reallocate. */ +void container_periodic_base::add_particle_memory(int i) { + + // Handle the case when no memory has been allocated for this block + if(mem[i]==0) { + mem[i]=init_mem; + id[i]=new int[init_mem]; + p[i]=new double[ps*init_mem]; + return; + } + + // Otherwise, double the memory allocation for this block. Carry out a + // check on the memory allocation size, and print a status message if + // requested. + int l,nmem(mem[i]<<1); + if(nmem>max_particle_memory) + voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=3 + fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem); +#endif + + // Allocate new memory and copy in the contents of the old arrays + int *idp=new int[nmem]; + for(l=0;l0) { + + // Compute the block's bounds, adding in a small tolerance + mix=i*boxx-tolerance;max=mix+boxx+tolerance; + miy=(j-ey)*boxy-tolerance;may=miy+boxy+tolerance; + miz=(k-ez)*boxz-tolerance;maz=miz+boxz+tolerance; + + // Print entries for any particles that lie outside the block's + // bounds + for(pp=p[l],c=0;cmax||pp[1]may||pp[2]maz) + printf("%d %d %d %d %f %f %f %f %f %f %f %f %f\n", + id[l][c],i,j,k,*pp,pp[1],pp[2],mix,max,miy,may,miz,maz); + } +} + +/** Creates particles within an image block that is aligned with the primary + * domain in the z axis. In this case, the image block may be comprised of + * particles from two primary blocks. The routine considers these two primary + * blocks, and adds the needed particles to the image. The remaining particles + * from the primary blocks are also filled into the neighboring images. + * \param[in] (di,dj,dk) the index of the block to consider. The z index must + * satisfy ez<=dk0) { + odijk=dijk-1;adis=dis; + } else { + odijk=dijk+nx-1;adis=dis+bx; + } + img[odijk]|=2; + for(l=0;lswitchx) put_image(dijk,fijk,l,dis,by*ima,0); + else put_image(odijk,fijk,l,adis,by*ima,0); + } + } + + // Right image computation + if((img[dijk]&2)==0) { + if(fi==nx-1) { + fijk+=1-nx;switchx+=(1-nx)*boxx;dis+=bx; + } else { + fijk++;switchx+=boxx; + } + if(di==nx-1) { + odijk=dijk-nx+1;adis=dis-bx; + } else { + odijk=dijk+1;adis=dis; + } + img[odijk]|=1; + for(l=0;l=wz. */ +void container_periodic_base::create_vertical_image(int di,int dj,int dk) { + int l,dijk=di+nx*(dj+oy*dk),dijkl,dijkr,ima=step_div(dk-ez,nz); + int qj=dj+step_int(-ima*byz*ysp),qjdiv=step_div(qj-ey,ny); + int qi=di+step_int((-ima*bxz-qjdiv*bxy)*xsp),qidiv=step_div(qi,nx); + int fi=qi-qidiv*nx,fj=qj-qjdiv*ny,fijk=fi+nx*(fj+oy*(dk-ima*nz)),fijk2; + double disy=ima*byz+qjdiv*by,switchy=(dj-ey)*boxy-ima*byz-qjdiv*by; + double disx=ima*bxz+qjdiv*bxy+qidiv*bx,switchx=di*boxx-ima*bxz-qjdiv*bxy-qidiv*bx; + double switchx2,disxl,disxr,disx2,disxr2; + + if(di==0) {dijkl=dijk+nx-1;disxl=disx+bx;} + else {dijkl=dijk-1;disxl=disx;} + + if(di==nx-1) {dijkr=dijk-nx+1;disxr=disx-bx;} + else {dijkr=dijk+1;disxr=disx;} + + // Down-left image computation + bool y_exist=dj!=0; + if((img[dijk]&1)==0) { + img[dijkl]|=2; + if(y_exist) { + img[dijkl-nx]|=8; + img[dijk-nx]|=4; + } + for(l=0;lswitchy) { + if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima); + else put_image(dijkl,fijk,l,disxl,disy,bz*ima); + } else { + if(!y_exist) continue; + if(p[fijk][ps*l]>switchx) put_image(dijk-nx,fijk,l,disx,disy,bz*ima); + else put_image(dijkl-nx,fijk,l,disxl,disy,bz*ima); + } + } + } + + // Down-right image computation + if((img[dijk]&2)==0) { + if(fi==nx-1) { + fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx; + } else { + fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr; + } + img[dijkr]|=1; + if(y_exist) { + img[dijkr-nx]|=4; + img[dijk-nx]|=8; + } + for(l=0;lswitchy) { + if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk,fijk2,l,disx2,disy,bz*ima); + } else { + if(!y_exist) continue; + if(p[fijk2][ps*l]>switchx2) put_image(dijkr-nx,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk-nx,fijk2,l,disx2,disy,bz*ima); + } + } + } + + // Recomputation of some intermediate quantities for boundary cases + if(fj==wy-1) { + fijk+=nx*(1-ny)-fi; + switchy+=(1-ny)*boxy; + disy+=by; + qi=di+step_int(-(ima*bxz+(qjdiv+1)*bxy)*xsp); + int dqidiv=step_div(qi,nx)-qidiv;qidiv+=dqidiv; + fi=qi-qidiv*nx; + fijk+=fi; + disx+=bxy+bx*dqidiv; + disxl+=bxy+bx*dqidiv; + disxr+=bxy+bx*dqidiv; + switchx-=bxy+bx*dqidiv; + } else { + fijk+=nx;switchy+=boxy; + } + + // Up-left image computation + y_exist=dj!=oy-1; + if((img[dijk]&4)==0) { + img[dijkl]|=8; + if(y_exist) { + img[dijkl+nx]|=2; + img[dijk+nx]|=1; + } + for(l=0;lswitchy) { + if(!y_exist) continue; + if(p[fijk][ps*l]>switchx) put_image(dijk+nx,fijk,l,disx,disy,bz*ima); + else put_image(dijkl+nx,fijk,l,disxl,disy,bz*ima); + } else { + if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima); + else put_image(dijkl,fijk,l,disxl,disy,bz*ima); + } + } + } + + // Up-right image computation + if((img[dijk]&8)==0) { + if(fi==nx-1) { + fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx; + } else { + fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr; + } + img[dijkr]|=4; + if(y_exist) { + img[dijkr+nx]|=1; + img[dijk+nx]|=2; + } + for(l=0;lswitchy) { + if(!y_exist) continue; + if(p[fijk2][ps*l]>switchx2) put_image(dijkr+nx,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk+nx,fijk2,l,disx2,disy,bz*ima); + } else { + if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk,fijk2,l,disx2,disy,bz*ima); + } + } + } + + // All contributions to the block now added, so set all four bits of + // the image information + img[dijk]=15; +} + +/** Copies a particle position from the primary domain into an image block. + * \param[in] reg the block index within the primary domain that the particle + * is within. + * \param[in] fijk the index of the image block. + * \param[in] l the index of the particle entry within the primary block. + * \param[in] (dx,dy,dz) the displacement vector to add to the particle. */ +void container_periodic_base::put_image(int reg,int fijk,int l,double dx,double dy,double dz) { + if(co[reg]==mem[reg]) add_particle_memory(reg); + double *p1=p[reg]+ps*co[reg],*p2=p[fijk]+ps*l; + *(p1++)=*(p2++)+dx; + *(p1++)=*(p2++)+dy; + *p1=*p2+dz; + if(ps==4) *(++p1)=*(++p2); + id[reg][co[reg]++]=id[fijk][l]; +} + +} diff --git a/src/voro++/container_prd.hh b/src/voro++/container_prd.hh new file mode 100644 index 00000000..ec698728 --- /dev/null +++ b/src/voro++/container_prd.hh @@ -0,0 +1,654 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file container_prd.hh + * \brief Header file for the container_periodic_base and related classes. */ + +#ifndef VOROPP_CONTAINER_PRD_HH +#define VOROPP_CONTAINER_PRD_HH + +#include +#include + +#include "config.hh" +#include "common.hh" +#include "v_base.hh" +#include "cell.hh" +#include "c_loops.hh" +#include "v_compute.hh" +#include "unitcell.hh" +#include "rad_option.hh" + +namespace voro { + +/** \brief Class for representing a particle system in a 3D periodic + * non-orthogonal periodic domain. + * + * This class represents a particle system in a three-dimensional + * non-orthogonal periodic domain. The domain is defined by three periodicity + * vectors (bx,0,0), (bxy,by,0), and (bxz,byz,bz) that represent a + * parallelepiped. Internally, the class stores particles in the box 0 + inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck,int &i,int &j,int &k,double &x,double &y,double &z,int &disp) { + c=unit_voro; + double *pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + i=nx;j=ey;k=ez; + return true; + } + /** Initializes parameters for a find_voronoi_cell call within + * the voro_compute template. + * \param[in] (ci,cj,ck) the coordinates of the test block in + * the container coordinate system. + * \param[in] ijk the index of the test block + * \param[out] (i,j,k) the coordinates of the test block + * relative to the voro_compute + * coordinate system. + * \param[out] disp a block displacement used internally by the + * find_voronoi_cell routine (but not needed + * in this instance.) */ + inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) { + i=nx;j=ey;k=ez; + } + /** Returns the position of a particle currently being computed + * relative to the computational block that it is within. It is + * used to select the optimal worklist entry to use. + * \param[in] (x,y,z) the position of the particle. + * \param[in] (ci,cj,ck) the block that the particle is within. + * \param[out] (fx,fy,fz) the position relative to the block. + */ + inline void frac_pos(double x,double y,double z,double ci,double cj,double ck,double &fx,double &fy,double &fz) { + fx=x-boxx*ci; + fy=y-boxy*(cj-ey); + fz=z-boxz*(ck-ez); + } + /** Calculates the index of block in the container structure + * corresponding to given coordinates. + * \param[in] (ci,cj,ck) the coordinates of the original block + * in the current computation, relative + * to the container coordinate system. + * \param[in] (ei,ej,ek) the displacement of the current block + * from the original block. + * \param[in,out] (qx,qy,qz) the periodic displacement that + * must be added to the particles + * within the computed block. + * \param[in] disp a block displacement used internally by the + * find_voronoi_cell and compute_cell routines + * (but not needed in this instance.) + * \return The block index. */ + inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) { + int qi=ci+(ei-nx),qj=cj+(ej-ey),qk=ck+(ek-ez); + int iv(step_div(qi,nx));if(iv!=0) {qx=iv*bx;qi-=nx*iv;} else qx=0; + create_periodic_image(qi,qj,qk); + return qi+nx*(qj+oy*qk); + } + void create_all_images(); + void check_compartmentalized(); + protected: + void add_particle_memory(int i); + void put_locate_block(int &ijk,double &x,double &y,double &z); + void put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak); + /** Creates particles within an image block by copying them + * from the primary domain and shifting them. If the given + * block is aligned with the primary domain in the z-direction, + * the routine calls the simpler create_side_image routine + * where the image block may comprise of particles from up to + * two primary blocks. Otherwise is calls the more complex + * create_vertical_image where the image block may comprise of + * particles from up to four primary blocks. + * \param[in] (di,dj,dk) the coordinates of the image block to + * create. */ + inline void create_periodic_image(int di,int dj,int dk) { + if(di<0||di>=nx||dj<0||dj>=oy||dk<0||dk>=oz) + voro_fatal_error("Constructing periodic image for nonexistent point",VOROPP_INTERNAL_ERROR); + if(dk>=ez&&dk=wy) create_side_image(di,dj,dk); + } else create_vertical_image(di,dj,dk); + } + void create_side_image(int di,int dj,int dk); + void create_vertical_image(int di,int dj,int dk); + void put_image(int reg,int fijk,int l,double dx,double dy,double dz); + inline void remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk); +}; + +/** \brief Extension of the container_periodic_base class for computing regular + * Voronoi tessellations. + * + * This class is an extension of the container_periodic_base that has routines + * specifically for computing the regular Voronoi tessellation with no + * dependence on particle radii. */ +class container_periodic : public container_periodic_base, public radius_mono { + public: + container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_); + void clear(); + void put(int n,double x,double y,double z); + void put(int n,double x,double y,double z,int &ai,int &aj,int &ak); + void put(particle_order &vo,int n,double x,double y,double z); + void import(FILE *fp=stdin); + void import(particle_order &vo,FILE *fp=stdin); + /** Imports a list of particles from an open file stream into + * the container. Entries of four numbers (Particle ID, x + * position, y position, z position) are searched for. If the + * file cannot be successfully read, then the routine causes a + * fatal error. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + /** Imports a list of particles from an open file stream into + * the container. Entries of four numbers (Particle ID, x + * position, y position, z position) are searched for. In + * addition, the order in which particles are read is saved + * into an ordering class. If the file cannot be successfully + * read, then the routine causes a fatal error. + * \param[in,out] vo the ordering class to use. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(particle_order &vo,const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(vo,fp); + fclose(fp); + } + void compute_all_cells(); + double sum_cell_volumes(); + /** Dumps particle IDs and positions to a file. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } else { + voronoicell c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx); + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z) { + int ijk; + put_locate_block(ijk,x,y,z); + double *pp=p[ijk]+3*co[ijk]++; + *(pp++)=x;*(pp++)=y;*(pp++)=z; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--; + return q; + } + private: + voro_compute vc; + friend class voro_compute; +}; + +/** \brief Extension of the container_periodic_base class for computing radical + * Voronoi tessellations. + * + * This class is an extension of container_periodic_base that has routines + * specifically for computing the radical Voronoi tessellation that depends + * on the particle radii. */ +class container_periodic_poly : public container_periodic_base, public radius_poly { + public: + container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_); + void clear(); + void put(int n,double x,double y,double z,double r); + void put(int n,double x,double y,double z,double r,int &ai,int &aj,int &ak); + void put(particle_order &vo,int n,double x,double y,double z,double r); + void import(FILE *fp=stdin); + void import(particle_order &vo,FILE *fp=stdin); + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. If the file cannot be successfully read, then the + * routine causes a fatal error. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. In addition, the order in which particles are read is + * saved into an ordering class. If the file cannot be + * successfully read, then the routine causes a fatal error. + * \param[in,out] vo the ordering class to use. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(particle_order &vo,const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(vo,fp); + fclose(fp); + } + void compute_all_cells(); + double sum_cell_volumes(); + /** Dumps particle IDs, positions and radii to a file. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } else { + voronoicell c; + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } + } + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx); + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \param[in] r the radius of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z,double r) { + int ijk; + put_locate_block(ijk,x,y,z); + double *pp=p[ijk]+4*co[ijk]++,tm=max_radius; + *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r; + if(r>max_radius) max_radius=r; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--;max_radius=tm; + return q; + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + private: + voro_compute vc; + friend class voro_compute; +}; + +} + +#endif diff --git a/src/voro++/makefile b/src/voro++/makefile new file mode 100644 index 00000000..fcbe2c49 --- /dev/null +++ b/src/voro++/makefile @@ -0,0 +1,21 @@ +#-------------------------------------------------------------- +# Makefile for voro++ module +#-------------------------------------------------------------- + +# List module object filenames +voro++_objects =\ +cell.o \ +common.o \ +container.o \ +unitcell.o \ +v_compute.o \ +c_loops.o \ +v_base.o \ +wall.o \ +pre_container.o \ +container_prd.o + +include src/voro++/Makefile.dep + +# Append module objects to global tree +OBJECTS+=$(addprefix obj/voro++/,$(voro++_objects)) diff --git a/src/voro++/pre_container.cpp b/src/voro++/pre_container.cpp new file mode 100644 index 00000000..00dba272 --- /dev/null +++ b/src/voro++/pre_container.cpp @@ -0,0 +1,236 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file pre_container.cpp + * \brief Function implementations for the pre_container and related classes. + */ + +#include + +#include "config.hh" +#include "pre_container.hh" + +namespace voro { + +/** The class constructor sets up the geometry of container, initializing the + * minimum and maximum coordinates in each direction. It allocates an initial + * chunk into which to store particle information. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the + * container is periodic in each + * coordinate direction. + * \param[in] ps_ the number of floating point entries to store for each + * particle. */ +pre_container_base::pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_) : + ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_), + xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_), ps(ps_), + index_sz(init_chunk_size), pre_id(new int*[index_sz]), end_id(pre_id), + pre_p(new double*[index_sz]), end_p(pre_p) { + ch_id=*end_id=new int[pre_container_chunk_size]; + l_id=end_id+index_sz;e_id=ch_id+pre_container_chunk_size; + ch_p=*end_p=new double[ps*pre_container_chunk_size]; +} + +/** The destructor frees the dynamically allocated memory. */ +pre_container_base::~pre_container_base() { + delete [] *end_p; + delete [] *end_id; + while (end_id!=pre_id) { + end_p--; + delete [] *end_p; + end_id--; + delete [] *end_id; + } + delete [] pre_p; + delete [] pre_id; +} + +/** Makes a guess at the optimal grid of blocks to use, computing in + * a way that + * \param[out] (nx,ny,nz) the number of blocks to use. */ +void pre_container_base::guess_optimal(int &nx,int &ny,int &nz) { + double dx=bx-ax,dy=by-ay,dz=bz-az; + double ilscale=pow(total_particles()/(optimal_particles*dx*dy*dz),1/3.0); + nx=int(dx*ilscale+1); + ny=int(dy*ilscale+1); + nz=int(dz*ilscale+1); +} + +/** Stores a particle ID and position, allocating a new memory chunk if + * necessary. For coordinate directions in which the container is not periodic, + * the routine checks to make sure that the particle is within the container + * bounds. If the particle is out of bounds, it is not stored. + * \param[in] n the numerical ID of the inserted particle. + * \param[in] (x,y,z) the position vector of the inserted particle. */ +void pre_container::put(int n,double x,double y,double z) { + if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) { + if(ch_id==e_id) new_chunk(); + *(ch_id++)=n; + *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z; + } +#if VOROPP_REPORT_OUT_OF_BOUNDS ==1 + else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z); +#endif +} + +/** Stores a particle ID and position, allocating a new memory chunk if necessary. + * \param[in] n the numerical ID of the inserted particle. + * \param[in] (x,y,z) the position vector of the inserted particle. + * \param[in] r the radius of the particle. */ +void pre_container_poly::put(int n,double x,double y,double z,double r) { + if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) { + if(ch_id==e_id) new_chunk(); + *(ch_id++)=n; + *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z;*(ch_p++)=r; + } +#if VOROPP_REPORT_OUT_OF_BOUNDS ==1 + else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z); +#endif +} + +/** Transfers the particles stored within the class to a container class. + * \param[in] con the container class to transfer to. */ +void pre_container::setup(container &con) { + int **c_id=pre_id,*idp,*ide,n; + double **c_p=pre_p,*pp,x,y,z; + while(c_idmax_chunk_size) + voro_fatal_error("Absolute memory limit on chunk index reached",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Pre-container chunk index scaled up to %d\n",index_sz); +#endif + int **n_id=new int*[index_sz],**p_id=n_id,**c_id=pre_id; + double **n_p=new double*[index_sz],**p_p=n_p,**c_p=pre_p; + while(c_id + +#include "c_loops.hh" +#include "container.hh" + +namespace voro { + +/** \brief A class for storing an arbitrary number of particles, prior to setting + * up a container geometry. + * + * The pre_container_base class can dynamically import and store an arbitrary + * number of particles. Once the particles have been read in, an appropriate + * container class can be set up with the optimal grid size, and the particles + * can be transferred. + * + * The pre_container_base class is not intended for direct use, but forms the + * base of the pre_container and pre_container_poly classes, that add routines + * depending on whether particle radii need to be tracked or not. */ +class pre_container_base { + public: + /** The minimum x coordinate of the container. */ + const double ax; + /** The maximum x coordinate of the container. */ + const double bx; + /** The minimum y coordinate of the container. */ + const double ay; + /** The maximum y coordinate of the container. */ + const double by; + /** The minimum z coordinate of the container. */ + const double az; + /** The maximum z coordinate of the container. */ + const double bz; + /** A boolean value that determines if the x coordinate in + * periodic or not. */ + const bool xperiodic; + /** A boolean value that determines if the y coordinate in + * periodic or not. */ + const bool yperiodic; + /** A boolean value that determines if the z coordinate in + * periodic or not. */ + const bool zperiodic; + void guess_optimal(int &nx,int &ny,int &nz); + pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_); + ~pre_container_base(); + /** Calculates and returns the total number of particles stored + * within the class. + * \return The number of particles. */ + inline int total_particles() { + return (end_id-pre_id)*pre_container_chunk_size+(ch_id-*end_id); + } + protected: + /** The number of doubles associated with a single particle + * (three for the standard container, four when radius + * information is stored). */ + const int ps; + void new_chunk(); + void extend_chunk_index(); + /** The size of the chunk index. */ + int index_sz; + /** A pointer to the chunk index to store the integer particle + * IDs. */ + int **pre_id; + /** A pointer to the last allocated integer ID chunk. */ + int **end_id; + /** A pointer to the end of the integer ID chunk index, used to + * determine when the chunk index is full. */ + int **l_id; + /** A pointer to the next available slot on the current + * particle ID chunk. */ + int *ch_id; + /** A pointer to the end of the current integer chunk. */ + int *e_id; + /** A pointer to the chunk index to store the floating point + * information associated with particles. */ + double **pre_p; + /** A pointer to the last allocated chunk of floating point + * information. */ + double **end_p; + /** A pointer to the next available slot on the current + * floating point chunk. */ + double *ch_p; +}; + +/** \brief A class for storing an arbitrary number of particles without radius + * information, prior to setting up a container geometry. + * + * The pre_container class is an extension of the pre_container_base class for + * cases when no particle radius information is available. */ +class pre_container : public pre_container_base { + public: + /** The class constructor sets up the geometry of container, + * initializing the minimum and maximum coordinates in each + * direction. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the + * container is periodic in + * each coordinate direction. */ + pre_container(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + bool xperiodic_,bool yperiodic_,bool zperiodic_) + : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,3) {}; + void put(int n,double x,double y,double z); + void import(FILE *fp=stdin); + /** Imports particles from a file. + * \param[in] filename the name of the file to read from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + void setup(container &con); + void setup(particle_order &vo,container &con); +}; + +/** \brief A class for storing an arbitrary number of particles with radius + * information, prior to setting up a container geometry. + * + * The pre_container_poly class is an extension of the pre_container_base class + * for cases when particle radius information is available. */ +class pre_container_poly : public pre_container_base { + public: + /** The class constructor sets up the geometry of container, + * initializing the minimum and maximum coordinates in each + * direction. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the + * container is periodic in + * each coordinate direction. */ + pre_container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + bool xperiodic_,bool yperiodic_,bool zperiodic_) + : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,4) {}; + void put(int n,double x,double y,double z,double r); + void import(FILE *fp=stdin); + /** Imports particles from a file. + * \param[in] filename the name of the file to read from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + void setup(container_poly &con); + void setup(particle_order &vo,container_poly &con); +}; + +} + +#endif diff --git a/src/voro++/rad_option.hh b/src/voro++/rad_option.hh new file mode 100644 index 00000000..d946fa4b --- /dev/null +++ b/src/voro++/rad_option.hh @@ -0,0 +1,158 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file rad_option.hh + * \brief Header file for the classes encapsulating functionality for the + * regular and radical Voronoi tessellations. */ + +#ifndef VOROPP_RAD_OPTION_HH +#define VOROPP_RAD_OPTION_HH + +#include + +namespace voro { + +/** \brief Class containing all of the routines that are specific to computing + * the regular Voronoi tessellation. + * + * The container and container_periodic classes are derived from this class, + * and during the Voronoi cell computation, these routines are used to create + * the regular Voronoi tessellation. */ +class radius_mono { + protected: + /** This is called prior to computing a Voronoi cell for a + * given particle to initialize any required constants. + * \param[in] ijk the block that the particle is within. + * \param[in] s the index of the particle within the block. */ + inline void r_init(int ijk,int s) {} + /** Sets a required constant to be used when carrying out a + * plane bounds check. */ + inline void r_prime(double rv) {} + /** Carries out a radius bounds check. + * \param[in] crs the radius squared to be tested. + * \param[in] mrs the current maximum distance to a Voronoi + * vertex multiplied by two. + * \return True if particles at this radius could not possibly + * cut the cell, false otherwise. */ + inline bool r_ctest(double crs,double mrs) {return crs>mrs;} + /** Scales a plane displacement during a plane bounds check. + * \param[in] lrs the plane displacement. + * \return The scaled value. */ + inline double r_cutoff(double lrs) {return lrs;} + /** Adds the maximum radius squared to a given value. + * \param[in] rs the value to consider. + * \return The value with the radius squared added. */ + inline double r_max_add(double rs) {return rs;} + /** Subtracts the radius squared of a particle from a given + * value. + * \param[in] rs the value to consider. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The value with the radius squared subtracted. */ + inline double r_current_sub(double rs,int ijk,int q) {return rs;} + /** Scales a plane displacement prior to use in the plane cutting + * algorithm. + * \param[in] rs the initial plane displacement. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The scaled plane displacement. */ + inline double r_scale(double rs,int ijk,int q) {return rs;} + /** Scales a plane displacement prior to use in the plane + * cutting algorithm, and also checks if it could possibly cut + * the cell. + * \param[in,out] rs the plane displacement to be scaled. + * \param[in] mrs the current maximum distance to a Voronoi + * vertex multiplied by two. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell could possibly cut the cell, false + * otherwise. */ + inline bool r_scale_check(double &rs,double mrs,int ijk,int q) {return rssqrt(mrs*crs);} + /** Scales a plane displacement during a plane bounds check. + * \param[in] lrs the plane displacement. + * \return The scaled value. */ + inline double r_cutoff(double lrs) {return lrs*r_val;} + /** Adds the maximum radius squared to a given value. + * \param[in] rs the value to consider. + * \return The value with the radius squared added. */ + inline double r_max_add(double rs) {return rs+max_radius*max_radius;} + /** Subtracts the radius squared of a particle from a given + * value. + * \param[in] rs the value to consider. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The value with the radius squared subtracted. */ + inline double r_current_sub(double rs,int ijk,int q) { + return rs-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; + } + /** Scales a plane displacement prior to use in the plane cutting + * algorithm. + * \param[in] rs the initial plane displacement. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The scaled plane displacement. */ + inline double r_scale(double rs,int ijk,int q) { + return rs+r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; + } + /** Scales a plane displacement prior to use in the plane + * cutting algorithm, and also checks if it could possibly cut + * the cell. + * \param[in,out] rs the plane displacement to be scaled. + * \param[in] mrs the current maximum distance to a Voronoi + * vertex multiplied by two. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell could possibly cut the cell, false + * otherwise. */ + inline bool r_scale_check(double &rs,double mrs,int ijk,int q) { + double trs=rs; + rs+=r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; + return rs +#include + +#include "unitcell.hh" +#include "cell.hh" + +namespace voro { + +/** Initializes the unit cell class for a particular non-orthogonal periodic + * geometry, corresponding to a parallelepiped with sides given by three + * vectors. The class constructs the unit Voronoi cell corresponding to this + * geometry. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. */ +unitcell::unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_) + : bx(bx_), bxy(bxy_), by(by_), bxz(bxz_), byz(byz_), bz(bz_) { + int i,j,l=1; + + // Initialize the Voronoi cell to be a very large rectangular box + const double ucx=max_unit_voro_shells*bx,ucy=max_unit_voro_shells*by,ucz=max_unit_voro_shells*bz; + unit_voro.init(-ucx,ucx,-ucy,ucy,-ucz,ucz); + + // Repeatedly cut the cell by shells of periodic image particles + while(l<2*max_unit_voro_shells) { + + // Check to see if any of the planes from the current shell + // will cut the cell + if(unit_voro_intersect(l)) { + + // If they do, apply the plane cuts from the current + // shell + unit_voro_apply(l,0,0); + for(i=1;il can't cut + // a cell lying within the paraboloid + // z<=(l*l-x*x-y*y)/(2*l). It is always a tighter bound + // than the one based on computing the maximum radius + // of a Voronoi cell vertex. + max_uv_y=max_uv_z=0; + double y,z,q,*pts=unit_voro.pts,*pp=pts; + while(ppmax_uv_y) max_uv_y=y+q; + if(z+q>max_uv_z) max_uv_z=z+q; + } + max_uv_z*=0.5; + max_uv_y*=0.5; + return; + } + l++; + } + + // If the routine makes it here, then the unit cell still hasn't been + // completely bounded by the plane cuts. Give the memory error code, + // because this is mainly a case of hitting a safe limit, than any + // inherent problem. + voro_fatal_error("Periodic cell computation failed",VOROPP_MEMORY_ERROR); +} + +/** Applies a pair of opposing plane cuts from a periodic image point + * to the unit Voronoi cell. + * \param[in] (i,j,k) the index of the periodic image to consider. */ +inline void unitcell::unit_voro_apply(int i,int j,int k) { + double x=i*bx+j*bxy+k*bxz,y=j*by+k*byz,z=k*bz; + unit_voro.plane(x,y,z); + unit_voro.plane(-x,-y,-z); +} + +/** Calculates whether the unit Voronoi cell intersects a given periodic image + * of the domain. + * \param[in] (dx,dy,dz) the displacement of the periodic image. + * \param[out] vol the proportion of the unit cell volume within this image, + * only computed in the case that the two intersect. + * \return True if they intersect, false otherwise. */ +bool unitcell::intersects_image(double dx,double dy,double dz,double &vol) { + const double bxinv=1/bx,byinv=1/by,bzinv=1/bz,ivol=bxinv*byinv*bzinv; + voronoicell c; + c=unit_voro; + dx*=2;dy*=2;dz*=2; + if(!c.plane(0,0,bzinv,dz+1)) return false; + if(!c.plane(0,0,-bzinv,-dz+1)) return false; + if(!c.plane(0,byinv,-byz*byinv*bzinv,dy+1)) return false; + if(!c.plane(0,-byinv,byz*byinv*bzinv,-dy+1)) return false; + if(!c.plane(bxinv,-bxy*bxinv*byinv,(bxy*byz-by*bxz)*ivol,dx+1)) return false; + if(!c.plane(-bxinv,bxy*bxinv*byinv,(-bxy*byz+by*bxz)*ivol,-dx+1)) return false; + vol=c.volume()*ivol; + return true; +} + +/** Computes a list of periodic domain images that intersect the unit Voronoi cell. + * \param[out] vi a vector containing triplets (i,j,k) corresponding to domain + * images that intersect the unit Voronoi cell, when it is + * centered in the middle of the primary domain. + * \param[out] vd a vector containing the fraction of the Voronoi cell volume + * within each corresponding image listed in vi. */ +void unitcell::images(std::vector &vi,std::vector &vd) { + const int ms2=max_unit_voro_shells*2+1,mss=ms2*ms2*ms2; + bool *a=new bool[mss],*ac=a+max_unit_voro_shells*(1+ms2*(1+ms2)),*ap=a; + int i,j,k; + double vol; + + // Initialize mask + while(ap q; + q.push(0);q.push(0);q.push(0); + + while(!q.empty()) { + + // Read the next entry on the queue + i=q.front();q.pop(); + j=q.front();q.pop(); + k=q.front();q.pop(); + + // Check intersection of this image + if(intersects_image(i,j,k,vol)) { + + // Add this entry to the output vectors + vi.push_back(i); + vi.push_back(j); + vi.push_back(k); + vd.push_back(vol); + + // Add neighbors to the queue if they have not been + // tested + ap=ac+i+ms2*(j+ms2*k); + if(k>-max_unit_voro_shells&&*(ap-ms2*ms2)) {q.push(i);q.push(j);q.push(k-1);*(ap-ms2*ms2)=false;} + if(j>-max_unit_voro_shells&&*(ap-ms2)) {q.push(i);q.push(j-1);q.push(k);*(ap-ms2)=false;} + if(i>-max_unit_voro_shells&&*(ap-1)) {q.push(i-1);q.push(j);q.push(k);*(ap-1)=false;} + if(i,<%g,0,0>,rr}\n" + "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz); + fprintf(fp,"cylinder{<0,0,0>,<%g,%g,0>,rr}\n" + "cylinder{<%g,0,0>,<%g,%g,0>,rr}\n",bxy,by,bx,bx+bxy,by); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxz,byz,bz,bx+bxy+bxz,by+byz,bz); + fprintf(fp,"cylinder{<0,0,0>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,0,0>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx,bx+bxz,byz,bz); + fprintf(fp,"cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n",bxy,by,bxy+bxz,by+byz,bz,bx+bxy,by,bx+bxy+bxz,by+byz,bz); + fprintf(fp,"sphere{<0,0,0>,rr}\nsphere{<%g,0,0>,rr}\n" + "sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by); + fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" + "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz); +} + +} diff --git a/src/voro++/unitcell.hh b/src/voro++/unitcell.hh new file mode 100644 index 00000000..9cee93c1 --- /dev/null +++ b/src/voro++/unitcell.hh @@ -0,0 +1,79 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file unitcell.hh + * \brief Header file for the unitcell class. */ + +#ifndef VOROPP_UNITCELL_HH +#define VOROPP_UNITCELL_HH + +#include + +#include "config.hh" +#include "cell.hh" + +namespace voro { + +/** \brief Class for computation of the unit Voronoi cell associated with + * a 3D non-rectangular periodic domain. */ +class unitcell { + public: + /** The x coordinate of the first vector defining the periodic + * domain. */ + const double bx; + /** The x coordinate of the second vector defining the periodic + * domain. */ + const double bxy; + /** The y coordinate of the second vector defining the periodic + * domain. */ + const double by; + /** The x coordinate of the third vector defining the periodic + * domain. */ + const double bxz; + /** The y coordinate of the third vector defining the periodic + * domain. */ + const double byz; + /** The z coordinate of the third vector defining the periodic + * domain. */ + const double bz; + /** The computed unit Voronoi cell corresponding the given + * 3D non-rectangular periodic domain geometry. */ + voronoicell unit_voro; + unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_gnuplot(const char* filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_domain_gnuplot(fp); + fclose(fp); + } + void draw_domain_gnuplot(FILE *fp=stdout); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_pov(const char* filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_domain_pov(fp); + fclose(fp); + } + void draw_domain_pov(FILE *fp=stdout); + bool intersects_image(double dx,double dy,double dz,double &vol); + void images(std::vector &vi,std::vector &vd); + protected: + /** The maximum y-coordinate that could possibly cut the + * computed unit Voronoi cell. */ + double max_uv_y; + /** The maximum z-coordinate that could possibly cut the + * computed unit Voronoi cell. */ + double max_uv_z; + private: + inline void unit_voro_apply(int i,int j,int k); + bool unit_voro_intersect(int l); + inline bool unit_voro_test(int i,int j,int k); +}; + +} + +#endif diff --git a/src/voro++/v_base.cpp b/src/voro++/v_base.cpp new file mode 100644 index 00000000..90df442d --- /dev/null +++ b/src/voro++/v_base.cpp @@ -0,0 +1,118 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_base.cpp + * \brief Function implementations for the base Voronoi container class. */ + +#include "v_base.hh" +#include "config.hh" + +namespace voro { + +/** This function is called during container construction. The routine scans + * all of the worklists in the wl[] array. For a given worklist of blocks + * labeled \f$w_1\f$ to \f$w_n\f$, it computes a sequence \f$r_0\f$ to + * \f$r_n\f$ so that $r_i$ is the minimum distance to all the blocks + * \f$w_{j}\f$ where \f$j>i\f$ and all blocks outside the worklist. The values + * of \f$r_n\f$ is calculated first, as the minimum distance to any block in + * the shell surrounding the worklist. The \f$r_i\f$ are then computed in + * reverse order by considering the distance to \f$w_{i+1}\f$. */ +voro_base::voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_) : + nx(nx_), ny(ny_), nz(nz_), nxy(nx_*ny_), nxyz(nxy*nz_), boxx(boxx_), boxy(boxy_), boxz(boxz_), + xsp(1/boxx_), ysp(1/boxy_), zsp(1/boxz_), mrad(new double[wl_hgridcu*wl_seq_length]) { + const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28; + const double xstep=boxx/wl_fgrid,ystep=boxy/wl_fgrid,zstep=boxz/wl_fgrid; + int i,j,k,lx,ly,lz,q; + unsigned int f,*e=const_cast (wl); + double xlo,ylo,zlo,xhi,yhi,zhi,minr,*radp=mrad; + for(zlo=0,zhi=zstep,lz=0;lz>7&127)-64; + k=(f>>14&127)-64; + if((f&b2)==b2) { + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i-1,j,k); + if((f&b1)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k); + } else if((f&b1)==b1) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k); + if((f&b4)==b4) { + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j-1,k); + if((f&b3)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k); + } else if((f&b3)==b3) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k); + if((f&b6)==b6) { + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k-1); + if((f&b5)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1); + } else if((f&b5)==b5) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1); + } + q--; + while(q>0) { + radp[q]=minr; + f=e[q]; + i=(f&127)-64; + j=(f>>7&127)-64; + k=(f>>14&127)-64; + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k); + q--; + } + *radp=minr; + e+=wl_seq_length; + radp+=wl_seq_length; + } + } + } +} + +/** Computes the minimum distance from a subregion to a given block. If this distance + * is smaller than the value of minr, then it passes + * \param[in,out] minr a pointer to the current minimum distance. If the distance + * computed in this function is smaller, then this distance is + * set to the new one. + * \param[out] (xlo,ylo,zlo) the lower coordinates of the subregion being + * considered. + * \param[out] (xhi,yhi,zhi) the upper coordinates of the subregion being + * considered. + * \param[in] (ti,tj,tk) the coordinates of the block. */ +void voro_base::compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk) { + double radsq,temp; + if(ti>0) {temp=boxx*ti-xhi;radsq=temp*temp;} + else if(ti<0) {temp=xlo-boxx*(1+ti);radsq=temp*temp;} + else radsq=0; + + if(tj>0) {temp=boxy*tj-yhi;radsq+=temp*temp;} + else if(tj<0) {temp=ylo-boxy*(1+tj);radsq+=temp*temp;} + + if(tk>0) {temp=boxz*tk-zhi;radsq+=temp*temp;} + else if(tk<0) {temp=zlo-boxz*(1+tk);radsq+=temp*temp;} + + if(radsq(format)); + + // Check to see if "%n" appears in the format sequence + while(*fmp!=0) { + if(*fmp=='%') { + fmp++; + if(*fmp=='n') return true; + else if(*fmp==0) return false; + } + fmp++; + } + + return false; +} + +#include "v_base_wl.cpp" + +} diff --git a/src/voro++/v_base.hh b/src/voro++/v_base.hh new file mode 100644 index 00000000..0436a75d --- /dev/null +++ b/src/voro++/v_base.hh @@ -0,0 +1,88 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_base.hh + * \brief Header file for the base Voronoi container class. */ + +#ifndef VOROPP_V_BASE_HH +#define VOROPP_V_BASE_HH + +#include "worklist.hh" + +namespace voro { + +/** \brief Class containing data structures common across all particle container classes. + * + * This class contains constants and data structures that are common across all + * particle container classes. It contains constants setting the size of the + * underlying subgrid of blocks that forms the basis of the Voronoi cell + * computations. It also constructs bound tables that are used in the Voronoi + * cell computation, and contains a number of routines that are common across + * all container classes. */ +class voro_base { + public: + /** The number of blocks in the x direction. */ + const int nx; + /** The number of blocks in the y direction. */ + const int ny; + /** The number of blocks in the z direction. */ + const int nz; + /** A constant, set to the value of nx multiplied by ny, which + * is used in the routines that step through blocks in + * sequence. */ + const int nxy; + /** A constant, set to the value of nx*ny*nz, which is used in + * the routines that step through blocks in sequence. */ + const int nxyz; + /** The size of a computational block in the x direction. */ + const double boxx; + /** The size of a computational block in the y direction. */ + const double boxy; + /** The size of a computational block in the z direction. */ + const double boxz; + /** The inverse box length in the x direction. */ + const double xsp; + /** The inverse box length in the y direction. */ + const double ysp; + /** The inverse box length in the z direction. */ + const double zsp; + /** An array to hold the minimum distances associated with the + * worklists. This array is initialized during container + * construction, by the initialize_radii() routine. */ + double *mrad; + /** The pre-computed block worklists. */ + static const unsigned int wl[wl_seq_length*wl_hgridcu]; + bool contains_neighbor(const char* format); + voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_); + ~voro_base() {delete [] mrad;} + protected: + /** A custom int function that returns consistent stepping + * for negative numbers, so that (-1.5, -0.5, 0.5, 1.5) maps + * to (-2,-1,0,1). + * \param[in] a the number to consider. + * \return The value of the custom int operation. */ + inline int step_int(double a) {return a<0?int(a)-1:int(a);} + /** A custom modulo function that returns consistent stepping + * for negative numbers. For example, (-2,-1,0,1,2) step_mod 2 + * is (0,1,0,1,0). + * \param[in] (a,b) the input integers. + * \return The value of a modulo b, consistent for negative + * numbers. */ + inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;} + /** A custom integer division function that returns consistent + * stepping for negative numbers. For example, (-2,-1,0,1,2) + * step_div 2 is (-1,-1,0,0,1). + * \param[in] (a,b) the input integers. + * \return The value of a div b, consistent for negative + * numbers. */ + inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;} + private: + void compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk); +}; + +} + +#endif diff --git a/src/voro++/v_base_wl.cpp b/src/voro++/v_base_wl.cpp new file mode 100644 index 00000000..7e6a2246 --- /dev/null +++ b/src/voro++/v_base_wl.cpp @@ -0,0 +1,79 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_base_wl.cpp + * \brief The table of block worklists that are used during the cell + * computation, which is part of the voro_base class. + * + * This file is automatically generated by worklist_gen.pl and it is not + * intended to be edited by hand. */ + +const unsigned int voro_base::wl[wl_seq_length*wl_hgridcu]={ + 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x10fe0bf,0x11020bf,0x11020c0,0x10fe0c0,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x3101f3f,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x12fe0c1,0x13020c1,0x91060c0,0x91060bf,0x8306041,0x8305fc1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x190fa0c0,0x190fa0bf,0x16fe0be,0x17020be,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3701f3e,0x36fdf3e,0x186f9fbe,0x186fa03e,0x1b0f9f3f,0x1b0f9f40,0x93060c1,0x192fa0c1,0x97060be,0xb305f41,0x1b2f9f41,0x196fa0be,0xb705f3e,0x1b6f9f3e, + 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0xfdfc1,0x101fc1,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x8106040,0x8105fc0,0x8105fbf,0x810603f,0x11020bf,0x10fe0bf,0x180fa040,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x93060c1,0xb305f41,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x9302042,0x192fe042, + 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x91060c0,0x91060bf,0xb105f40,0xb105f3f,0x190fa0c0,0x190fa0bf,0x93060c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0x192fa0c1,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0x9302042,0xb301fc2,0x1b6f9fbe,0x196fa03e, + 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x10fe0c1,0x11020c1,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x190fa0bf,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b6f9fbe,0x196fa03e, + 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0xfe0bf,0x1020bf,0x1020c0,0xfe0c0,0x2fe041,0x302041,0x8106040,0x810603f,0x8105fbf,0x8105fc0,0x301fc1,0x2fdfc1,0x180fa040,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x9302140,0x192fe140, + 9,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0xfdfc1,0x101fc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x10fe0c1,0x11020c1,0x70203e,0x6fe03e,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8106041,0x91060c0,0x91060bf,0x8305fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0x180f9fc1,0x30fdf41,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x1b6fdf3e,0xb701f3e,0x97060be,0xb305f41,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0xa302042, + 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x11020c1,0x10fe0c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x30fdf40,0x3101f40,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x91060c0,0x91060bf,0x180fa041,0x180f9fc1,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x190fa0c0,0x190fa0bf,0x30fdf41,0x3101f41,0x93060c1,0x192fa0c1,0xb105f40,0xb105f3f,0x17020be,0x16fe0be,0x970603e,0x8705fbe,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x1b6fdf3e,0xb701f3e, + 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x11020c1,0x10fe0c1,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x3101f40,0x180fa03f,0x180f9fbf,0x30fdf40,0x180fa041,0x180f9fc1,0x91060c0,0x3101f3f,0x30fdf3f,0x30fdf41,0x3101f41,0x91060bf,0x190fa0c0,0x91060c1,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x190fa0c1,0x182fe042,0xb105f40,0xb105f3f,0x302042,0x3301fc2,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0xb105f41,0x17020be,0x196fe0be,0x970603e,0xb705fbe,0x1b2f9f41,0x192fe0c2,0x13020c2,0x9306042,0xb305fc2, + 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8306041,0x8305fc1,0x870603e,0x8705fbe,0x182fa041,0x182f9fc1,0x93060c1,0x186fa03e,0x186f9fbe,0x97060be,0x192fa0c1,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x196fa0be,0x196fe13f,0x192fe140,0x9302140,0x970213f,0x1b6f9f3f,0x1b2f9f40, + 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x3020c1,0x2fe0c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x6fe03e,0x70203e,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x8306041,0x8305fc1,0x180fa0c0,0x180fa0bf,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x182fa041,0x182f9fc1,0x6fe0be,0x7020be,0x93060c1,0x192fa0c1,0x870603e,0x8705fbe,0x3301f41,0x32fdf41,0xb305f40,0xb105f3f,0x186fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x1b6fdf3e,0xb701f3e, + 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x8106040,0x1020c1,0xfe0c1,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180fa0bf,0x6fe03e,0x70203e,0x3101f40,0x30fdf40,0x180f9fc1,0x30fdf3f,0x3101f3f,0x3701fbe,0x36fdfbe,0x93060c1,0x192fa0c1,0x6fe0be,0x7020be,0x3101f41,0x30fdf41,0xb305f40,0xb105f3f,0x970603e,0xb705fbe,0x196fa03e,0x186f9fbe,0x1b2f9f40,0x1b6f9f3f,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x192fe140,0x9302140,0x970213f,0x196fe13f, + 15,0xfe040,0xfdfc0,0x101fc0,0x10203f,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x1020c1,0xfe0c1,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x81060c0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x180fa0c0,0x81060bf,0x91060c1,0x3101f40,0x30fdf40,0x30fdf3f,0x180fa0bf,0x190fa0c1,0x3101f3f,0x3101f41,0x30fdf41,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x8302042,0x182fe042,0x1b2fdfc2,0xb301fc2,0xb105f40,0xb705f3f,0xb305f41,0x1b2f9f40,0x1b6f9f3f,0x192fe140,0x9302140,0x93020c2,0x192fe0c2,0x196fe13f,0x970213f,0xa70603e, + 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x2fe0c1,0x3020c1,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x182f9fc1,0x192fa0c1,0x186fa03e,0x186f9fbe,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x1b6f9f3f,0x1b2f9f40, + 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x3020c1,0x2fe0c1,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x70203e,0x180f9fc0,0x180f9fbf,0x6fe03e,0x180fa0c0,0x180fa0bf,0x8306041,0x701fbe,0x6fdfbe,0x6fe0be,0x7020be,0x8305fc1,0x182fa041,0x83060c1,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x870603e,0x8705fbe,0x1102140,0x170213f,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x87060be,0x3301f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x196fa0be,0x192fe141,0x1302141,0x9306140,0x970613f, + 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x8106041,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x180fa041,0x8105fc1,0x83060c1,0x70203e,0x6fe03e,0x6fdfbe,0x180f9fc1,0x182fa0c1,0x701fbe,0x7020be,0x6fe0be,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x1b0fdf41,0x3101f41,0x9102140,0x190fe140,0x196fe13f,0x970213f,0x870603e,0xb705fbe,0x97060be,0x196fa03e,0x1b6f9fbe,0x192fe042,0x9302042,0x9302141,0x192fe141,0x1b2fdfc2,0xb301fc2,0xb505f40, + 17,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfe041,0x102041,0x1020c0,0xfe0c0,0xfdfbf,0x101fbf,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x180fa040,0x180fa03f,0x180f9fc0,0x8105fbf,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180f9fbf,0x81060c1,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x186fe03e,0x70203e,0x3101f40,0x1b0fdf40,0x1b0fdf3f,0x3101f3f,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x9102140,0x190fe140,0x182fe042,0x8302042,0x3101f41,0x1b0fdf41,0x1b2fdfc2,0xb301fc2,0x83020c2,0x182fe0c2,0x196fe13f,0x970213f,0x9302141,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe, + 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x105fbf,0x10603f,0x106040,0x105fc0,0x301fc1,0x302041,0x11020c0,0x11020bf,0x10fe0bf,0x10fe0c0,0x2fe041,0x2fdfc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0xb305f41,0xb705f3e,0xb709fbf,0x970a03f,0x930a040,0xb309fc0, + 9,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0xfdfc1,0xfe041,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x8105fc1,0x8106041,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x11020c1,0x91060c0,0x91060bf,0x12fe0c1,0x3101f41,0xb105f40,0xb105f3f,0x30fdf41,0x180f9fc1,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0xb305f41,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x97060be,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x11302042, + 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x8106041,0x8105fc1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x180f9fc0,0x180fa040,0x11020c1,0x10fe0c1,0x180fa03f,0x180f9fbf,0x91060c0,0x91060bf,0x3101f41,0x30fdf41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0xb105f40,0xb105f3f,0x180f9fc1,0x180fa041,0x93060c1,0xb305f41,0x190fa0c0,0x190fa0bf,0x870603e,0x8705fbe,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x1b2f9f41,0x1b6f9fbe,0x196fa03e, + 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x8106041,0x8105fc1,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x180fa040,0x3101f3f,0x30fdf3f,0x180f9fc0,0x3101f41,0x30fdf41,0x91060c0,0x180fa03f,0x180f9fbf,0x180f9fc1,0x180fa041,0x91060bf,0xb105f40,0x91060c1,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0xb105f41,0x3301fc2,0x190fa0c0,0x190fa0bf,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0x190fa0c1,0x870603e,0xb705fbe,0x97020be,0x196fe0be,0x1b2f9f41,0xb305fc2,0x8306042,0x93020c2,0x192fe0c2, + 9,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0xfe0bf,0xfe0c0,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x81060bf,0x81060c0,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3020c1,0x8306041,0x8305fc1,0x12fe0c1,0x7020be,0x870603e,0x8705fbe,0x6fe0be,0x180fa0bf,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x97060be,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0xb305f41,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x11302140, + 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x302041,0x1020c0,0x8106040,0x810603f,0x1020bf,0xfe0c0,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0xfe0bf,0x2fdfc1,0x3020c1,0x8306041,0x81060c0,0x81060bf,0x2fe0c1,0x8305fc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x30fdf40,0x30fdf3f,0x6fdfbe,0x180f9fbf,0x180fa0c0,0x182fa041,0x93060c1,0x870603e,0x7020be,0x6fe0be,0x180fa0bf,0x182f9fc1,0x32fdf41,0x3301f41,0xb105f40,0xb105f3f,0x8705fbe,0x97060be,0x192fa0c1,0xb305f41,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x1b6f9fbe,0x196fa03e,0x196fe13f,0x9302140,0x970213f,0x192fe140, + 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x93060c1,0x3101f41,0x30fdf41,0x180f9fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x17020be,0x16fe0be,0x192fa0c1,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x97060be,0xb701f3e,0x1b6fdf3e, + 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x3101f41,0x91060c1,0x180fa041,0x180f9fc1,0x30fdf41,0xb105f40,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x190fa0c0,0x190fa0bf,0x190fa0c1,0xb105f3f,0xb105f41,0x3301fc2,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x17020be,0x970603e,0xb705fbe,0x196fe0be,0x1b6f9f3f,0x1b2f9f41,0x13020c2,0x9306042,0xb305fc2,0x192fe0c2, + 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x81060c0,0x81060bf,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x180fa03f,0x180fa040,0x3020c1,0x2fe0c1,0x180f9fc0,0x180f9fbf,0x8306041,0x8305fc1,0x7020be,0x6fe0be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x870603e,0x8705fbe,0x180fa0bf,0x180fa0c0,0x93060c1,0x97060be,0x182fa041,0x182f9fc1,0xb105f40,0xb105f3f,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x196fe13f,0x196fa0be,0x1b6f9f3f,0x1b2f9f40, + 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x93060c1,0x7020be,0x6fe0be,0x180fa0bf,0x180fa0c0,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3301f41,0x32fdf41,0x192fa0c1,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0xb305f41,0xb701f3e,0x1b6fdf3e, + 15,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x101fbf,0xfdfbf,0x102041,0x1020c0,0xfe0c0,0xfe041,0x101fc1,0xfdfc1,0x1020bf,0xfe0bf,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x1020c1,0xfe0c1,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x93060c1,0x70203e,0x6fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x6fdfbe,0x1b6fdf3f,0x180fa041,0x180fa0c0,0x180fa0bf,0x180f9fc1,0x1b0fdf41,0x3101f41,0x7020be,0x6fe0be,0x870603e,0x8705fbe,0xb105f40,0xb705f3f,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x97060be,0x9302042,0x192fe042,0xb301fc2,0xb305f41,0x1b2fdfc2,0x196fe13f,0x196fa03e,0x1b6f9fbe, + 14,0xfe040,0x101fc0,0xfdfc0,0x10203f,0xfe03f,0x101fbf,0xfdfbf,0x102041,0xfe041,0x101fc1,0xfdfc1,0x1020c0,0xfe0c0,0x1020bf,0x8106040,0xfe0bf,0x8105fc0,0x1020c1,0xfe0c1,0x810603f,0x8105fbf,0x8106041,0x8105fc1,0x81060c0,0x81060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x1b0fdf40,0x3101f40,0x3101f3f,0x1b0fdf3f,0x180fa0c0,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x3101f41,0x1b0fdf41,0x1b6fdfbe,0x190fa0c1,0x182fe042,0x302042,0xb105f40,0xb105f3f,0x3301fc2,0x1b2fdfc2,0x7020be,0x196fe0be,0x970603e,0xb705fbe,0xb105f41,0x9302140,0x192fe140,0x13020c2,0x192fe0c2,0x9306042,0xb305fc2,0x1170213f, + 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x81060c0,0x81060bf,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x180fa040,0x701fbe,0x6fdfbe,0x180fa03f,0x7020be,0x6fe0be,0x8306041,0x180f9fc0,0x180f9fbf,0x180fa0bf,0x180fa0c0,0x8305fc1,0x870603e,0x83060c1,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x87060be,0x170213f,0x182fa041,0x182f9fc1,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x182fa0c1,0xb105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x196fa0be,0x970613f,0x9106140,0x9302141,0x192fe141, + 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x7020be,0x83060c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x870603e,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x182fa041,0x182f9fc1,0x182fa0c1,0x8705fbe,0x87060be,0x170213f,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x3301f41,0xb305f40,0xb705f3f,0x1b2fdf41,0x1b6f9fbe,0x196fa0be,0x1302141,0x9306140,0x970613f,0x192fe141, + 14,0xfe040,0x10203f,0xfe03f,0x101fc0,0xfdfc0,0x101fbf,0xfdfbf,0x1020c0,0xfe0c0,0x1020bf,0xfe0bf,0x102041,0xfe041,0x101fc1,0x8106040,0xfdfc1,0x810603f,0x1020c1,0xfe0c1,0x8105fc0,0x8105fbf,0x81060c0,0x81060bf,0x8106041,0x8105fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x186fe03e,0x70203e,0x701fbe,0x186fdfbe,0x180fa041,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x7020be,0x186fe0be,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x1102140,0x870603e,0x8705fbe,0x170213f,0x196fe13f,0x3101f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x87060be,0x9302042,0x192fe042,0x1302141,0x192fe141,0x9306140,0x970613f,0x13301fc2, + 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0xfe041,0xfe0c0,0x101fbf,0xfdfbf,0x1020bf,0xfe0bf,0x101fc1,0xfdfc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x1b0fdf41,0x8302042,0x182fe042,0x9102140,0x7020be,0x186fe0be,0x190fe140,0x970213f,0x196fe13f,0x9102141,0xb301fc2,0x1b2fdfc2,0x93020c2,0x182fe0c2,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe, + 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x13020c1,0x12fe0c1,0x17020be,0x16fe0be,0x3301f41,0x32fdf41,0x93060c1,0x3701f3e,0x36fdf3e,0x97060be,0xb305f41,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0xb705f3e,0xb709fbf,0xb309fc0,0x930a040,0x970a03f,0x1b6f9f3f,0x1b2f9f40, + 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x306041,0x305fc1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x701fbe,0x70203e,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x13020c1,0x12fe0c1,0x3105f40,0x3105f3f,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3301f41,0x32fdf41,0x705fbe,0x70603e,0x93060c1,0xb305f41,0x17020be,0x16fe0be,0x182fa041,0x182f9fc1,0x192fa0c0,0x190fa0bf,0x3701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x1b6f9fbe,0x196fa03e, + 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x105fc0,0x106040,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x11020c0,0x106041,0x105fc1,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x3105f3f,0x701fbe,0x70203e,0x180fa040,0x180f9fc0,0x30fdf41,0x180f9fbf,0x180fa03f,0x186fe03e,0x186fdfbe,0x93060c1,0xb305f41,0x705fbe,0x70603e,0x180fa041,0x180f9fc1,0x192fa0c0,0x190fa0bf,0x97020be,0x196fe0be,0xb701f3e,0x36fdf3e,0x1b2f9f40,0x1b6f9f3f,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0xb309fc0,0x930a040,0x970a03f,0xb709fbf, + 15,0x101fc0,0xfdfc0,0xfe040,0x10203f,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106040,0x105fc0,0x105fbf,0x10603f,0x106041,0x105fc1,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x11060c0,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x3105f40,0x11060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180f9fbf,0x3105f3f,0xb105f41,0x180fa03f,0x180fa041,0x180f9fc1,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x1302042,0x3301fc2,0x1b2fdfc2,0x192fe042,0x190fa0c0,0x196fa0bf,0x192fa0c1,0x1b2f9f40,0x1b6f9f3f,0xb309fc0,0x930a040,0x9306042,0xb305fc2,0xb709fbf,0x970a03f,0x117020be, + 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x11060c0,0x11060bf,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3101f3f,0x3101f40,0x306041,0x305fc1,0x30fdf40,0x30fdf3f,0x13020c1,0x12fe0c1,0x70603e,0x705fbe,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x17020be,0x16fe0be,0x3105f3f,0x3105f40,0x93060c1,0x97060be,0x3301f41,0x32fdf41,0x190fa0c0,0x190fa0bf,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0xb709fbf,0xb705f3e,0x1b6f9f3f,0x1b2f9f40, + 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x93060c1,0x70603e,0x705fbe,0x3105f3f,0x3105f40,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x182fa041,0x182f9fc1,0xb305f41,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x192fa0c1,0x196fa03e,0x1b6f9fbe, + 15,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x106040,0x105fc0,0x101fc1,0xfe041,0xfdfc1,0x10603f,0x105fbf,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x106041,0x105fc1,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x93060c1,0x70203e,0x701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x6fdfbe,0x1b6f9fbf,0x3101f41,0x3105f40,0x3105f3f,0x30fdf41,0x1b0f9fc1,0x180fa041,0x70603e,0x705fbe,0x17020be,0x16fe0be,0x190fa0c0,0x196fa0bf,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0x97060be,0x9302042,0xb301fc2,0x192fe042,0x192fa0c1,0x1b2fdfc2,0xb709fbf,0xb701f3e,0x1b6fdf3e, + 14,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x106040,0x105fc0,0x10603f,0x11020c0,0x105fbf,0x10fe0c0,0x106041,0x105fc1,0x11020bf,0x10fe0bf,0x11020c1,0x10fe0c1,0x11060c0,0x11060bf,0x91060c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x1b0f9fc0,0x180fa040,0x180fa03f,0x1b0f9fbf,0x3105f40,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x180fa041,0x1b0f9fc1,0x1b6fdfbe,0xb105f41,0x3301fc2,0x302042,0x190fa0c0,0x190fa0bf,0x182fe042,0x1b2fdfc2,0x70603e,0xb705fbe,0x97020be,0x196fe0be,0x190fa0c1,0x930a040,0xb309fc0,0x8306042,0xb305fc2,0x93020c2,0x192fe0c2,0xa70a03f, + 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x10603f,0x106040,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x302041,0x1060c0,0x1060bf,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x705fbe,0x3101f3f,0x3101f40,0x180fa040,0x180fa03f,0x6fe0be,0x180f9fbf,0x180f9fc0,0x1b0fdf40,0x1b0fdf3f,0x93060c1,0x97060be,0x3105f3f,0x3105f40,0x180fa0c0,0x180fa0bf,0x192fa041,0x182f9fc1,0xb301f41,0x1b2fdf41,0xb701f3e,0x36fdf3e,0x196fa03e,0x1b6f9fbe,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x970a03f,0x930a040,0xb309fc0,0xb709fbf, + 15,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe0bf,0x105fc0,0x105fbf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x1060c0,0x1060bf,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x93060c1,0x3101f40,0x3101f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x30fdf3f,0x1b6f9fbf,0x7020be,0x70603e,0x705fbe,0x6fe0be,0x186fa0bf,0x180fa0c0,0x3105f40,0x3105f3f,0x3301f41,0x32fdf41,0x182fa041,0x1b2f9fc1,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb305f41,0x9302140,0x970213f,0x192fe140,0x192fa0c1,0x196fe13f,0xb709fbf,0xb701f3e,0x1b6fdf3e, + 16,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x102041,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfe0bf,0xfdfc1,0x1020c1,0x106041,0x1060c0,0x1060bf,0xfe0c1,0x105fc1,0x93060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180f9fbf,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x3105f40,0xb105f3f,0x70603e,0x7020be,0x6fe0be,0x180fa0c0,0x180fa041,0x182f9fc1,0x1b2fdf41,0xb705fbe,0x186fa0bf,0x192fa0c1,0x97060be,0xb305f41,0x9302042,0x192fe042,0x13301fc2,0x930a040,0x970a03f,0xb509fc0,0x9302140,0x970213f,0x192fe140,0x196fe13f, + 15,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0x102041,0x101fc1,0xfdfbf,0xfe041,0x1020c0,0x106040,0xfdfc1,0x105fc0,0xfe0c0,0x1020bf,0x10603f,0x105fbf,0xfe0bf,0x1020c1,0x106041,0x105fc1,0xfe0c1,0x1060c0,0x11060bf,0x91060c1,0x3101f40,0x180fa040,0x180f9fc0,0x1b0fdf40,0x3101f3f,0x30fdf3f,0x180fa03f,0x1b0f9fbf,0x180fa041,0x180f9fc1,0x3101f41,0x1b0fdf41,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x3105f40,0xb105f3f,0x180fa0c0,0x190fa0bf,0x192fa0c1,0x302042,0x3301fc2,0xb305f41,0x182fe042,0x1b2fdfc2,0x17020be,0x170603e,0xb705fbe,0x196fe0be,0x9502140,0x194fe140,0x193020c2,0xa306042,0x930a040,0xb309fc0,0xa70a03f, + 15,0x10203f,0xfe03f,0xfe040,0x101fc0,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1060c0,0x1060bf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x306041,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x70603e,0x305fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fbf,0x705fbe,0x87060be,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x1302140,0x170213f,0x196fe13f,0x192fe140,0x182fa041,0x1b2f9fc1,0x192fa0c1,0x196fa03e,0x1b6f9fbe,0x970a03f,0x930a040,0x9306140,0x970613f,0xb709fbf,0xb309fc0,0x13301f41, + 14,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x106040,0x10603f,0x105fc0,0x302041,0x105fbf,0x2fe041,0x1060c0,0x1060bf,0x301fc1,0x2fdfc1,0x3020c1,0x2fe0c1,0x306041,0x305fc1,0x83060c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x186fa03f,0x180fa040,0x180f9fc0,0x186f9fbf,0x70603e,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x180fa0c0,0x186fa0bf,0x1b6fdf3f,0x87060be,0x170213f,0x1102140,0x182fa041,0x182f9fc1,0x190fe140,0x196fe13f,0x3105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x182fa0c1,0x930a040,0x970a03f,0x9106140,0x970613f,0x9302141,0x192fe141,0xb509fc0, + 15,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0x1020c0,0x1020bf,0xfdfbf,0xfe0c0,0x102041,0x106040,0xfe0bf,0x10603f,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfdfc1,0x1020c1,0x1060c0,0x1060bf,0xfe0c1,0x106041,0x305fc1,0x83060c1,0x70203e,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x6fdfbe,0x180f9fc0,0x186f9fbf,0x180fa0c0,0x180fa0bf,0x7020be,0x186fe0be,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x70603e,0x8705fbe,0x180fa041,0x182f9fc1,0x192fa0c1,0x1102140,0x170213f,0x97060be,0x190fe140,0x196fe13f,0x3301f41,0x3305f40,0xb705f3f,0x1b2fdf41,0xa302042,0x1a2fe042,0x19302141,0x9506140,0x930a040,0x970a03f,0xb509fc0, + 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0x101fbf,0xfe041,0xfe0c0,0x106040,0xfdfbf,0x1020bf,0x101fc1,0xfdfc1,0xfe0bf,0x1020c1,0x10603f,0x105fc0,0xfe0c1,0x1060c0,0x106041,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x180f9fc1,0x180fa0bf,0x701fbe,0x3701f3f,0x1b0fdf3f,0x1b6fdfbe,0x7020be,0x186fe0be,0x192fa0c1,0x9102140,0x190fe140,0x8302042,0x3101f41,0x1b0fdf41,0x182fe042,0xb301fc2,0xb305f40,0x870603e,0x970213f,0x196fe13f,0x11102141,0x113020c2,0x1b2fdfc2,0xb105f3f,0xb705fbe,0x97060be,0xa50a040, + 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x305fc1,0x306041,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x180fa040,0x180fa03f,0x180f9fbf,0x180f9fc0,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x32fdf41,0xb305f41,0x3701f3e,0x36fdf3e,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0x1b6f9f3f,0x1b2f9f40, + 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x306041,0x305fc1,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x70203e,0x30fdf40,0x30fdf3f,0x701fbe,0x3105f40,0x3105f3f,0x13020c1,0x6fe03e,0x6fdfbe,0x705fbe,0x70603e,0x12fe0c1,0x3301f41,0x13060c1,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x3305f41,0xb109fc0,0x17020be,0x16fe0be,0x810a040,0x870a03f,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x17060be,0x182fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0xb705f3e,0xb309fc1,0x830a041,0x930a0c0,0x970a0bf, + 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x106040,0x105fc0,0x105fbf,0x10603f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x11020c1,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3101f41,0x10fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fdfbe,0x30fdf41,0x3305f41,0x6fe03e,0x70603e,0x705fbe,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x1b0f9fc1,0x180fa041,0x910a040,0xb109fc0,0xb709fbf,0x970a03f,0x17020be,0x196fe0be,0x97060be,0xb701f3e,0x1b6fdf3e,0xb301fc2,0x9302042,0x930a041,0xb309fc1,0x1b2fdfc2,0x192fe042,0x194fa0c0, + 17,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0x101fc1,0x102041,0x106040,0x105fc0,0xfdfbf,0xfe03f,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x3101f40,0x3101f3f,0x30fdf40,0x10fe0bf,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x30fdf3f,0x11060c1,0x3105f3f,0x30fdf41,0x3105f41,0x3701fbe,0x70203e,0x180fa040,0x1b0f9fc0,0x1b0f9fbf,0x180fa03f,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x910a040,0xb109fc0,0x3301fc2,0x1302042,0x180fa041,0x1b0f9fc1,0x1b2fdfc2,0x192fe042,0x1306042,0x3305fc2,0xb709fbf,0x970a03f,0x930a041,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be, + 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x11060c0,0x11060bf,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3101f40,0x6fe03e,0x6fdfbe,0x3101f3f,0x70603e,0x705fbe,0x13020c1,0x30fdf40,0x30fdf3f,0x3105f3f,0x3105f40,0x12fe0c1,0x17020be,0x13060c1,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x17060be,0x870a03f,0x3301f41,0x32fdf41,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x3305f41,0x190fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0xb705f3e,0x970a0bf,0x910a0c0,0x930a041,0xb309fc1, + 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x70603e,0x13060c1,0x3105f40,0x3105f3f,0x705fbe,0x17020be,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x3301f41,0x32fdf41,0x3305f41,0x16fe0be,0x17060be,0x870a03f,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x182fa041,0x192fa0c0,0x196fa0bf,0x1b2f9fc1,0x1b6fdf3e,0xb705f3e,0x830a041,0x930a0c0,0x970a0bf,0xb309fc1, + 14,0x101fc0,0x10203f,0x101fbf,0xfe040,0xfdfc0,0xfe03f,0xfdfbf,0x106040,0x105fc0,0x10603f,0x105fbf,0x102041,0x101fc1,0xfe041,0x11020c0,0xfdfc1,0x11020bf,0x106041,0x105fc1,0x10fe0c0,0x10fe0bf,0x11060c0,0x11060bf,0x11020c1,0x10fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3701fbe,0x70203e,0x6fe03e,0x36fdfbe,0x3101f41,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x70603e,0x3705fbe,0x1b6f9fbf,0x3305f41,0xb109fc0,0x810a040,0x17020be,0x16fe0be,0x870a03f,0xb709fbf,0x180fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0x17060be,0x9302042,0xb301fc2,0x830a041,0xb309fc1,0x930a0c0,0x970a0bf,0x1a2fe042, + 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0x101fc1,0x105fc0,0xfe03f,0xfdfbf,0x10603f,0x105fbf,0xfe041,0xfdfc1,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x3105f3f,0x30fdf41,0x3105f41,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x36fdfbe,0x1b6f9fbf,0x180fa041,0x1b0f9fc1,0x1302042,0x3301fc2,0x910a040,0x70603e,0x3705fbe,0xb109fc0,0x970a03f,0xb709fbf,0x910a041,0x192fe042,0x1b2fdfc2,0x9306042,0x3305fc2,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be, + 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3020c1,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x7020be,0x2fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf3f,0x6fe0be,0x17060be,0x30fdf40,0x3105f40,0x3105f3f,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x186fa0bf,0x180fa0c0,0x830a040,0x870a03f,0xb709fbf,0xb309fc0,0x3301f41,0x1b2fdf41,0xb305f41,0xb701f3e,0x1b6fdf3e,0x970213f,0x9302140,0x930a0c0,0x970a0bf,0x196fe13f,0x192fe140,0x1a2fa041, + 14,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x1020c0,0x1020bf,0xfe0c0,0x302041,0xfe0bf,0x301fc1,0x1060c0,0x1060bf,0x2fe041,0x2fdfc1,0x306041,0x305fc1,0x3020c1,0x2fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x3701f3f,0x3101f40,0x30fdf40,0x36fdf3f,0x7020be,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x3105f40,0x3705f3f,0x1b6f9fbf,0x17060be,0x870a03f,0x810a040,0x3301f41,0x32fdf41,0xb109fc0,0xb709fbf,0x180fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0x3305f41,0x9302140,0x970213f,0x910a0c0,0x970a0bf,0x930a041,0xb309fc1,0x194fe140, + 15,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0x106040,0x10603f,0xfdfbf,0x105fc0,0x102041,0x1020c0,0x105fbf,0x1020bf,0x101fc1,0xfe041,0xfe0c0,0xfe0bf,0xfdfc1,0x106041,0x1060c0,0x1060bf,0x105fc1,0x1020c1,0x2fe0c1,0x13060c1,0x70203e,0x3101f40,0x3101f3f,0x3701fbe,0x6fe03e,0x6fdfbe,0x30fdf40,0x36fdf3f,0x3105f40,0x3105f3f,0x70603e,0x3705fbe,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x7020be,0x16fe0be,0x3101f41,0x32fdf41,0xb305f41,0x810a040,0x870a03f,0x97060be,0xb109fc0,0xb709fbf,0x182fa041,0x182fa0c0,0x196fa0bf,0x1b2f9fc1,0x11302042,0x13301fc2,0xb30a041,0x950a0c0,0x9302140,0x970213f,0x194fe140, + 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0xfe03f,0x101fc1,0x105fc0,0x1020c0,0xfdfbf,0x10603f,0xfe041,0xfdfc1,0x105fbf,0x106041,0x1020bf,0xfe0c0,0x105fc1,0x1060c0,0x1020c1,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x30fdf41,0x3105f3f,0x6fe03e,0x186fa03f,0x1b0f9fbf,0x1b6fdfbe,0x70603e,0x3705fbe,0xb305f41,0x910a040,0xb109fc0,0x1302042,0x180fa041,0x1b0f9fc1,0x3301fc2,0x192fe042,0x192fa0c0,0x17020be,0x970a03f,0xb709fbf,0xa10a041,0xa306042,0x1b2fdfc2,0x190fa0bf,0x196fe0be,0x97060be,0x11502140, + 17,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0x1020bf,0x1020c0,0x106040,0x10603f,0xfdfbf,0xfdfc0,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x70203e,0x701fbe,0x6fe03e,0x2fdfc1,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x6fdfbe,0x3060c1,0x705fbe,0x6fe0be,0x7060be,0x3701f3f,0x3101f40,0x180fa040,0x186fa03f,0x186f9fbf,0x180f9fc0,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x830a040,0x870a03f,0x170213f,0x1302140,0x180fa0c0,0x186fa0bf,0x196fe13f,0x192fe140,0x1306140,0x170613f,0xb709fbf,0xb309fc0,0x930a0c0,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41, + 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0x1020bf,0x10603f,0xfdfc0,0xfdfbf,0x105fc0,0x105fbf,0xfe0c0,0xfe0bf,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x705fbe,0x6fe0be,0x7060be,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x36fdf3f,0x1b6f9fbf,0x180fa0c0,0x186fa0bf,0x1302140,0x170213f,0x830a040,0x3105f40,0x3705f3f,0x870a03f,0xb309fc0,0xb709fbf,0x830a0c0,0x192fe140,0x196fe13f,0x9306140,0x170613f,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41, + 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0xfdfc0,0x1020bf,0x10603f,0x102041,0xfdfbf,0x105fc0,0xfe0c0,0xfe0bf,0x105fbf,0x1060c0,0x101fc1,0xfe041,0x1060bf,0x106041,0x1020c1,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x6fe0be,0x705fbe,0x30fdf40,0x1b0f9fc0,0x186f9fbf,0x1b6fdf3f,0x3105f40,0x3705f3f,0x97060be,0x830a040,0x870a03f,0x1302140,0x180fa0c0,0x186fa0bf,0x170213f,0x192fe140,0x192fa041,0x3301f41,0xb309fc0,0xb709fbf,0x850a0c0,0x9506140,0x196fe13f,0x182f9fc1,0x1b2fdf41,0xb305f41,0x12302042, + 16,0x10203f,0x101fc0,0xfe040,0x102041,0x1020c0,0x106040,0x101fbf,0xfe03f,0xfdfc0,0x101fc1,0x105fc0,0x10603f,0x1020bf,0xfe0c0,0xfe041,0xfdfbf,0x1020c1,0x106041,0x1060c0,0x105fbf,0xfe0bf,0xfdfc1,0x105fc1,0x1060bf,0xfe0c1,0x83060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180fa041,0x180fa0c0,0x7020be,0x70603e,0x3105f40,0xb101f41,0x9302042,0x1102140,0x930a040,0x6fdfbe,0x36fdf3f,0x1b6f9fbf,0x190fa0bf,0x196fe0be,0x170213f,0x196fe140,0x182f9fc1,0x1b2fdf41,0xb301fc2,0x1a2fe042,0x192fa0c1,0x8705fbe,0xb705f3f,0xb309fc0,0xa70a03f,0x97060be,0x9706140,0x11302141 +}; diff --git a/src/voro++/v_compute.cpp b/src/voro++/v_compute.cpp new file mode 100644 index 00000000..2b15388d --- /dev/null +++ b/src/voro++/v_compute.cpp @@ -0,0 +1,1006 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_compute.cpp + * \brief Function implementantions for the voro_compute template. */ + +#include "worklist.hh" +#include "v_compute.hh" +#include "rad_option.hh" +#include "container.hh" +#include "container_prd.hh" + +namespace voro { + +/** The class constructor initializes constants from the container class, and + * sets up the mask and queue used for Voronoi computations. + * \param[in] con_ a reference to the container class to use. + * \param[in] (hx_,hy_,hz_) the size of the mask to use. */ +template +voro_compute::voro_compute(c_class &con_,int hx_,int hy_,int hz_) : + con(con_), boxx(con_.boxx), boxy(con_.boxy), boxz(con_.boxz), + xsp(con_.xsp), ysp(con_.ysp), zsp(con_.zsp), + hx(hx_), hy(hy_), hz(hz_), hxy(hx_*hy_), hxyz(hxy*hz_), ps(con_.ps), + id(con_.id), p(con_.p), co(con_.co), bxsq(boxx*boxx+boxy*boxy+boxz*boxz), + mv(0), qu_size(3*(3+hxy+hz*(hx+hy))), wl(con_.wl), mrad(con_.mrad), + mask(new unsigned int[hxyz]), qu(new int[qu_size]), qu_l(qu+qu_size) { + reset_mask(); +} + +/** Scans all of the particles within a block to see if any of them have a + * smaller distance to the given test vector. If one is found, the routine + * updates the minimum distance and store information about this particle. + * \param[in] ijk the index of the block. + * \param[in] (x,y,z) the test vector to consider (which may have already had a + * periodic displacement applied to it). + * \param[in] (di,dj,dk) the coordinates of the current block, to store if the + * particle record is updated. + * \param[in,out] w a reference to a particle record in which to store + * information about the particle whose Voronoi cell the + * vector is within. + * \param[in,out] mrs the current minimum distance, that may be updated if a + * closer particle is found. */ +template +inline void voro_compute::scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs) { + double x1,y1,z1,rs;bool in_block=false; + for(int l=0;l +void voro_compute::find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs) { + double qx=0,qy=0,qz=0,rs; + int i,j,k,di,dj,dk,ei,ej,ek,f,g,disp; + double fx,fy,fz,mxs,mys,mzs,*radp; + unsigned int q,*e,*mijk; + + // Init setup for parameters to return + w.ijk=-1;mrs=large_number; + + con.initialize_search(ci,cj,ck,ijk,i,j,k,disp); + + // Test all particles in the particle's local region first + scan_all(ijk,x,y,z,0,0,0,w,mrs); + + // Now compute the fractional position of the particle within its + // region and store it in (fx,fy,fz). We use this to compute an index + // (di,dj,dk) of which subregion the particle is within. + unsigned int m1,m2; + con.frac_pos(x,y,z,ci,cj,ck,fx,fy,fz); + di=int(fx*xsp*wl_fgrid);dj=int(fy*ysp*wl_fgrid);dk=int(fz*zsp*wl_fgrid); + + // The indices (di,dj,dk) tell us which worklist to use, to test the + // blocks in the optimal order. But we only store worklists for the + // eighth of the region where di, dj, and dk are all less than half the + // full grid. The rest of the cases are handled by symmetry. In this + // section, we detect for these cases, by reflecting high values of di, + // dj, and dk. For these cases, a mask is constructed in m1 and m2 + // which is used to flip the worklist information when it is loaded. + if(di>=wl_hgrid) { + mxs=boxx-fx; + m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0; + } else {m1=m2=0;mxs=fx;} + if(dj>=wl_hgrid) { + mys=boxy-fy; + m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0; + } else mys=fy; + if(dk>=wl_hgrid) { + mzs=boxz-fz; + m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0; + } else mzs=fz; + + // Do a quick test to account for the case when the minimum radius is + // small enought that no other blocks need to be considered + rs=con.r_max_add(mrs); + if(mxs*mxs>rs&&mys*mys>rs&&mzs*mzs>rs) return; + + // Now compute which worklist we are going to use, and set radp and e to + // point at the right offsets + ijk=di+wl_hgrid*(dj+wl_hgrid*dk); + radp=mrad+ijk*wl_seq_length; + e=(const_cast (wl))+ijk*wl_seq_length; + + // Read in how many items in the worklist can be tested without having to + // worry about writing to the mask + f=e[0];g=0; + do { + + // If mrs is less than the minimum distance to any untested + // block, then we are done + if(con.r_max_add(mrs)>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Check that the worklist position is in range + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + + // Call the compute_min_max_radius() function. This returns + // true if the minimum distance to the block is bigger than the + // current mrs, in which case we skip this block and move on. + // Otherwise, it computes the maximum distance to the block and + // returns it in crs. + if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + + // If mrs is bigger than the maximum distance to the block, + // then we have to test all particles in the block for + // intersections. Otherwise, we do additional checks and skip + // those particles which can't possibly intersect the block. + scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs); + } while(g>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Compute the position in the mask of the current block. If + // this lies outside the mask, then skip it. Otherwise, mark + // it. + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + mijk=mask+ei+hx*(ej+hy*ek); + *mijk=mv; + + // Skip this block if it is further away than the current + // minimum radius + if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs); + + if(qu_e>qu_l-18) add_list_memory(qu_s,qu_e); + scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e); + } + + // Do a check to see if we've reached the radius cutoff + if(con.r_max_add(mrs) +inline void voro_compute::add_to_mask(int ei,int ej,int ek,int *&qu_e) { + unsigned int *mijk=mask+ei+hx*(ej+hy*ek); + if(ek>0) if(*(mijk-hxy)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;} + if(ej>0) if(*(mijk-hx)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;} + if(ei>0) if(*(mijk-1)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;} + if(ei +inline void voro_compute::scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e) { + const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28; + if((q&b2)==b2) { + if(ei>0) {*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;} + if((q&b1)==0&&ei0) {*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;} + if((q&b3)==0&&ej0) {*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;} + if((q&b5)==0&&ek +template +bool voro_compute::compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck) { + static const int count_list[8]={7,11,15,19,26,35,45,59},*count_e=count_list+8; + double x,y,z,x1,y1,z1,qx=0,qy=0,qz=0; + double xlo,ylo,zlo,xhi,yhi,zhi,x2,y2,z2,rs; + int i,j,k,di,dj,dk,ei,ej,ek,f,g,l,disp; + double fx,fy,fz,gxs,gys,gzs,*radp; + unsigned int q,*e,*mijk; + + if(!con.initialize_voronoicell(c,ijk,s,ci,cj,ck,i,j,k,x,y,z,disp)) return false; + con.r_init(ijk,s); + + // Initialize the Voronoi cell to fill the entire container + double crs,mrs; + + int next_count=3,*count_p=(const_cast (count_list)); + + // Test all particles in the particle's local region first + for(l=0;l=wl_hgrid) { + gxs=fx; + m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0; + } else {m1=m2=0;gxs=boxx-fx;} + if(dj>=wl_hgrid) { + gys=fy; + m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0; + } else gys=boxy-fy; + if(dk>=wl_hgrid) { + gzs=fz; + m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0; + } else gzs=boxz-fz; + gxs*=gxs;gys*=gys;gzs*=gzs; + + // Now compute which worklist we are going to use, and set radp and e to + // point at the right offsets + ijk=di+wl_hgrid*(dj+wl_hgrid*dk); + radp=mrad+ijk*wl_seq_length; + e=(const_cast (wl))+ijk*wl_seq_length; + + // Read in how many items in the worklist can be tested without having to + // worry about writing to the mask + f=e[0];g=0; + do { + + // At the intervals specified by count_list, we recompute the + // maximum radius squared + if(g==next_count) { + mrs=c.max_radius_squared(); + if(count_p!=count_e) next_count=*(count_p++); + } + + // If mrs is less than the minimum distance to any untested + // block, then we are done + if(con.r_ctest(radp[g],mrs)) return true; + g++; + + // Load in a block off the worklist, permute it with the + // symmetry mask, and decode its position. These are all + // integer bit operations so they should run very fast. + q=e[g];q^=m1;q+=m2; + di=q&127;di-=64; + dj=(q>>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Check that the worklist position is in range + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + + // Call the compute_min_max_radius() function. This returns + // true if the minimum distance to the block is bigger than the + // current mrs, in which case we skip this block and move on. + // Otherwise, it computes the maximum distance to the block and + // returns it in crs. + if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + + // If mrs is bigger than the maximum distance to the block, + // then we have to test all particles in the block for + // intersections. Otherwise, we do additional checks and skip + // those particles which can't possibly intersect the block. + if(co[ijk]>0) { + l=0;x2=x-qx;y2=y-qy;z2=z-qz; + if(!con.r_ctest(crs,mrs)) { + do { + x1=p[ijk][ps*l]-x2; + y1=p[ijk][ps*l+1]-y2; + z1=p[ijk][ps*l+2]-z2; + rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); + if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; + l++; + } while (l>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Compute the position in the mask of the current block. If + // this lies outside the mask, then skip it. Otherwise, mark + // it. + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + mijk=mask+ei+hx*(ej+hy*ek); + *mijk=mv; + + // Call the compute_min_max_radius() function. This returns + // true if the minimum distance to the block is bigger than the + // current mrs, in which case we skip this block and move on. + // Otherwise, it computes the maximum distance to the block and + // returns it in crs. + if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + + // If mrs is bigger than the maximum distance to the block, + // then we have to test all particles in the block for + // intersections. Otherwise, we do additional checks and skip + // those particles which can't possibly intersect the block. + if(co[ijk]>0) { + l=0;x2=x-qx;y2=y-qy;z2=z-qz; + if(!con.r_ctest(crs,mrs)) { + do { + x1=p[ijk][ps*l]-x2; + y1=p[ijk][ps*l+1]-y2; + z1=p[ijk][ps*l+2]-z2; + rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); + if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; + l++; + } while (lqu_l-18) add_list_memory(qu_s,qu_e); + + // Test the parts of the worklist element which tell us what + // neighbors of this block are not on the worklist. Store them + // on the block list, and mark the mask. + scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e); + } + + // Do a check to see if we've reached the radius cutoff + if(con.r_ctest(radp[g],mrs)) return true; + + // We were unable to completely compute the cell based on the blocks in + // the worklist, so now we have to go block by block, reading in items + // off the list + while(qu_s!=qu_e) { + + // If we reached the end of the list memory loop back to the + // start + if(qu_s==qu_l) qu_s=qu; + + // Read in a block off the list, and compute the upper and lower + // coordinates in each of the three dimensions + ei=*(qu_s++);ej=*(qu_s++);ek=*(qu_s++); + xlo=(ei-i)*boxx-fx;xhi=xlo+boxx; + ylo=(ej-j)*boxy-fy;yhi=ylo+boxy; + zlo=(ek-k)*boxz-fz;zhi=zlo+boxz; + + // Carry out plane tests to see if any particle in this block + // could possibly intersect the cell + if(ei>i) { + if(ej>j) { + if(ek>k) {if(corner_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} + else if(ekk) {if(corner_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;} + else if(ekk) {if(edge_y_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} + else if(ekj) { + if(ek>k) {if(corner_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;} + else if(ekk) {if(corner_test(c,xhi,yhi,zlo,xlo,ylo,zhi)) continue;} + else if(ekk) {if(edge_y_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;} + else if(ekj) { + if(ek>k) {if(edge_x_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} + else if(ekk) {if(edge_x_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;} + else if(ekk) {if(face_z_test(c,xlo,ylo,zlo,xhi,yhi)) continue;} + else if(ek0) { + l=0;x2=x-qx;y2=y-qy;z2=z-qz; + do { + x1=p[ijk][ps*l]-x2; + y1=p[ijk][ps*l+1]-y2; + z1=p[ijk][ps*l+2]-z2; + rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); + if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; + l++; + } while (l +template +bool voro_compute::corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh) { + con.r_prime(xl*xl+yl*yl+zl*zl); + if(c.plane_intersects_guess(xh,yl,zl,con.r_cutoff(xl*xh+yl*yl+zl*zl))) return false; + if(c.plane_intersects(xh,yh,zl,con.r_cutoff(xl*xh+yl*yh+zl*zl))) return false; + if(c.plane_intersects(xl,yh,zl,con.r_cutoff(xl*xl+yl*yh+zl*zl))) return false; + if(c.plane_intersects(xl,yh,zh,con.r_cutoff(xl*xl+yl*yh+zl*zh))) return false; + if(c.plane_intersects(xl,yl,zh,con.r_cutoff(xl*xl+yl*yl+zl*zh))) return false; + if(c.plane_intersects(xh,yl,zh,con.r_cutoff(xl*xh+yl*yl+zl*zh))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on an edge which points along the x + * direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the + * block. + * \param[in] (yl,zl) the relative y and z coordinates of the corner of the + * block closest to the cell center. + * \param[in] (yh,zh) the relative y and z coordinates of the corner of the + * block furthest away from the cell center. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh) { + con.r_prime(yl*yl+zl*zl); + if(c.plane_intersects_guess(x0,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false; + if(c.plane_intersects(x1,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false; + if(c.plane_intersects(x1,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false; + if(c.plane_intersects(x0,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false; + if(c.plane_intersects(x0,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false; + if(c.plane_intersects(x1,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on an edge which points along the y + * direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the + * block. + * \param[in] (xl,zl) the relative x and z coordinates of the corner of the + * block closest to the cell center. + * \param[in] (xh,zh) the relative x and z coordinates of the corner of the + * block furthest away from the cell center. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh) { + con.r_prime(xl*xl+zl*zl); + if(c.plane_intersects_guess(xl,y0,zh,con.r_cutoff(xl*xl+zl*zh))) return false; + if(c.plane_intersects(xl,y1,zh,con.r_cutoff(xl*xl+zl*zh))) return false; + if(c.plane_intersects(xl,y1,zl,con.r_cutoff(xl*xl+zl*zl))) return false; + if(c.plane_intersects(xl,y0,zl,con.r_cutoff(xl*xl+zl*zl))) return false; + if(c.plane_intersects(xh,y0,zl,con.r_cutoff(xl*xh+zl*zl))) return false; + if(c.plane_intersects(xh,y1,zl,con.r_cutoff(xl*xh+zl*zl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on an edge which points along the z + * direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the block. + * \param[in] (xl,yl) the relative x and y coordinates of the corner of the + * block closest to the cell center. + * \param[in] (xh,yh) the relative x and y coordinates of the corner of the + * block furthest away from the cell center. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1) { + con.r_prime(xl*xl+yl*yl); + if(c.plane_intersects_guess(xl,yh,z0,con.r_cutoff(xl*xl+yl*yh))) return false; + if(c.plane_intersects(xl,yh,z1,con.r_cutoff(xl*xl+yl*yh))) return false; + if(c.plane_intersects(xl,yl,z1,con.r_cutoff(xl*xl+yl*yl))) return false; + if(c.plane_intersects(xl,yl,z0,con.r_cutoff(xl*xl+yl*yl))) return false; + if(c.plane_intersects(xh,yl,z0,con.r_cutoff(xl*xh+yl*yl))) return false; + if(c.plane_intersects(xh,yl,z1,con.r_cutoff(xl*xh+yl*yl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on a face aligned with the x direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] xl the minimum distance from the cell center to the face. + * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the + * block. + * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the + * block. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1) { + con.r_prime(xl*xl); + if(c.plane_intersects_guess(xl,y0,z0,con.r_cutoff(xl*xl))) return false; + if(c.plane_intersects(xl,y0,z1,con.r_cutoff(xl*xl))) return false; + if(c.plane_intersects(xl,y1,z1,con.r_cutoff(xl*xl))) return false; + if(c.plane_intersects(xl,y1,z0,con.r_cutoff(xl*xl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on a face aligned with the y direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] yl the minimum distance from the cell center to the face. + * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the + * block. + * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the + * block. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1) { + con.r_prime(yl*yl); + if(c.plane_intersects_guess(x0,yl,z0,con.r_cutoff(yl*yl))) return false; + if(c.plane_intersects(x0,yl,z1,con.r_cutoff(yl*yl))) return false; + if(c.plane_intersects(x1,yl,z1,con.r_cutoff(yl*yl))) return false; + if(c.plane_intersects(x1,yl,z0,con.r_cutoff(yl*yl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on a face aligned with the z direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] zl the minimum distance from the cell center to the face. + * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the + * block. + * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the + * block. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1) { + con.r_prime(zl*zl); + if(c.plane_intersects_guess(x0,y0,zl,con.r_cutoff(zl*zl))) return false; + if(c.plane_intersects(x0,y1,zl,con.r_cutoff(zl*zl))) return false; + if(c.plane_intersects(x1,y1,zl,con.r_cutoff(zl*zl))) return false; + if(c.plane_intersects(x1,y0,zl,con.r_cutoff(zl*zl))) return false; + return true; +} + + +/** This routine checks to see whether a point is within a particular distance + * of a nearby region. If the point is within the distance of the region, then + * the routine returns true, and computes the maximum distance from the point + * to the region. Otherwise, the routine returns false. + * \param[in] (di,dj,dk) the position of the nearby region to be tested, + * relative to the region that the point is in. + * \param[in] (fx,fy,fz) the displacement of the point within its region. + * \param[in] (gxs,gys,gzs) the maximum squared distances from the point to the + * sides of its region. + * \param[out] crs a reference in which to return the maximum distance to the + * region (only computed if the routine returns false). + * \param[in] mrs the distance to be tested. + * \return True if the region is further away than mrs, false if the region in + * within mrs. */ +template +bool voro_compute::compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gxs,double gys,double gzs,double &crs,double mrs) { + double xlo,ylo,zlo; + if(di>0) { + xlo=di*boxx-fx; + crs=xlo*xlo; + if(dj>0) { + ylo=dj*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo+boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo+boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs; + } + } else if(dj<0) { + ylo=(dj+1)*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo-boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo-boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs; + } + } else { + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=gys+boxx*(2*xlo+boxx); + } + } else if(di<0) { + xlo=(di+1)*boxx-fx; + crs=xlo*xlo; + if(dj>0) { + ylo=dj*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo+boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo+boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(-2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs; + } + } else if(dj<0) { + ylo=(dj+1)*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo-boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo-boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(-2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs; + } + } else { + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=gys+boxx*(-2*xlo+boxx); + } + } else { + if(dj>0) { + ylo=dj*boxy-fy; + crs=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=boxy*(2*ylo+boxy); + } else if(dj<0) { + ylo=(dj+1)*boxy-fy; + crs=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=boxy*(-2*ylo+boxy); + } else { + if(dk>0) { + zlo=dk*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + crs=0; + voro_fatal_error("Min/max radius function called for central block, which should never\nhappen.",VOROPP_INTERNAL_ERROR); + } + crs+=gys; + } + crs+=gxs; + } + return false; +} + +template +bool voro_compute::compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs) { + double t,crs; + + if(di>0) {t=di*boxx-fx;crs=t*t;} + else if(di<0) {t=(di+1)*boxx-fx;crs=t*t;} + else crs=0; + + if(dj>0) {t=dj*boxy-fy;crs+=t*t;} + else if(dj<0) {t=(dj+1)*boxy-fy;crs+=t*t;} + + if(dk>0) {t=dk*boxz-fz;crs+=t*t;} + else if(dk<0) {t=(dk+1)*boxz-fz;crs+=t*t;} + + return crs>con.r_max_add(mrs); +} + +/** Adds memory to the queue. + * \param[in,out] qu_s a reference to the queue start pointer. + * \param[in,out] qu_e a reference to the queue end pointer. */ +template +inline void voro_compute::add_list_memory(int*& qu_s,int*& qu_e) { + qu_size<<=1; + int *qu_n=new int[qu_size],*qu_c=qu_n; +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"List memory scaled up to %d\n",qu_size); +#endif + if(qu_s<=qu_e) { + while(qu_s::voro_compute(container&,int,int,int); +template voro_compute::voro_compute(container_poly&,int,int,int); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); + +// Explicit template instantiation +template voro_compute::voro_compute(container_periodic&,int,int,int); +template voro_compute::voro_compute(container_periodic_poly&,int,int,int); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); + +} diff --git a/src/voro++/v_compute.hh b/src/voro++/v_compute.hh new file mode 100644 index 00000000..03e58efb --- /dev/null +++ b/src/voro++/v_compute.hh @@ -0,0 +1,149 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_compute.hh + * \brief Header file for the voro_compute template and related classes. */ + +#ifndef VOROPP_V_COMPUTE_HH +#define VOROPP_V_COMPUTE_HH + +#include "config.hh" +#include "worklist.hh" +#include "cell.hh" + +namespace voro { + +/** \brief Structure for holding information about a particle. + * + * This small structure holds information about a single particle, and is used + * by several of the routines in the voro_compute template for passing + * information by reference between functions. */ +struct particle_record { + /** The index of the block that the particle is within. */ + int ijk; + /** The number of particle within its block. */ + int l; + /** The x-index of the block. */ + int di; + /** The y-index of the block. */ + int dj; + /** The z-index of the block. */ + int dk; +}; + +/** \brief Template for carrying out Voronoi cell computations. */ +template +class voro_compute { + public: + /** A reference to the container class on which to carry out*/ + c_class &con; + /** The size of an internal computational block in the x + * direction. */ + const double boxx; + /** The size of an internal computational block in the y + * direction. */ + const double boxy; + /** The size of an internal computational block in the z + * direction. */ + const double boxz; + /** The inverse box length in the x direction, set to + * nx/(bx-ax). */ + const double xsp; + /** The inverse box length in the y direction, set to + * ny/(by-ay). */ + const double ysp; + /** The inverse box length in the z direction, set to + * nz/(bz-az). */ + const double zsp; + /** The number of boxes in the x direction for the searching mask. */ + const int hx; + /** The number of boxes in the y direction for the searching mask. */ + const int hy; + /** The number of boxes in the z direction for the searching mask. */ + const int hz; + /** A constant, set to the value of hx multiplied by hy, which + * is used in the routines which step through mask boxes in + * sequence. */ + const int hxy; + /** A constant, set to the value of hx*hy*hz, which is used in + * the routines which step through mask boxes in sequence. */ + const int hxyz; + /** The number of floating point entries to store for each + * particle. */ + const int ps; + /** This array holds the numerical IDs of each particle in each + * computational box. */ + int **id; + /** A two dimensional array holding particle positions. For the + * derived container_poly class, this also holds particle + * radii. */ + double **p; + /** An array holding the number of particles within each + * computational box of the container. */ + int *co; + voro_compute(c_class &con_,int hx_,int hy_,int hz_); + /** The class destructor frees the dynamically allocated memory + * for the mask and queue. */ + ~voro_compute() { + delete [] qu; + delete [] mask; + } + template + bool compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck); + void find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs); + private: + /** A constant set to boxx*boxx+boxy*boxy+boxz*boxz, which is + * frequently used in the computation. */ + const double bxsq; + /** This sets the current value being used to mark tested blocks + * in the mask. */ + unsigned int mv; + /** The current size of the search list. */ + int qu_size; + /** A pointer to the array of worklists. */ + const unsigned int *wl; + /** An pointer to the array holding the minimum distances + * associated with the worklists. */ + double *mrad; + /** This array is used during the cell computation to determine + * which blocks have been considered. */ + unsigned int *mask; + /** An array is used to store the queue of blocks to test + * during the Voronoi cell computation. */ + int *qu; + /** A pointer to the end of the queue array, used to determine + * when the queue is full. */ + int *qu_l; + template + bool corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh); + template + inline bool edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh); + template + inline bool edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh); + template + inline bool edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1); + template + inline bool face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1); + template + inline bool face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1); + template + inline bool face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1); + bool compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gx,double gy,double gz,double& crs,double mrs); + bool compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs); + inline void add_to_mask(int ei,int ej,int ek,int *&qu_e); + inline void scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e); + inline void scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs); + void add_list_memory(int*& qu_s,int*& qu_e); + /** Resets the mask in cases where the mask counter wraps + * around. */ + inline void reset_mask() { + for(unsigned int *mp(mask);mp +bool wall_sphere::cut_cell_base(v_cell &c,double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,dq=xd*xd+yd*yd+zd*zd; + if (dq>1e-5) { + dq=2*(sqrt(dq)*rc-dq); + return c.nplane(xd,yd,zd,dq,w_id); + } + return true; +} + +/** Tests to see whether a point is inside the plane wall object. + * \param[in] (x,y,z) the vector to test. + * \return True if the point is inside, false if the point is outside. */ +bool wall_plane::point_inside(double x,double y,double z) { + return x*xc+y*yc+z*zc +bool wall_plane::cut_cell_base(v_cell &c,double x,double y,double z) { + double dq=2*(ac-x*xc-y*yc-z*zc); + return c.nplane(xc,yc,zc,dq,w_id); +} + +/** Tests to see whether a point is inside the cylindrical wall object. + * \param[in] (x,y,z) the vector to test. + * \return True if the point is inside, false if the point is outside. */ +bool wall_cylinder::point_inside(double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc; + double pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + return xd*xd+yd*yd+zd*zd +bool wall_cylinder::cut_cell_base(v_cell &c,double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + pa=xd*xd+yd*yd+zd*zd; + if(pa>1e-5) { + pa=2*(sqrt(pa)*rc-pa); + return c.nplane(xd,yd,zd,pa,w_id); + } + return true; +} + +/** Tests to see whether a point is inside the cone wall object. + * \param[in] (x,y,z) the vector to test. + * \return True if the point is inside, false if the point is outside. */ +bool wall_cone::point_inside(double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + pa*=gra; + if (pa<0) return false; + pa*=pa; + return xd*xd+yd*yd+zd*zd +bool wall_cone::cut_cell_base(v_cell &c,double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,xf,yf,zf,q,pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + pa=xd*xd+yd*yd+zd*zd; + if(pa>1e-5) { + pa=1/sqrt(pa); + q=sqrt(asi); + xf=-sang*q*xa+cang*pa*xd; + yf=-sang*q*ya+cang*pa*yd; + zf=-sang*q*za+cang*pa*zd; + pa=2*(xf*(xc-x)+yf*(yc-y)+zf*(zc-z)); + return c.nplane(xf,yf,zf,pa,w_id); + } + return true; +} + +// Explicit instantiation +template bool wall_sphere::cut_cell_base(voronoicell&,double,double,double); +template bool wall_sphere::cut_cell_base(voronoicell_neighbor&,double,double,double); +template bool wall_plane::cut_cell_base(voronoicell&,double,double,double); +template bool wall_plane::cut_cell_base(voronoicell_neighbor&,double,double,double); +template bool wall_cylinder::cut_cell_base(voronoicell&,double,double,double); +template bool wall_cylinder::cut_cell_base(voronoicell_neighbor&,double,double,double); +template bool wall_cone::cut_cell_base(voronoicell&,double,double,double); +template bool wall_cone::cut_cell_base(voronoicell_neighbor&,double,double,double); + +} diff --git a/src/voro++/wall.hh b/src/voro++/wall.hh new file mode 100644 index 00000000..6db0c5a2 --- /dev/null +++ b/src/voro++/wall.hh @@ -0,0 +1,119 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file wall.hh + * \brief Header file for the derived wall classes. */ + +#ifndef VOROPP_WALL_HH +#define VOROPP_WALL_HH + +#include "cell.hh" +#include "container.hh" + +namespace voro { + +/** \brief A class representing a spherical wall object. + * + * This class represents a spherical wall object. */ +struct wall_sphere : public wall { + public: + /** Constructs a spherical wall object. + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. + * \param[in] (xc_,yc_,zc_) a position vector for the sphere's + * center. + * \param[in] rc_ the radius of the sphere. */ + wall_sphere(double xc_,double yc_,double zc_,double rc_,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), rc(rc_) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,rc; +}; + +/** \brief A class representing a plane wall object. + * + * This class represents a single plane wall object. */ +struct wall_plane : public wall { + public: + /** Constructs a plane wall object. + * \param[in] (xc_,yc_,zc_) a normal vector to the plane. + * \param[in] ac_ a displacement along the normal vector. + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. */ + wall_plane(double xc_,double yc_,double zc_,double ac_,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), ac(ac_) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,ac; +}; + +/** \brief A class representing a cylindrical wall object. + * + * This class represents a open cylinder wall object. */ +struct wall_cylinder : public wall { + public: + /** Constructs a cylinder wall object. + * \param[in] (xc_,yc_,zc_) a point on the axis of the + * cylinder. + * \param[in] (xa_,ya_,za_) a vector pointing along the + * direction of the cylinder. + * \param[in] rc_ the radius of the cylinder + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. */ + wall_cylinder(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double rc_,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_), + asi(1/(xa_*xa_+ya_*ya_+za_*za_)), rc(rc_) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,xa,ya,za,asi,rc; +}; + + +/** \brief A class representing a conical wall object. + * + * This class represents a cone wall object. */ +struct wall_cone : public wall { + public: + /** Constructs a cone wall object. + * \param[in] (xc_,yc_,zc_) the apex of the cone. + * \param[in] (xa_,ya_,za_) a vector pointing along the axis of + * the cone. + * \param[in] ang the angle (in radians) of the cone, measured + * from the axis. + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. */ + wall_cone(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double ang,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_), + asi(1/(xa_*xa_+ya_*ya_+za_*za_)), + gra(tan(ang)), sang(sin(ang)), cang(cos(ang)) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,xa,ya,za,asi,gra,sang,cang; +}; + +} + +#endif diff --git a/src/voro++/worklist.hh b/src/voro++/worklist.hh new file mode 100644 index 00000000..878cf2cf --- /dev/null +++ b/src/voro++/worklist.hh @@ -0,0 +1,32 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (LBL / UC Berkeley) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file worklist.hh + * \brief Header file for setting constants used in the block worklists that are + * used during cell computation. + * + * This file is automatically generated by worklist_gen.pl and it is not + * intended to be edited by hand. */ + +#ifndef VOROPP_WORKLIST_HH +#define VOROPP_WORKLIST_HH + +namespace voro { + +/** Each region is divided into a grid of subregions, and a worklist is +# constructed for each. This parameter sets is set to half the number of +# subregions that the block is divided into. */ +const int wl_hgrid=4; +/** The number of subregions that a block is subdivided into, which is twice +the value of hgrid. */ +const int wl_fgrid=8; +/** The total number of worklists, set to the cube of hgrid. */ +const int wl_hgridcu=64; +/** The number of elements in each worklist. */ +const int wl_seq_length=64; + +} +#endif diff --git a/src/voro++/worklist_gen.pl b/src/voro++/worklist_gen.pl new file mode 100755 index 00000000..a3153f5a --- /dev/null +++ b/src/voro++/worklist_gen.pl @@ -0,0 +1,236 @@ +#!/usr/bin/perl +# Voro++, a 3D cell-based Voronoi library +# +# Author : Chris H. Rycroft (LBL / UC Berkeley) +# Email : chr@alum.mit.edu +# Date : August 30th 2011 +# +# worklist_gen.pl - this perl script is used to automatically generate the +# worklists of blocks that are stored in worklist.cpp, that are used by the +# compute_cell() routine to ensure maximum efficiency + +# Each region is divided into a grid of subregions, and a worklist is +# constructed for each. This parameter sets is set to half the number of +# subregions that the block is divided into. +$hr=4; + +# This parameter is automatically set to the the number of subregions that the +# block is divided into +$r=$hr*2; + +# This parameter sets the total number of block entries in each worklist +$ls=63; + +# When the worklists are being constructed, a mask array is made use of. To +# prevent the creation of array elements with negative indices, this parameter +# sets an arbitrary displacement. +$dis=8; + +# Constants used mask indexing +$d=2*$dis+1;$dd=$d*$d;$d0=(1+$d+$dd)*$dis; + +use Math::Trig; + +# Construct the worklist header file, based on the parameters above +open W,">worklist.hh"; +print W <v_base_wl.cpp"; +print W < $l $xp $yp $zp\n" if $l<4; + push @b,(splice @a,3*$nx,3);$ac--; + } + + # Mark all blocks that are on this worklist entry + $m[$d0]=++$v; + for($i=0;$i<$#b;$i+=3) { + $xt=$b[$i];$yt=$b[$i+1];$zt=$b[$i+2]; + $m[$d0+$xt+$d*$yt+$dd*$zt]=$v; + } + + # Find which neighboring outside blocks need to be marked when + # considering this block, being as conservative as possible by + # overwriting the marks, so that the last possible entry that can reach + # a block is used + for($i=$j=0;$i<$#b;$i+=3,$j++) { + $xt=$b[$i];$yt=$b[$i+1];$zt=$b[$i+2]; + $k=$d0+$xt+$yt*$d+$zt*$dd; + $la[$k+1]=$j, $m[$k+1]=$v+1 if $xt>=0 && $m[$k+1]!=$v; + $la[$k+$d]=$j, $m[$k+$d]=$v+1 if $yt>=0 && $m[$k+$d]!=$v; + $la[$k+$dd]=$j, $m[$k+$dd]=$v+1 if $zt>=0 && $m[$k+$dd]!=$v; + $la[$k-1]=$j, $m[$k-1]=$v+1 if $xt<=0 && $m[$k-1]!=$v; + $la[$k-$d]=$j, $m[$k-$d]=$v+1 if $yt<=0 && $m[$k-$d]!=$v; + $la[$k-$dd]=$j, $m[$k-$dd]=$v+1 if $zt<=0 && $m[$k-$dd]!=$v; + } + + # Scan to ensure that no neighboring blocks have been missed by the + # outwards-looking logic in the above section + for($i=0;$i<$#b;$i+=3) { + wl_check($d0+$b[$i]+$b[$i+1]*$d+$b[$i+2]*$dd); + } + + # Compute the number of entries where outside blocks do not need to be + # consider + for($i=$j=0;$i<$#b;$i+=3,$j++) { + $k=$d0+$b[$i]+$b[$i+1]*$d+$b[$i+2]*$dd; + last if $m[$k+1]!=$v; + last if $m[$k+$d]!=$v; + last if $m[$k+$dd]!=$v; + last if $m[$k-1]!=$v; + last if $m[$k-$d]!=$v; + last if $m[$k-$dd]!=$v; + } + print W "\t$j"; + + # Create worklist entry and save to file + $j=0; + while ($#b>0) { + $xt=shift @b;$yt=shift @b;$zt=shift @b; + $k=$d0+$xt+$yt*$d+$zt*$dd; + $o=0; + $o|=1 if $m[$k+1]!=$v && $la[$k+1]==$j; + $o^=3 if $m[$k-1]!=$v && $la[$k-1]==$j; + $o|=8 if $m[$k+$d]!=$v && $la[$k+$d]==$j; + $o^=24 if $m[$k-$d]!=$v && $la[$k-$d]==$j; + $o|=64 if $m[$k+$dd]!=$v && $la[$k+$dd]==$j; + $o^=192 if $m[$k-$dd]!=$v && $la[$k-$dd]==$j; + printf W ",%#x",(($xt+64)|($yt+64)<<7|($zt+64)<<14|$o<<21); + $j++; + } + print W "," unless $ind==$hr*$hr*$hr-1; + print W "\n"; + + # Remove list memory + undef @a; + undef @b; +} + +sub add { + if ($m[$d0+@_[0]+$d*@_[1]+$dd*@_[2]]!=$v) { + $ac++; + push @a,@_[0],@_[1],@_[2]; + $m[$d0+@_[0]+$d*@_[1]+$dd*@_[2]]=$v; + } +} + +sub dis { + $xl=@_[3]+0.3-@_[0];$xh=@_[3]+0.7-@_[0]; + $yl=@_[4]+0.3-@_[1];$yh=@_[4]+0.7-@_[1]; + $zl=@_[5]+0.3-@_[2];$zh=@_[5]+0.7-@_[2]; + $dis=(abs($xl)0; + $xco=@_[0]-@_[3]-1 if @_[3]<0; + $yco=@_[1]-@_[4] if @_[4]>0; + $yco=@_[1]-@_[4]-1 if @_[4]<0; + $zco=@_[2]-@_[5] if @_[5]>0; + $zco=@_[2]-@_[5]-1 if @_[5]<0; + return sqrt $xco*$xco+$yco*$yco+$zco*$zco; +} + +sub wl_check { + die "Failure in worklist construction\n" if $m[@_[0]+1]<$v||$m[@_[0]-1]<$v + ||$m[@_[0]+$d]<$v||$m[@_[0]-$d]<$v + ||$m[@_[0]+$dd]<$v||$m[@_[0]-$dd]<$v; +}