FML
Data Structures | Typedefs | Enumerations | Functions | Variables
FML::TRIANGULATION Namespace Reference

This namespace deals with performin triangulations (Delaunay and Voronoi) More...

Data Structures

class  MPIPeriodicDelaunay
 Create a Periodic Delaunay triangulation with particles spread across tasks. More...
 
struct  VertexDataWatershed
 The vertex base for the CGAL tesselation needed for the Watershed algorithm. More...
 
struct  WatershedBasin
 This is a general binning class for doing watershed based on [quantity] (typically volume) When we bin particles to the watershed basins we compute stuff based on what is in this class. More...
 

Typedefs

typedef CGAL::Exact_predicates_inexact_constructions_kernel K
 
typedef CGAL::Periodic_3_Delaunay_triangulation_traits_3< K > Gt
 
using VbDS = CGAL::Periodic_3_triangulation_ds_vertex_base_3<>
 
using Vb = CGAL::Triangulation_vertex_base_3< Gt, VbDS >
 
using CbDS = CGAL::Periodic_3_triangulation_ds_cell_base_3<>
 
using Cb = CGAL::Triangulation_cell_base_3< Gt, CbDS >
 
template<class T >
using VbInfo = CGAL::Triangulation_vertex_base_with_info_3< T, Gt, Vb >
 
template<class T >
using TDS = CGAL::Triangulation_data_structure_3< VbInfo< T >, Cb >
 
template<class T >
using PeriodicDelaunayWithInfo = CGAL::Periodic_3_Delaunay_triangulation_3< Gt, TDS< T > >
 
using VD_FIDUCIAL = CGAL::Periodic_3_triangulation_ds_vertex_base_3<>
 
using Fb = CGAL::Periodic_2_triangulation_face_base_2< Gt >
 
using IDType = int
 
using QuantityType = float
 

Enumerations

enum  PointType { REGULAR_POINT , BOUNDARY_POINT , GUARD_POINT }
 

Functions

template<class VD , class T >
void fiducial_assignment_function (VD *v, T *p)
 Fiducial assignment function for when we have info at vertices (the fiducial choice is no info) More...
 
template<class T , class U >
void WatershedGeneral (MPIPeriodicDelaunay< T, VertexDataWatershed > &D, T *p, size_t NumPart, std::vector< double > &quantity, std::vector< U > &watershed_groups)
 In this method we perform a tesselation, then we assign [quantity] to vertices and walk the tesselation assigning the particles to Watershed basins Quantity is a vector of size NumPart. More...
 
template<class T , class U >
void WatershedDensity (T *p, size_t NumPart, std::vector< U > &watershed_groups, double buffer_fraction=0.30, double random_fraction=0.5, bool do_density_maximum=false)
 In this method we perform a tesselation, compute voronoi volumes and assign density to each cell mass_of_part/volume and then we locate the density minima (or maxima) and walk the tesselation assigning the particles to Watershed basins If density_maximum = true then we assign 1/density to the particles and run the same algorithm so we locate density peaks instead of density minima. More...
 

Variables

const IDType NoWatershedID = std::numeric_limits<IDType>::max()
 
const QuantityType Infinity = std::numeric_limits<QuantityType>::infinity()
 

Detailed Description

This namespace deals with performin triangulations (Delaunay and Voronoi)

Typedef Documentation

◆ Cb

using FML::TRIANGULATION::Cb = typedef CGAL::Triangulation_cell_base_3<Gt, CbDS>

Definition at line 56 of file PeriodicDelaunay.h.

◆ CbDS

using FML::TRIANGULATION::CbDS = typedef CGAL::Periodic_3_triangulation_ds_cell_base_3<>

Definition at line 55 of file PeriodicDelaunay.h.

◆ Fb

using FML::TRIANGULATION::Fb = typedef CGAL::Periodic_2_triangulation_face_base_2<Gt>

Definition at line 69 of file PeriodicDelaunay.h.

◆ Gt

typedef CGAL::Periodic_2_Delaunay_triangulation_traits_2< K > FML::TRIANGULATION::Gt

Definition at line 52 of file PeriodicDelaunay.h.

◆ IDType

using FML::TRIANGULATION::IDType = typedef int

Definition at line 695 of file PeriodicDelaunay.h.

◆ K

typedef CGAL::Exact_predicates_inexact_constructions_kernel FML::TRIANGULATION::K

Definition at line 50 of file PeriodicDelaunay.h.

◆ PeriodicDelaunayWithInfo

template<class T >
using FML::TRIANGULATION::PeriodicDelaunayWithInfo = typedef CGAL::Periodic_2_Delaunay_triangulation_2<Gt, TDS<T> >

Definition at line 62 of file PeriodicDelaunay.h.

◆ QuantityType

using FML::TRIANGULATION::QuantityType = typedef float

Definition at line 696 of file PeriodicDelaunay.h.

◆ TDS

template<class T >
using FML::TRIANGULATION::TDS = typedef CGAL::Triangulation_data_structure_2<VbInfo<T>, Fb>

Definition at line 60 of file PeriodicDelaunay.h.

◆ Vb

typedef CGAL::Triangulation_vertex_base_2< Gt > FML::TRIANGULATION::Vb

Definition at line 54 of file PeriodicDelaunay.h.

◆ VbDS

using FML::TRIANGULATION::VbDS = typedef CGAL::Periodic_3_triangulation_ds_vertex_base_3<>

Definition at line 53 of file PeriodicDelaunay.h.

◆ VbInfo

template<class T >
using FML::TRIANGULATION::VbInfo = typedef CGAL::Triangulation_vertex_base_with_info_2<T, Gt, Vb>

Definition at line 58 of file PeriodicDelaunay.h.

◆ VD_FIDUCIAL

typedef CGAL::Periodic_2_triangulation_vertex_base_2< Gt > FML::TRIANGULATION::VD_FIDUCIAL

Definition at line 63 of file PeriodicDelaunay.h.

Enumeration Type Documentation

◆ PointType

Enumerator
REGULAR_POINT 
BOUNDARY_POINT 
GUARD_POINT 

Definition at line 692 of file PeriodicDelaunay.h.

Function Documentation

◆ fiducial_assignment_function()

template<class VD , class T >
void FML::TRIANGULATION::fiducial_assignment_function ( VD *  v,
T *  p 
)

Fiducial assignment function for when we have info at vertices (the fiducial choice is no info)

Template Parameters
VDThe vertex base
Theparticle class
Parameters
[in]vPointer to the vertex
[in]pPointer to the particle

Definition at line 88 of file PeriodicDelaunay.h.

88{}

◆ WatershedDensity()

template<class T , class U >
void FML::TRIANGULATION::WatershedDensity ( T *  p,
size_t  NumPart,
std::vector< U > &  watershed_groups,
double  buffer_fraction = 0.30,
double  random_fraction = 0.5,
bool  do_density_maximum = false 
)

In this method we perform a tesselation, compute voronoi volumes and assign density to each cell mass_of_part/volume and then we locate the density minima (or maxima) and walk the tesselation assigning the particles to Watershed basins If density_maximum = true then we assign 1/density to the particles and run the same algorithm so we locate density peaks instead of density minima.

The method sets the volume of each particle and the particle must have a set_volume method.

Template Parameters
TThe particle class
UThe watershed binning class
Parameters
[in]pPointer to the particles
[in]NumPartNumber of local particles.
[out]watershed_groupsThe result of the watershed: a list of watershed groups.
[in]buffer_fractionOptional. How big part of the neighbor domain do we include as the buffer.
[in]random_fractionOptional. How many (as fraction of the normal particles) random particles do we add (this is to help speed up the tesslation).
[in]do_density_maximumOptional. Watershed based on the density (false) or 1/density (true).

Definition at line 1304 of file PeriodicDelaunay.h.

1309 {
1310
1311 static_assert(FML::PARTICLE::has_set_volume<T>(),
1312 "[WatershedDensity] We require the particle to have a set_volume / get_volume method");
1313
1314 if (FML::ThisTask == 0) {
1315 if (do_density_maximum)
1316 std::cout << "[WatershedDensity] We will locate minima of 1/(voronoid density) of particles\n";
1317 else
1318 std::cout << "[WatershedDensity] We will locate minima of the voronoi density of particles\n";
1319 std::cout << "[WatershedDensity] Buffer fraction " << buffer_fraction << " of neighbor domains\n";
1320 }
1321
1322 // Vertex assignement function
1323 std::function<void(VertexDataWatershed *, T *)> vertex_assignment_function =
1324 [](VertexDataWatershed * v, T * p) { v->part_ptr = p ? (void *)p : nullptr; };
1325
1326 // Create tesselation
1327 if (FML::ThisTask == 0)
1328 std::cout << "[WatershedDensity] Computing tesselation\n";
1329 MPIPeriodicDelaunay<T, VertexDataWatershed> D;
1330 D.create(p, NumPart, buffer_fraction, random_fraction, vertex_assignment_function);
1331 // double dx_buffer = D.get_dx_buffer();
1332
1333 // Compute Voronoi volumes
1334 if (FML::ThisTask == 0)
1335 std::cout << "[WatershedDensity] Computing voronoi volumes from tesselation\n";
1336 std::vector<double> volumes;
1337 D.VoronoiVolume(volumes);
1338
1339 // Set the volume to the particles
1340 if constexpr (FML::PARTICLE::has_set_volume<T>()) {
1341 for (size_t i = 0; i < NumPart; i++) {
1342 FML::PARTICLE::SetVolume(p[i], volumes[i]);
1343 }
1344 }
1345
1346 // Convert volumes to mass density
1347 // If you want to tesselated based on an other property then this is all we
1348 // need to change
1349 std::vector<double> & density = volumes;
1350 double mass = 1.0;
1351 for (size_t i = 0; i < NumPart; i++) {
1352 if constexpr (FML::PARTICLE::has_get_mass<T>()) {
1353 mass = FML::PARTICLE::GetMass(p[i]);
1354 }
1355 if (do_density_maximum)
1356 // To find clusters use 1/density as the quantity to watershed
1357 density[i] = volumes[i] / mass;
1358 else
1359 // To find voids use density as watershed quantity
1360 density[i] = mass / volumes[i];
1361 }
1362
1363 // Run general algorithm
1364 WatershedGeneral<T, U>(D, p, NumPart, density, watershed_groups);
1365 }
constexpr void SetVolume(...)
set_mass constexpr double GetMass(Args &... args)
int ThisTask
The MPI task number.
Definition: Global.cpp:33

◆ WatershedGeneral()

template<class T , class U >
void FML::TRIANGULATION::WatershedGeneral ( MPIPeriodicDelaunay< T, VertexDataWatershed > &  D,
T *  p,
size_t  NumPart,
std::vector< double > &  quantity,
std::vector< U > &  watershed_groups 
)

In this method we perform a tesselation, then we assign [quantity] to vertices and walk the tesselation assigning the particles to Watershed basins Quantity is a vector of size NumPart.

When quantity is the density or inverse density of the particle then we get a Void or Cluster finder

If the buffer is too small the results will not be perfect, we give warnings when this is the case (instead of just throwing) as some times this is fine. To be sure of the result its a good idea to try with a smaller number of CPUs or large buffer and check that the results match

Template Parameters
TThe particle class
UThe watershed binning class
Parameters
[in]DMPIPeriodicDelaunay tesselation (already created).
[in]pPointer to the particles.
[in]NumPartNumber of local particles.
[in]quantityVector with the quantity to watershed on (e.g. the density of the particles). quantity[i] corresponds to the quantity for particle i.
[out]watershed_groupsThe result of the watershed: a list of watershed groups.

Definition at line 733 of file PeriodicDelaunay.h.

737 {
738
739 assert(quantity.size() == NumPart);
740 [[maybe_unused]] const double dx_buffer = D.get_dx_buffer();
741
742 // Fetch tesselation
743 auto dt = D.get_delaunay_triangulation();
744 using Vertex_handle = typename decltype(dt)::Vertex_handle;
745
746 // For boundaries and communication
747 [[maybe_unused]] size_t count_left = 0;
748 [[maybe_unused]] size_t count_right = 0;
749 [[maybe_unused]] size_t recv_left = 0;
750 [[maybe_unused]] size_t recv_right = 0;
751 [[maybe_unused]] size_t nboundary = 0;
752
753 // Assign quantity to particles
754 // Here we fetch the vertex handles from the tesselation
755 auto vs = D.get_vertex_handles_regular();
756 assert(vs.size() == NumPart);
757 for (size_t i = 0; i < NumPart; i++) {
758 vs[i]->info().quantity = quantity[i];
759 vs[i]->info().point_type = REGULAR_POINT;
760 }
761
762 // Deal with the boundary
763#ifdef USE_MPI
764 int LeftTask = (FML::ThisTask - 1 + FML::NTasks) % FML::NTasks;
765 int RightTask = (FML::ThisTask + 1) % FML::NTasks;
766 if (FML::NTasks > 1) {
767
768 // Count how many particles we have on the left and right
769 // NB: this relies on we do exactly the same thing in MPIPeriodicDelaunay
770 // A more roboust way is to communicate index we want, gather, send etc.
771 count_left = 0;
772 count_right = 0;
773 for (size_t i = 0; i < NumPart; i++) {
774 auto * pos = FML::PARTICLE::GetPos(p[i]);
775 if (pos[0] < FML::xmin_domain + dx_buffer)
776 count_left++;
777 if (pos[0] > FML::xmax_domain - dx_buffer)
778 count_right++;
779 }
780
781 // Comunicate how many to send
782 MPI_Status status;
783 MPI_Sendrecv(&count_left,
784 sizeof(count_left),
785 MPI_CHAR,
786 LeftTask,
787 0,
788 &recv_right,
789 sizeof(recv_right),
790 MPI_CHAR,
791 RightTask,
792 0,
793 MPI_COMM_WORLD,
794 &status);
795 MPI_Sendrecv(&count_right,
796 sizeof(count_right),
797 MPI_CHAR,
798 RightTask,
799 0,
800 &recv_left,
801 sizeof(recv_left),
802 MPI_CHAR,
803 LeftTask,
804 0,
805 MPI_COMM_WORLD,
806 &status);
807
808 // Gather quantity
809 std::vector<QuantityType> quantity_to_send_left(count_left);
810 std::vector<QuantityType> quantity_to_send_right(count_right);
811 std::vector<QuantityType> quantity_to_recv_left(recv_left);
812 std::vector<QuantityType> quantity_to_recv_right(recv_right);
813 count_left = 0;
814 count_right = 0;
815 for (size_t i = 0; i < NumPart; i++) {
816 auto * pos = FML::PARTICLE::GetPos(p[i]);
817 if (pos[0] < FML::xmin_domain + dx_buffer) {
818 quantity_to_send_left[count_left] = quantity[i];
819 count_left++;
820 }
821 if (pos[0] > FML::xmax_domain - dx_buffer) {
822 quantity_to_send_right[count_right] = quantity[i];
823 count_right++;
824 }
825 }
826
827 // Send quantity
828 MPI_Sendrecv(quantity_to_send_left.data(),
829 sizeof(QuantityType) * count_left,
830 MPI_CHAR,
831 LeftTask,
832 0,
833 quantity_to_recv_right.data(),
834 sizeof(QuantityType) * recv_right,
835 MPI_CHAR,
836 RightTask,
837 0,
838 MPI_COMM_WORLD,
839 &status);
840 MPI_Sendrecv(quantity_to_send_right.data(),
841 sizeof(QuantityType) * count_right,
842 MPI_CHAR,
843 RightTask,
844 0,
845 quantity_to_recv_left.data(),
846 sizeof(QuantityType) * recv_left,
847 MPI_CHAR,
848 LeftTask,
849 0,
850 MPI_COMM_WORLD,
851 &status);
852
853 // Assign quantity to particles
854 // Here we fetch the vertex handles from the tesselation
855 auto vs_boundary = D.get_vertex_handles_boundary();
856 nboundary = recv_left + recv_right;
857 assert(vs_boundary.size() == nboundary);
858 for (size_t i = 0; i < recv_left; i++) {
859 vs_boundary[i]->info().quantity = quantity_to_recv_left[i];
860 vs_boundary[i]->info().point_type = BOUNDARY_POINT;
861 }
862 for (size_t i = 0; i < recv_right; i++) {
863 vs_boundary[i + recv_left]->info().quantity = quantity_to_recv_right[i];
864 vs_boundary[i + recv_left]->info().point_type = BOUNDARY_POINT;
865 }
866
867 // Assign minimum neighbor
868 for (size_t i = 0; i < nboundary; i++) {
869 auto v = vs_boundary[i];
870 std::vector<Vertex_handle> vertices;
871 dt.adjacent_vertices(v, std::back_inserter(vertices));
872 auto min_quantity = Infinity;
873 for (auto & vnew : vertices) {
874 if (vnew->info().point_type == GUARD_POINT)
875 continue;
876 if (vnew->info().quantity < min_quantity)
877 min_quantity = vnew->info().quantity;
878 }
879 v->info().min_quantity_nbor = min_quantity;
880 if (v->info().min_quantity_nbor == Infinity) {
881 std::cout << "[WatershedGeneral] Warning boundary particle has no normal nbors " << v->point()
882 << " " << FML::ThisTask << "\n";
883 }
884 }
885 }
886#endif
887
888 // Find quantity minima
889 std::vector<char> is_minimum(NumPart, 0);
890 for (size_t i = 0; i < NumPart; i++) {
891 std::vector<Vertex_handle> vertices;
892 dt.adjacent_vertices(vs[i], std::back_inserter(vertices));
893 auto quantity = vs[i]->info().quantity;
894 is_minimum[i] = 1;
895 for (auto & v : vertices) {
896 if (v == vs[i])
897 continue;
898 if (v->info().quantity < quantity) {
899 is_minimum[i] = 0;
900 break;
901 }
902 }
903 }
904
905 // Count number of minima
906 size_t nminima = 0;
907 for (size_t i = 0; i < NumPart; i++) {
908 if (is_minimum[i] == 1) {
909 nminima++;
910 }
911 }
912
913 // Ensure unique task id
914 std::vector<int> nminima_per_task(FML::NTasks, 0);
915 nminima_per_task[FML::ThisTask] = nminima;
916#ifdef USE_MPI
917 MPI_Allreduce(MPI_IN_PLACE, nminima_per_task.data(), FML::NTasks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
918#endif
919 int id_start = 0;
920 for (int i = 0; i < FML::ThisTask; i++)
921 id_start += nminima_per_task[i];
922 size_t ntotal_minima = 0;
923 for (int i = 0; i < FML::NTasks; i++)
924 ntotal_minima += size_t(nminima_per_task[i]);
925
926 if (FML::ThisTask == 0)
927 std::cout << "[WatershedGeneral] We found " << ntotal_minima << " minimum points\n";
928
929 // Assign unique wathershed ID - this ID is unique across tasks
930 // Gather vertex handles to all the minimas
931 decltype(vs) vminima(nminima);
932 nminima = 0;
933 for (size_t i = 0; i < NumPart; i++) {
934 if (is_minimum[i]) {
935 auto v = vs[i];
936 vminima[nminima] = v;
937 v->info().WatershedID = id_start + nminima;
938 nminima++;
939 }
940 }
941 is_minimum.clear();
942 is_minimum.shrink_to_fit();
943
944 // Find the smallest quantity in the nbors
945 for (size_t i = 0; i < NumPart; i++) {
946 auto v = vs[i];
947 std::vector<Vertex_handle> vertices;
948 dt.adjacent_vertices(v, std::back_inserter(vertices));
949 // auto quantity = v->info().quantity;
950 auto min_quantity = Infinity;
951 for (auto & vnew : vertices) {
952 if (vnew->info().point_type == GUARD_POINT)
953 continue;
954 if (vnew->info().quantity < min_quantity)
955 min_quantity = vnew->info().quantity;
956 }
957 assert(min_quantity < Infinity);
958 v->info().min_quantity_nbor = min_quantity;
959 }
960
961 // Recursive call to find the watershed basins we set all neighbors with higher values of quantity as
962 // part of the basin and then do a recursive call with each of these neighbors as the starting point
963 std::function<int(Vertex_handle, IDType)> recursive_through_vertices = [&](Vertex_handle v, IDType id) {
964 if (v->info().point_type == GUARD_POINT)
965 return 0;
966
967 // Assign current vertex
968 int count = 0;
969 v->info().WatershedID = id;
970 if (v->info().point_type == REGULAR_POINT)
971 count++;
972
973 // Loop over neighbors
974 std::vector<Vertex_handle> vertices;
975 dt.adjacent_vertices(v, std::back_inserter(vertices));
976 auto quantity = v->info().quantity;
977 for (auto & vnew : vertices) {
978 if (vnew == v)
979 continue;
980 if (vnew->info().WatershedID == id)
981 continue;
982 if (vnew->info().point_type == GUARD_POINT)
983 continue;
984 if (quantity > vnew->info().min_quantity_nbor)
985 continue;
986 if (vnew->info().quantity > quantity) {
987 if (vnew->info().point_type == REGULAR_POINT) {
988 count++;
989 }
990 vnew->info().WatershedID = id;
991 count += recursive_through_vertices(vnew, id);
992 }
993 }
994 return count;
995 };
996
997 // We can now do this step with OpenMP if we want as no two basins should be able to overlap
998 size_t ntot = 0;
999 for (size_t i = 0; i < nminima; i++) {
1000 auto v = vminima[i];
1001 auto id = v->info().WatershedID;
1002 ntot += recursive_through_vertices(v, id);
1003 }
1004#ifdef DEBUG_TESSELATION
1005 std::cout << "We have " << nminima << " minima with " << ntot << " of " << NumPart
1006 << " particles tied up on task " << FML::ThisTask << "\n";
1007#endif
1008
1009 // Now we need to merge across tasks
1010 // We communicate boundary particles and their WatershedID to the left and right and do the merging
1011 // If some of the particles have id > 0 then they will be merged
1012 // We should really do this thing one by one (i,i+1) to ensure structures that span many tasks gets
1013 // assigned correctly. This is rarely an issue unless we have extremely few particles and many tasks
1014#ifdef USE_MPI
1015 if (FML::NTasks > 1) {
1016
1017 std::vector<IDType> watershed_id_to_send_left(count_left);
1018 std::vector<IDType> watershed_id_to_send_right(count_right);
1019 std::vector<IDType> watershed_id_to_recv_left(recv_left);
1020 std::vector<IDType> watershed_id_to_recv_right(recv_right);
1021
1022 // We merge back and forth until we are done
1023 // or maximum 3 times
1024 for (int s = 0; s < 3; s++) {
1025 auto vs = D.get_vertex_handles_regular();
1026 assert(vs.size() == NumPart);
1027 count_left = 0;
1028 count_right = 0;
1029 for (size_t i = 0; i < NumPart; i++) {
1030 auto id = vs[i]->info().WatershedID;
1031 auto * pos = FML::PARTICLE::GetPos(p[i]);
1032 if (pos[0] < FML::xmin_domain + dx_buffer) {
1033 watershed_id_to_send_left[count_left] = id;
1034 count_left++;
1035 }
1036 if (pos[0] > FML::xmax_domain - dx_buffer) {
1037 watershed_id_to_send_right[count_right] = id;
1038 count_right++;
1039 }
1040 }
1041 assert(watershed_id_to_send_left.size() == count_left);
1042 assert(watershed_id_to_send_right.size() == count_right);
1043
1044 MPI_Status status;
1045 MPI_Sendrecv(watershed_id_to_send_left.data(),
1046 sizeof(IDType) * count_left,
1047 MPI_CHAR,
1048 LeftTask,
1049 0,
1050 watershed_id_to_recv_right.data(),
1051 sizeof(IDType) * recv_right,
1052 MPI_CHAR,
1053 RightTask,
1054 0,
1055 MPI_COMM_WORLD,
1056 &status);
1057 MPI_Sendrecv(watershed_id_to_send_right.data(),
1058 sizeof(IDType) * count_right,
1059 MPI_CHAR,
1060 RightTask,
1061 0,
1062 watershed_id_to_recv_left.data(),
1063 sizeof(IDType) * recv_left,
1064 MPI_CHAR,
1065 LeftTask,
1066 0,
1067 MPI_COMM_WORLD,
1068 &status);
1069
1070 // Update Watershed IDs
1071 auto vs_boundary = D.get_vertex_handles_boundary();
1072 for (size_t i = 0; i < recv_left; i++) {
1073 auto v = vs_boundary[i];
1074 auto id = watershed_id_to_recv_left[i];
1075 v->info().WatershedID = id;
1076 }
1077 for (size_t i = 0; i < recv_right; i++) {
1078 auto v = vs_boundary[i + recv_left];
1079 auto id = watershed_id_to_recv_right[i];
1080 v->info().WatershedID = id;
1081 }
1082
1083 // Assign IDs to vertices recursively
1084 int ntot = 0;
1085 for (size_t i = 0; i < nboundary; i++) {
1086 auto v = vs_boundary[i];
1087 auto id = v->info().WatershedID;
1088 if (id == NoWatershedID)
1089 continue;
1090 ntot += recursive_through_vertices(v, id);
1091 }
1092#ifdef DEBUG_TESSELATION
1093 std::cout << "In merging on " << FML::ThisTask << " we assigned " << ntot << "\n";
1094#endif
1095 // If no more particles gets assigned we stop
1096 FML::SumOverTasks(&ntot);
1097 if (ntot == 0)
1098 break;
1099 if (s == 2 and FML::ThisTask == 0)
1100 std::cout << "[WatershedGeneral] Warning we merged 3 times and still not done, buffer should "
1101 "be larger, cannot guarantee the results are perfect\n";
1102 }
1103 }
1104#endif
1105
1106 // Count how many regular particles we have processed
1107 long long int assigned = 0;
1108 for (size_t i = 0; i < NumPart; i++) {
1109 if (vs[i]->info().WatershedID != NoWatershedID) {
1110 assigned++;
1111 } else {
1112 std::cout << "[WatershedGeneral] Warning: on task " << FML::ThisTask << " particle " << i
1113 << " pos: " << vs[i]->point() << " has not been assigned!\n";
1114 }
1115 }
1116#ifdef DEBUG_TESSELATION
1117 std::cout << "Processed on task " << FML::ThisTask << " is " << assigned << " / " << NumPart << "\n";
1118#endif
1119
1120 long long int npartotal = NumPart;
1121 FML::SumOverTasks(&assigned);
1122 FML::SumOverTasks(&npartotal);
1123
1124 if (FML::ThisTask == 0) {
1125 std::cout << "[WatershedGeneral] Total particles assigned " << assigned << " / " << npartotal << "\n";
1126 if (assigned < npartotal) {
1127 std::cout << "[WatershedGeneral] Warning we have not mange to assign all particles to their "
1128 "respective minima, cannot guarantee the results are perfect\n";
1129 }
1130 }
1131
1132 // Compute how many particles there are per group and communicate this
1133 std::vector<int> n_per_group(ntotal_minima, 0);
1134 for (size_t i = 0; i < NumPart; i++) {
1135 if (vs[i]->info().WatershedID != NoWatershedID) {
1136 n_per_group[vs[i]->info().WatershedID]++;
1137 }
1138 }
1139#ifdef USE_MPI
1140 MPI_Allreduce(MPI_IN_PLACE, n_per_group.data(), ntotal_minima, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
1141#endif
1142
1143 // Compute the minimum position and make sure all tasks have this
1144 std::vector<double> x_minima(ntotal_minima, 0.0);
1145 std::vector<double> y_minima(ntotal_minima, 0.0);
1146 std::vector<double> z_minima(ntotal_minima, 0.0);
1147 for (size_t i = 0; i < nminima; i++) {
1148 auto v = vminima[i];
1149
1150 // Get points in dual polyhedra, i.e. voronoi cell
1151 // std::vector< decltype(p) > pts;
1152 // auto tetra = dt.dual(v, std::back_inserter(pts));
1153 // std::cout << "Dual polyhedra: " << pts.size() << "\n";
1154
1155 // XXX Add compute circumcenter of core particle and 3 closest low density region
1156
1157 // Minimum density particle
1158 auto pos = v->point();
1159 x_minima[v->info().WatershedID] = pos[0];
1160 y_minima[v->info().WatershedID] = pos[1];
1161#if CGAL_NDIM == 3
1162 z_minima[v->info().WatershedID] = pos[2];
1163#endif
1164 }
1165#ifdef USE_MPI
1166 MPI_Allreduce(MPI_IN_PLACE, x_minima.data(), ntotal_minima, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
1167 MPI_Allreduce(MPI_IN_PLACE, y_minima.data(), ntotal_minima, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
1168 MPI_Allreduce(MPI_IN_PLACE, z_minima.data(), ntotal_minima, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
1169#endif
1170
1171 // Time to compile up the results. Make the result array
1172 watershed_groups.resize(ntotal_minima);
1173
1174 // Initialize groups on all tasks
1175 for (size_t i = 0; i < ntotal_minima; i++) {
1176#if CGAL_NDIM == 2
1177 double pos[] = {x_minima[i], y_minima[i]};
1178#elif CGAL_NDIM == 3
1179 double pos[] = {x_minima[i], y_minima[i], z_minima[i]};
1180#endif
1181 watershed_groups[i].init(pos);
1182 }
1183
1184 // Loop through all particles and assign data to groups
1185 for (size_t i = 0; i < NumPart; i++) {
1186 auto v = vs[i];
1187 auto id = v->info().WatershedID;
1188 if (id != NoWatershedID) {
1189 watershed_groups[id].add_particle((T *)v->info().part_ptr, v->info().quantity);
1190 }
1191 }
1192
1193 // Communicate stuff so that task 0 has all the data
1194 // NB: assumes watershed_groups is a simple type so that sizeof works, i.e. no dynamic allocated
1195 // objects in the class
1196#ifdef USE_MPI
1197 for (int i = 1; i < FML::NTasks; i++) {
1198 size_t bytes = sizeof(U) * ntotal_minima;
1199 if (FML::ThisTask == 0) {
1200 std::vector<U> watershed_groups_from_other_task(ntotal_minima);
1201 MPI_Status status;
1202 MPI_Recv(watershed_groups_from_other_task.data(), bytes, MPI_BYTE, i, 0, MPI_COMM_WORLD, &status);
1203
1204 // Merge in the groups
1205 for (size_t j = 0; j < ntotal_minima; j++) {
1206 watershed_groups[j].merge(watershed_groups_from_other_task[j]);
1207 }
1208 } else if (FML::ThisTask == i) {
1209 MPI_Send(watershed_groups.data(), bytes, MPI_BYTE, 0, 0, MPI_COMM_WORLD);
1210 }
1211 MPI_Barrier(MPI_COMM_WORLD);
1212 }
1213#endif
1214
1215 // Finalize the binning
1216 if (FML::ThisTask == 0) {
1217 for (size_t i = 0; i < ntotal_minima; i++) {
1218 watershed_groups[i].finalize();
1219 }
1220 } else {
1221 // We only keep the data on task 0 so clear the rest to avoid any confusion
1222 watershed_groups.clear();
1223 watershed_groups.shrink_to_fit();
1224 }
1225
1226 // XXX The stuff below should be in WatershedDensity or we should store quantity_min above!
1227 // If we sort we need to keep the id
1228 // Sort them by minimum density from large to small
1229 // std::sort(watershed_groups.begin(), watershed_groups.end(), [](const auto i, const auto j){ return
1230 // i.density_min > j.density_min; } );
1231
1232 /*
1233 // NB: only task 0 has groups
1234 // Second watershed merging. Compile up list of links between groups and send it all to task 0
1235 std::vector< std::set<IDType> > set_of_group_links(ntotal_minima);
1236 for(size_t i = 0; i < NumPart; i++){
1237 auto v = vs[i];
1238 auto id = v->info().WatershedID;
1239 auto & curset = set_of_group_links[id];
1240 std::vector<Vertex_handle> vertices;
1241 dt.adjacent_vertices(v, std::back_inserter(vertices));
1242 std::vector<IDType> links;
1243 for(auto &vnew : vertices){
1244 auto nborid = vnew->info().WatershedID;
1245 if(nborid != id and nborid != NoWatershedID){
1246 curset.insert(nborid); // Add to list
1247 }
1248 }
1249 }
1250
1251 // We now have a list of sets containing all the links between groups. Compile this up
1252 std::vector<int> num_links(ntotal_minima,0);
1253 std::vector<std::vector<IDType>> links(ntotal_minima);
1254 for(int i = 0; i < ntotal_minima; i++){
1255 num_links[i] = set_of_group_links[i].size();
1256 links[i].reserve(num_links[i]);
1257 for(auto val : set_of_group_links[i]){
1258 links[i].push_back(val);
1259 }
1260 std::cout << FML::ThisTask << " group " << i << " has " << num_links[i] << " neighbors\n";
1261 set_of_group_links.clear();
1262 set_of_group_links.shrink_to_fit();
1263 }
1264
1265 // We now have all the links. We should really do a communication here
1266 // to ensure we have all the same links. We typically have 10-50 links.
1267 // Now we sort ntotal_minima group ids by density and merge them. We only
1268 // keep the groups that are higher than the minimum density
1269
1270 for(int i = 0; i < ntotal_minima; i++){
1271 for(auto nborid : links){
1272 // Check if density is higher if so then merge
1273 }
1274 }
1275
1276 */
1277
1278 // Compile up struct with {id, quantity_min} for each group. Then sort by quantity_min
1279 // Now loop over this struct. index tells us the location in watershed_groups
1280 }
void info(HaloModel **hmp)
Definition: Wrapper.cpp:83
std::vector< Vertex_handle > & get_vertex_handles_regular()
PeriodicDelaunay & get_delaunay_triangulation()
Return the CGAL triangulation we have created.
std::vector< Vertex_handle > & get_vertex_handles_boundary()
get_vel constexpr double * GetPos(...)
const IDType NoWatershedID
const QuantityType Infinity
double xmax_domain
The local extent of the domain (global domain goes from 0 to 1)
Definition: Global.cpp:42
void SumOverTasks(T *value)
Inplace sum over tasks of a single value.
Definition: Global.h:147
double xmin_domain
The local extent of the domain (global domain goes from 0 to 1)
Definition: Global.cpp:41
int NTasks
Total number of MPI tasks.
Definition: Global.cpp:34

Variable Documentation

◆ Infinity

const QuantityType FML::TRIANGULATION::Infinity = std::numeric_limits<QuantityType>::infinity()

Definition at line 698 of file PeriodicDelaunay.h.

◆ NoWatershedID

const IDType FML::TRIANGULATION::NoWatershedID = std::numeric_limits<IDType>::max()

Definition at line 697 of file PeriodicDelaunay.h.