mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
1469 lines
52 KiB
C++
1469 lines
52 KiB
C++
/*=========================================================================
|
|
|
|
Copyright (c) 2007, Los Alamos National Security, LLC
|
|
|
|
All rights reserved.
|
|
|
|
Copyright 2007. Los Alamos National Security, LLC.
|
|
This software was produced under U.S. Government contract DE-AC52-06NA25396
|
|
for Los Alamos National Laboratory (LANL), which is operated by
|
|
Los Alamos National Security, LLC for the U.S. Department of Energy.
|
|
The U.S. Government has rights to use, reproduce, and distribute this software.
|
|
NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY,
|
|
EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.
|
|
If software is modified to produce derivative works, such modified software
|
|
should be clearly marked, so as not to confuse it with the version available
|
|
from LANL.
|
|
|
|
Additionally, redistribution and use in source and binary forms, with or
|
|
without modification, are permitted provided that the following conditions
|
|
are met:
|
|
- Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
- Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
- Neither the name of Los Alamos National Security, LLC, Los Alamos National
|
|
Laboratory, LANL, the U.S. Government, nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software
|
|
without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
=========================================================================*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <set>
|
|
#include <math.h>
|
|
|
|
#include "Partition.h"
|
|
#include "SubHaloFinder.h"
|
|
#include "FOFHaloProperties.h"
|
|
#include "HaloCenterFinder.h"
|
|
|
|
#include "Timings.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace cosmotk {
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SubHaloFinder uses the results of the CosmoHaloFinder to locate the
|
|
// particles belonging to each FOF halo and then to segment those particles
|
|
// into subhalos and fuzz
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
SubHaloFinder::SubHaloFinder()
|
|
{
|
|
// Get the number of processors and rank of this processor
|
|
this->numProc = Partition::getNumProc();
|
|
this->myProc = Partition::getMyProc();
|
|
|
|
this->candidateCount = 0;
|
|
this->bhTree = 0;
|
|
|
|
this->particleList = 0;
|
|
this->candidateIndx = 0;
|
|
this->subhaloCount = 0;
|
|
this->subhalos = 0;
|
|
}
|
|
|
|
SubHaloFinder::~SubHaloFinder()
|
|
{
|
|
if (this->particleList != 0) delete [] this->particleList;
|
|
if (this->candidateIndx != 0) delete [] this->candidateIndx;
|
|
if (this->bhTree != 0) delete this->bhTree;
|
|
if (this->subhaloCount != 0) delete [] this->subhaloCount;
|
|
if (this->subhalos != 0) delete [] this->subhalos;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set the parameters for algorithms
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::setParameters(
|
|
POSVEL_T avgMass,
|
|
POSVEL_T G,
|
|
POSVEL_T alpha,
|
|
POSVEL_T beta,
|
|
int minCandSize,
|
|
int numSPH,
|
|
int numClose)
|
|
{
|
|
this->particleMass = avgMass;
|
|
this->gravityConstant = G;
|
|
this->alphaFactor = 1.0 / alpha;
|
|
this->betaFactor = beta;
|
|
|
|
this->minCandidateSize = minCandSize;
|
|
|
|
this->numberOfSPHNeighbors = numSPH;
|
|
this->numberOfCloseNeighbors = numClose;
|
|
this->potentialFactor = this->particleMass * this->gravityConstant;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set the particle vectors that have already been read and which
|
|
// contain only the alive particles for this processor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::setParticles(
|
|
ID_T count,
|
|
POSVEL_T* xLocHalo,
|
|
POSVEL_T* yLocHalo,
|
|
POSVEL_T* zLocHalo,
|
|
POSVEL_T* xVelHalo,
|
|
POSVEL_T* yVelHalo,
|
|
POSVEL_T* zVelHalo,
|
|
POSVEL_T* pmass,
|
|
ID_T* id)
|
|
{
|
|
this->particleCount = count;
|
|
this->xx = xLocHalo;
|
|
this->yy = yLocHalo;
|
|
this->zz = zLocHalo;
|
|
this->vx = xVelHalo;
|
|
this->vy = yVelHalo;
|
|
this->vz = zVelHalo;
|
|
this->mass = pmass;
|
|
this->tag = id;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Find the subhalos contained in a body of particles
|
|
// This is the basic algorithm described in the HSF paper
|
|
//
|
|
// i) Calculate local density for each particle
|
|
// This is done by building a BHTree, calculating the smoothing length,
|
|
// and finally calculating a local density using SPH
|
|
//
|
|
// ii) Sort by density
|
|
// Iterate over every particle high density to low
|
|
// Find two closest already placed neighbors (higher density neighbors)
|
|
//
|
|
// a) 0 neighbors - make a new structure
|
|
//
|
|
// b) 1 neighbor - join to the neighbor structure
|
|
// 2 neighbors in same structure - join to that structure
|
|
// 2 neighbors in different structures with one being the massive
|
|
// partner of the other, join to massive partner (boundary particle)
|
|
//
|
|
// c) 2 neighbors in different structures, neither massive partner of other
|
|
// this is a saddle point (connects 2 branches together in tree)
|
|
// mark one structure as massive compared to other, or mark as same
|
|
// Calculate significance of both structures
|
|
//
|
|
// 1) If one structure is not significant COMBINE into the other
|
|
// If neither is significant COMBINE smaller into larger
|
|
//
|
|
// 2) If both are significant
|
|
// If one is alphaFactor times larger than the other
|
|
// CUT the smaller (meaning keep it but don't add new particles)
|
|
// All particles below the saddle point which have these
|
|
// 2 structures will be added to the massive partner
|
|
// If not larger consider structures the same size
|
|
// but still mark the larger as the massive partner and
|
|
// add the saddle point to the massive partner
|
|
// but both remain open so both GROW
|
|
//
|
|
// iii) Iterate over all structures created in reverse order
|
|
// structure with less than N_cut particles is considered insignificant
|
|
// and it is combined with its massive partner.
|
|
// If it does not have a massive partner (never put in tree?)
|
|
// then mark as fuzz
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::findSubHalos()
|
|
{
|
|
// Find the range of particles
|
|
POSVEL_T minLoc[DIMENSION];
|
|
POSVEL_T maxLoc[DIMENSION];
|
|
minLoc[0] = maxLoc[0] = this->xx[0];
|
|
minLoc[1] = maxLoc[1] = this->yy[0];
|
|
minLoc[2] = maxLoc[2] = this->zz[0];
|
|
|
|
for (int i = 1; i < this->particleCount; i++) {
|
|
if (minLoc[0] > this->xx[i]) minLoc[0] = this->xx[i];
|
|
if (maxLoc[0] < this->xx[i]) maxLoc[0] = this->xx[i];
|
|
if (minLoc[1] > this->yy[i]) minLoc[1] = this->yy[i];
|
|
if (maxLoc[1] < this->yy[i]) maxLoc[1] = this->yy[i];
|
|
if (minLoc[2] > this->zz[i]) minLoc[2] = this->zz[i];
|
|
if (maxLoc[2] < this->zz[i]) maxLoc[2] = this->zz[i];
|
|
}
|
|
|
|
// BHTree is constructed from halo particles
|
|
this->bhTree = new BHTree(minLoc, maxLoc,
|
|
this->particleCount,
|
|
this->xx, this->yy, this->zz, this->mass,
|
|
this->particleMass);
|
|
|
|
// Calculate smoothing length for density calculation
|
|
this->bhTree->calculateInitialSmoothingLength(this->numberOfSPHNeighbors);
|
|
|
|
// Refine smoothing length and calculate local density of particles
|
|
this->bhTree->calculateDensity(this->numberOfSPHNeighbors);
|
|
|
|
vector<SPHParticle*>& sphParticle = this->bhTree->getSPHParticle();
|
|
|
|
for (int p = 0; p < this->particleCount; p++) {
|
|
ValueInfo info;
|
|
info.value = sphParticle[p]->density;
|
|
info.particleId = p;
|
|
this->data.push_back(info);
|
|
}
|
|
sort(this->data.begin(), this->data.end(), ValueGT());
|
|
|
|
calculateSubGroups();
|
|
|
|
// Collect the totals across all nodes
|
|
int numberOfCandidates = this->candidates.size();
|
|
int cIndx = numberOfCandidates - 1;
|
|
while (cIndx >= 0 && this->candidates[cIndx]->parent == -1) {
|
|
collectAllTotals(cIndx);
|
|
cIndx--;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// Print the initial candidate tree before unbinding
|
|
cout << "CANDIDATE TREE" << endl;
|
|
cIndx = numberOfCandidates - 1;
|
|
while (cIndx >= 0 && this->candidates[cIndx]->parent == -1) {
|
|
printCandidate(cIndx, 0);
|
|
cIndx--;
|
|
}
|
|
#endif
|
|
|
|
// Move candidates that are too small to massive partner or to fuzz
|
|
// Move unattached candidates to fuzz if too small
|
|
removeSmallCandidates();
|
|
|
|
// Unbind remaining candidates
|
|
unbind();
|
|
|
|
#ifdef DEBUG
|
|
// Print the candidate tree after unbinding
|
|
cout << "POST UNBIND CANDIDATE TREE" << endl;
|
|
cIndx = numberOfCandidates - 1;
|
|
while (cIndx >= 0 && this->candidates[cIndx]->parent == -1) {
|
|
printCandidate(cIndx, 0);
|
|
cIndx--;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
// Print the candidate tree after unbinding
|
|
cout << "SUBHALO SUMMARY " << endl;
|
|
cIndx = numberOfCandidates - 1;
|
|
while (cIndx >= 0 && this->candidates[cIndx]->parent == -1) {
|
|
printSubHalo(cIndx, 0);
|
|
cIndx--;
|
|
}
|
|
#endif
|
|
|
|
// Summarize FOF halo's subhalos in the same type of structure
|
|
// suitable for running with FOFHaloProperties. Particle indices are
|
|
// relative to this FOF halo and will need actualIndx to allow all
|
|
// subhalos to be grouped together
|
|
createSubhaloStructure();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// i) Calculate the subgroups of particles which have been identified as a halo
|
|
// Particles have a local density already calculated through SPH algorithm
|
|
//
|
|
// ii) Sort all particles in decreasing order of density so that high density
|
|
// particles will be placed in candidates first.
|
|
//
|
|
// Create the following data structures:
|
|
// "candidateIndx" array which indicates which candidate a particle belongs to
|
|
// "particleList" array which through indirect addressing can locate all
|
|
// particles of a single candidate. Using this structure combined with
|
|
// the member "first" in a candidate allows quick combining of candidates
|
|
// "candidates" array of standalone candidates created
|
|
// candidates are either leaf which will contain particles or branch
|
|
// which maintain structure of a corresponding tree
|
|
// "candidates" tree will be built bottom up by joining leaf candidates into
|
|
// small branches and then into larger branches until the complete
|
|
// tree is formed. Some leaves may never be attached to the tree.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// For the first particle:
|
|
// Place it in a new candidate in the candidates list
|
|
//
|
|
// For each successive particle:
|
|
// Collect the closest "numberOfCloseNeighbors" particles (default 20)
|
|
// Of these particles see if any have a higher density than this particle
|
|
// meaning they have already been placed in candidates.
|
|
// Of these neighbors that have been placed consider the closest two only
|
|
// Record the candidate containing each neighbor
|
|
// Record the topmost branch candidate (parent) containing each neighbor
|
|
// Depending on the location of the neighbors within candidates and tree
|
|
// and if the candidate is cut or growing several actions are possible
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// a) Make a new candidate:
|
|
// If none of the close particles have been placed, make a new candidate
|
|
// and add this particle to that candidate
|
|
// Method: makeNewCandidate()
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// b) Join an existing candidate:
|
|
// If there is only one neighbor, or if there are two neighbors
|
|
// but they are members of the same candidate
|
|
// add this particle to that candidate if it has not been cut
|
|
// otherwise search the massive partners looking for one that
|
|
// is not cut and add the particle there
|
|
// Method: joinCandidate()
|
|
//
|
|
// If there are two neighbors in two different candidates which are
|
|
// already placed in the same branch in the tree (top is equal)
|
|
// If neither candidate is cut, but one is much smaller than the other
|
|
// cut the smaller candidate and set massive parter to the larger
|
|
// add this particle to the larger candidate
|
|
// If the larger candidate is not cut
|
|
// add this particle to the larger candidate
|
|
// If the larger candidate is cut and the smaller is not
|
|
// add this particle to the smaller candidate
|
|
// If both candidates are cut search the massive partners to find one
|
|
// that is not cut and add the particle there
|
|
// Method: joinCandidate()
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// c) Merge candidates in tree before joining candidate:
|
|
// If there are two neighbors placed in two candidates which are
|
|
// not yet in the tree (neither has a child or a parent) and
|
|
// If the smaller candidate is not significant (beta factor)
|
|
// or if it is much smaller than the larger (alpha factor)
|
|
// Do not alter the tree (larger remains separate)
|
|
// Combine the smaller candidate into the larger candidate
|
|
// Add saddlepoint particle to the larger
|
|
// Method: combineCandidate()
|
|
//
|
|
// Otherwise the two candidates must be merged into the tree
|
|
// with a new branch candidate being created
|
|
// Candidates can be two leaf candidates
|
|
// Candidates can be one leaf candidate and one branch candidate
|
|
// Candidates can be two branch candidates
|
|
//
|
|
// Only for leaf candidates set the massive partner
|
|
// of the smaller candidate to the larger candidate
|
|
// If smaller candidate branch is much smaller than the larger
|
|
// cut the smaller candidate and set massive partner to larger
|
|
// If larger candidate branch is much smaller than the smaller
|
|
// cut the larger candidate and set massive partner to smaller
|
|
// Add saddlepoint particle to the uncut candidate
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Make a final candidate which will hold the fuzz which are all particles
|
|
// not bound to any structure
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Note on structures:
|
|
// The "candidates" vector contains all created SubHaloCandidates.
|
|
// An instance of SubHaloCandidate holds the information needed to deduce
|
|
// the tree structure within the "candidates" vector. It has indices for the
|
|
// parent and each child candidate. It holds the index of the first
|
|
// particle member of the candidate which can be followed through the
|
|
// "particleList" array to locate all particles in the candidate.
|
|
// It has an index to the top ancestor of the candidate tree which is
|
|
// able to accept new particle members. This is needed when looking at
|
|
// the two neighbors to see if they are within the same candidate branch.
|
|
// Count of particles in the candidate as well as the count of particles
|
|
// in the entire tree above the candidate are kept.
|
|
//
|
|
// The SubHaloCandidate can actually hold particles if it was created by
|
|
// having a new particle placed in it, or it is just a "merge" candidate
|
|
// which holds structure but no actual particles
|
|
//
|
|
// The "candidateIndx" array is of particleCount Size. It indicates which
|
|
// candidate a particle is a member of. Using this and the "top" variable
|
|
// within the candidate indicates what branch that particle belongs in.
|
|
//
|
|
// The "particleList" is of particleCount size. The SubHaloCandidate points
|
|
// to the index of its first particle member and that position in the
|
|
// particleList points to the next particle in the candidate. This allows
|
|
// locating all particles in a single candidate.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::calculateSubGroups()
|
|
{
|
|
vector<SPHParticle*>& sphParticle = this->bhTree->getSPHParticle();
|
|
|
|
// Create arrays to hold the subhalo candidate particle information
|
|
this->candidateIndx = new int[this->particleCount];
|
|
this->particleList = new int[this->particleCount];
|
|
for (int p = 0; p < this->particleCount; p++) {
|
|
this->particleList[p] = -1;
|
|
this->candidateIndx[p] = -1;
|
|
}
|
|
|
|
// Put the most dense particle into the first subhalo candidate
|
|
makeNewCandidate(0);
|
|
|
|
// Iterate over the particles by decreasing density
|
|
for (ID_T p = 1; p < this->particleCount; p++) {
|
|
|
|
ID_T particleIndx = this->data[p].particleId;
|
|
|
|
// Find neighbors of particle within radius of smoothing length h
|
|
POSVEL_T pos[DIMENSION];
|
|
POSVEL_T h, rho;
|
|
|
|
pos[0] = this->xx[particleIndx];
|
|
pos[1] = this->yy[particleIndx];
|
|
pos[2] = this->zz[particleIndx];
|
|
h = sphParticle[particleIndx]->smoothingLength;
|
|
rho = sphParticle[particleIndx]->density;
|
|
|
|
vector<ValueInfo> neighborList;
|
|
set<int> possibleGroup;
|
|
set<int>::iterator siter;
|
|
|
|
this->bhTree->getClosestNeighbors(this->numberOfCloseNeighbors,
|
|
particleIndx, pos, h,
|
|
this->particleCount, neighborList);
|
|
|
|
// Find the neighbors which have already been placed in candidates
|
|
vector<ValueInfo> closeList;
|
|
for (int n = 0; n < this->numberOfCloseNeighbors; n++) {
|
|
int neighbor = neighborList[n].particleId;
|
|
if (rho < sphParticle[neighbor]->density) {
|
|
ValueInfo info;
|
|
info.particleId = neighbor;
|
|
info.value = neighborList[n].value;
|
|
closeList.push_back(info);
|
|
}
|
|
}
|
|
|
|
int numNeighbors = closeList.size();
|
|
int cand1 = -1;
|
|
int cand2 = -2;
|
|
int top1 = -1;
|
|
int top2 = -2;
|
|
|
|
// If there are two candidates make sure cand1 is the larger
|
|
// Set the candidate index and the top open candidate index
|
|
if (numNeighbors > 0) {
|
|
cand1 = this->candidateIndx[closeList[0].particleId];
|
|
top1 = this->candidates[cand1]->top;
|
|
}
|
|
if (numNeighbors > 1) {
|
|
cand2 = this->candidateIndx[closeList[1].particleId];
|
|
top2 = this->candidates[cand2]->top;
|
|
|
|
if (this->candidates[cand1]->count < this->candidates[cand2]->count) {
|
|
int tmp = cand1;
|
|
cand1 = cand2;
|
|
cand2 = tmp;
|
|
tmp = top1;
|
|
top1 = top2;
|
|
top2 = tmp;
|
|
}
|
|
}
|
|
|
|
// a) If neither close neighbor is in a candidate make a new candidate
|
|
if (numNeighbors == 0) {
|
|
makeNewCandidate(p);
|
|
}
|
|
|
|
// b) Particle has neighbors in one candidate only
|
|
// Join the particle to that candidate if it is not already cut
|
|
// Otherwise join to the more massive partner
|
|
else if (numNeighbors == 1 || cand1 == cand2) {
|
|
|
|
// Not cut so join directly to the candidate
|
|
if (this->candidates[cand1]->cut == 0) {
|
|
joinCandidate(p, cand1);
|
|
|
|
// Candidate is cut so find the massive partner that is still not cut
|
|
} else {
|
|
cand1 = this->candidates[cand1]->partner;
|
|
while (this->candidates[cand1]->cut == 1 && cand1 != -1) {
|
|
cand1 = this->candidates[cand1]->partner;
|
|
}
|
|
joinCandidate(p, cand1);
|
|
}
|
|
}
|
|
|
|
// b) Particle has neighbors in two candidates which have been merged
|
|
// into the same branch of the tree
|
|
//
|
|
// If neither candidate has been cut, test smaller against the
|
|
// larger to see if it can be cut, if so cut smaller and set
|
|
// its massive partner to the larger
|
|
// Add the particle to the larger candidate
|
|
// If it has been cut add to the smaller if it was not cut otherwise
|
|
// search the massive partners
|
|
//
|
|
else if (top1 == top2) {
|
|
|
|
// If the smaller candidate is not cut see if it should be cut now
|
|
// and set the massive partner to the larger
|
|
if (this->candidates[cand2]->cut == 0 &&
|
|
this->candidates[cand1]->cut == 0) {
|
|
if (this->candidates[cand1]->count >
|
|
(this->alphaFactor * this->candidates[cand2]->count)) {
|
|
this->candidates[cand2]->cut = 1;
|
|
this->candidates[cand2]->partner = cand1;
|
|
}
|
|
}
|
|
|
|
// If the larger is not cut add particle to larger
|
|
if (this->candidates[cand1]->cut == 0) {
|
|
joinCandidate(p, cand1);
|
|
}
|
|
|
|
// If larger is cut and smaller is not add particle to smaller
|
|
else if (this->candidates[cand2]->cut == 0) {
|
|
joinCandidate(p, cand2);
|
|
}
|
|
|
|
// If both are cut search massive partners of larger
|
|
else {
|
|
cand1 = this->candidates[cand1]->partner;
|
|
while (this->candidates[cand1]->cut == 1 && cand1 != -1) {
|
|
cand1 = this->candidates[cand1]->partner;
|
|
}
|
|
joinCandidate(p, cand1);
|
|
}
|
|
}
|
|
|
|
// Particle has neighbors in two candidates
|
|
// Absorb one candidate into the other plus the saddle point
|
|
// or merge the two candidates into a third depending on significance
|
|
else {
|
|
mergeCandidate(p, cand1, cand2, top1, top2);
|
|
}
|
|
}
|
|
|
|
// Last candidate holds the fuzz which are unbound particles
|
|
SubHaloCandidate* candidate = new SubHaloCandidate();
|
|
this->fuzz = this->candidateCount;
|
|
candidate->top = -1;
|
|
candidate->first = -1;
|
|
candidate->partner = -1;
|
|
candidate->cut = 0;
|
|
candidate->parent = -1;
|
|
candidate->child1 = -1;
|
|
candidate->child2 = -1;
|
|
candidate->count = 1;
|
|
candidate->totalCount = 0;
|
|
this->candidates.push_back(candidate);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create a new candidate with the particle
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::makeNewCandidate(int p)
|
|
{
|
|
ID_T particleIndx = this->data[p].particleId;
|
|
|
|
SubHaloCandidate* candidate = new SubHaloCandidate();
|
|
candidate->top = this->candidateCount;
|
|
candidate->first = particleIndx;
|
|
candidate->partner = -1;
|
|
candidate->cut = 0;
|
|
candidate->parent = -1;
|
|
candidate->child1 = -1;
|
|
candidate->child2 = -1;
|
|
candidate->count = 1;
|
|
candidate->totalCount = 0;
|
|
|
|
this->candidates.push_back(candidate);
|
|
candidateIndx[particleIndx] = this->candidateCount;
|
|
|
|
this->candidateCount++;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Join the particle to an existing candidate.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::joinCandidate(int p, int cand1)
|
|
{
|
|
ID_T particleIndx = this->data[p].particleId;
|
|
|
|
// Push particle in front of candidate's particles
|
|
// Increment the candidate and set candidate index for this particle
|
|
this->particleList[particleIndx] = this->candidates[cand1]->first;
|
|
this->candidates[cand1]->first = particleIndx;
|
|
this->candidates[cand1]->count++;
|
|
this->candidateIndx[particleIndx] = cand1;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Add the particle to an existing candidate
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::addParticleToCandidate(int particleIndx, int cIndx)
|
|
{
|
|
// Push particle in front of candidate's particles
|
|
// Increment the candidate and set candidate index for this particle
|
|
this->particleList[particleIndx] = this->candidates[cIndx]->first;
|
|
this->candidates[cIndx]->first = particleIndx;
|
|
this->candidates[cIndx]->count++;
|
|
this->candidateIndx[particleIndx] = cIndx;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Remove the particle from an existing candidate
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::removeParticleFromCandidate(int particleIndx, int cIndx)
|
|
{
|
|
// Is it the first particle that must be removed
|
|
int curPart = this->candidates[cIndx]->first;
|
|
int nextPart;
|
|
int nnextPart;
|
|
|
|
if (curPart != -1) {
|
|
if (curPart == particleIndx) {
|
|
nextPart = this->particleList[curPart];
|
|
this->candidates[cIndx]->first = nextPart;
|
|
this->candidates[cIndx]->count--;
|
|
} else while (curPart != -1) {
|
|
nextPart = this->particleList[curPart];
|
|
if (nextPart == particleIndx) {
|
|
nnextPart = this->particleList[nextPart];
|
|
this->particleList[curPart] = nnextPart;
|
|
this->candidates[cIndx]->count--;
|
|
curPart = -1;
|
|
}
|
|
curPart = nextPart;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Particle is a saddle point between two existing candidates
|
|
// The candidates either contain particles or are "merge" candidates
|
|
// which contain no particles of their own, but their children do
|
|
//
|
|
// Check the size and significance of smaller candidate to decide whether
|
|
// to COMBINE one candidate into the other or to merge two candidates into third
|
|
// If merged the candidates can both be allowed to GROW or one may be CUT
|
|
// meaning it takes no additional particles but will continue to exist
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::mergeCandidate(int p,
|
|
int cand1, int cand2,
|
|
int top1, int top2)
|
|
{
|
|
vector<SPHParticle*>& sphParticle = this->bhTree->getSPHParticle();
|
|
ID_T particleIndx = this->data[p].particleId;
|
|
|
|
// Is the smaller halo significant, if not absorb its particles into larger
|
|
// Calculate the average density of the small candidate's particles
|
|
int count = candidates[cand2]->count;
|
|
POSVEL_T avgDensity = 0;
|
|
int curPart = this->candidates[cand2]->first;
|
|
while (curPart != -1) {
|
|
avgDensity += sphParticle[curPart]->density;
|
|
curPart = this->particleList[curPart];
|
|
}
|
|
avgDensity /= count;
|
|
POSVEL_T saddleDensity = sphParticle[particleIndx]->density;
|
|
|
|
// Use the Poisson Noise beta parameter to decide if smaller is significant
|
|
int significant = 0;
|
|
if (avgDensity > (saddleDensity * (1.0 + betaFactor / sqrt(count))))
|
|
significant = 1;
|
|
|
|
// If the smaller candidate has no children or parent it may be absorbed
|
|
int hasChildren2 = 0;
|
|
if (candidates[cand2]->child1 != -1 || candidates[cand2]->parent != -1)
|
|
hasChildren2 = 1;
|
|
|
|
// Collect totalCounts of each candidate to decide on CUT or GROW
|
|
int totalCount1 = collectTotal(top1);
|
|
int totalCount2 = collectTotal(top2);
|
|
int cutCandidate1 = 0;
|
|
int cutCandidate2 = 0;
|
|
|
|
if (totalCount1 > (this->alphaFactor * totalCount2))
|
|
cutCandidate2 = 1;
|
|
if (totalCount2 > (this->alphaFactor * totalCount1))
|
|
cutCandidate1 = 1;
|
|
|
|
int removeCandidate2 = 0;
|
|
if (hasChildren2 == 0) {
|
|
if (significant == 0)
|
|
removeCandidate2 = 1;
|
|
else if (cutCandidate2 == 1 &&
|
|
this->candidates[cand2]->count < this->minCandidateSize)
|
|
removeCandidate2 = 1;
|
|
}
|
|
|
|
// Small candidate is significant and can't be absorbed
|
|
// c) 2) MERGE two candidates
|
|
// If one is much smaller than the other CUT it for joins
|
|
// Any particle to be joined to it should go to the massive partner
|
|
// If same size, keep both open for GROW
|
|
if (removeCandidate2 == 0) {
|
|
|
|
SubHaloCandidate* candidate = new SubHaloCandidate();
|
|
candidate->top = this->candidateCount;
|
|
candidate->first = -1;
|
|
candidate->partner = -1;
|
|
candidate->parent = -1;
|
|
candidate->child1 = top1;
|
|
candidate->child2 = top2;
|
|
candidate->count = 0;
|
|
candidate->totalCount = 0;
|
|
|
|
// Set the new candidate as the parent of the children
|
|
this->candidates[top1]->parent = this->candidateCount;
|
|
this->candidates[top2]->parent = this->candidateCount;
|
|
|
|
// Recurse through all children of children to set new top open candidate
|
|
setTopCandidate(top1, this->candidateCount);
|
|
setTopCandidate(top2, this->candidateCount);
|
|
|
|
// Collect totalCounts of each candidate to decide on CUT or GROW
|
|
this->candidates[top1]->totalCount = totalCount1;
|
|
this->candidates[top2]->totalCount = totalCount2;
|
|
|
|
// Set the smaller candidate's massive partner to the larger candidate
|
|
if (this->candidates[cand1]->count >
|
|
this->candidates[cand2]->count) {
|
|
if (this->candidates[cand2]->partner == -1)
|
|
this->candidates[cand2]->partner = cand1;
|
|
} else {
|
|
if (this->candidates[cand1]->partner == -1)
|
|
this->candidates[cand1]->partner = cand2;
|
|
}
|
|
|
|
// If one branch is smaller, cut the candidate of the other branch
|
|
int saddlePointCand = cand1;
|
|
if (cutCandidate2 == 1) {
|
|
this->candidates[cand2]->cut = 1;
|
|
this->candidates[cand2]->partner = cand1;
|
|
if (this->candidates[cand1]->cut == 0)
|
|
saddlePointCand = cand1;
|
|
else
|
|
saddlePointCand = cand2;
|
|
}
|
|
|
|
// If one branch is smaller, cut the candidate of the other branch
|
|
else if (cutCandidate1 == 1) {
|
|
this->candidates[cand1]->cut = 1;
|
|
this->candidates[cand1]->partner = cand2;
|
|
if (this->candidates[cand2]->cut == 0)
|
|
saddlePointCand = cand2;
|
|
else
|
|
saddlePointCand = cand1;
|
|
}
|
|
|
|
// Add in saddle point particle
|
|
this->particleList[particleIndx] = this->candidates[saddlePointCand]->first;
|
|
this->candidates[saddlePointCand]->first = particleIndx;
|
|
this->candidates[saddlePointCand]->count++;
|
|
this->candidateIndx[particleIndx] = saddlePointCand;
|
|
|
|
// Add new branch candidate to candidate list
|
|
this->candidates.push_back(candidate);
|
|
this->candidateCount++;
|
|
}
|
|
|
|
// c) 1) ABSORB smaller candidate into the larger candidate
|
|
else {
|
|
// If the larger candidate has already been cut join to massive partner
|
|
if (this->candidates[cand1]->cut == 1) {
|
|
cand1 = this->candidates[cand1]->partner;
|
|
}
|
|
|
|
// Move all particles in small candidate to the large candidate
|
|
combineCandidate(cand1, cand2);
|
|
|
|
// Add saddlepoint to cand1
|
|
this->particleList[particleIndx] = this->candidates[cand1]->first;
|
|
this->candidates[cand1]->first = particleIndx;
|
|
this->candidates[cand1]->count++;
|
|
this->candidateIndx[particleIndx] = cand1;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Combine one candidate into another candidate
|
|
// All particles in second candidate are moved to the first candidate
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::combineCandidate(int cand1, int cand2)
|
|
{
|
|
// Have all particles in cand2 change their candidateIndx to cand1
|
|
int curParticle = this->candidates[cand2]->first;
|
|
while (curParticle != -1) {
|
|
this->candidateIndx[curParticle] = cand1;
|
|
curParticle = this->particleList[curParticle];
|
|
}
|
|
|
|
// If the cand1 is empty fill with cand2
|
|
if (this->candidates[cand1]->first == -1) {
|
|
this->candidates[cand1]->first = this->candidates[cand2]->first;
|
|
this->candidates[cand1]->count = this->candidates[cand2]->count;
|
|
this->candidates[cand2]->count = 0;
|
|
this->candidates[cand2]->first = -1;
|
|
}
|
|
|
|
else {
|
|
// Find last particle in cand2 (points to -1)
|
|
curParticle = this->candidates[cand2]->first;
|
|
int lastParticle = curParticle;
|
|
while (curParticle != -1) {
|
|
lastParticle = curParticle;
|
|
curParticle = this->particleList[curParticle];
|
|
}
|
|
|
|
// Add cand1 list to the end of cand2 list, point to start of cand2 list
|
|
if (lastParticle > -1) {
|
|
this->particleList[lastParticle] = this->candidates[cand1]->first;
|
|
this->candidates[cand1]->first = this->candidates[cand2]->first;
|
|
this->candidates[cand1]->count += this->candidates[cand2]->count;
|
|
this->candidates[cand2]->count = 0;
|
|
this->candidates[cand2]->first = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Recursively set the enclosing candidate that is open to new particles
|
|
// This is so that when a neighbor particle is identified, the group containing
|
|
// it can be immediately known. If two neighbors are in two different
|
|
// subcandidates which were joined into a single candidate, we want to know
|
|
// they are both in the same one.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::setTopCandidate(int candidate, int top)
|
|
{
|
|
this->candidates[candidate]->top = top;
|
|
int child1 = this->candidates[candidate]->child1;
|
|
int child2 = this->candidates[candidate]->child2;
|
|
if (child1 != -1)
|
|
setTopCandidate(child1, top);
|
|
|
|
if (child2 != -1)
|
|
setTopCandidate(child2, top);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Remove candidates that are too small
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::removeSmallCandidates()
|
|
{
|
|
int numberOfCandidates = this->candidates.size();
|
|
for (int cIndx = 0; cIndx < (numberOfCandidates-1); cIndx++) {
|
|
|
|
int count = this->candidates[cIndx]->count;
|
|
int partner = this->candidates[cIndx]->partner;
|
|
|
|
// Only work with candidates with size, not the merge points
|
|
if (count > 0) {
|
|
|
|
if (count < this->minCandidateSize) {
|
|
if (partner >= 0)
|
|
combineCandidate(partner, cIndx);
|
|
else
|
|
combineCandidate(fuzz, cIndx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Unbind particles with positive total energy
|
|
// For now walk the candidate vector in reverse order building the tree
|
|
// Possibly more than one root because not all candidates have to join
|
|
// in the end.
|
|
//
|
|
// total_energy = kinetic_energy + potential_energy
|
|
//
|
|
// kinetic_energy = (mass * velocity * velocity) / 2 always positive
|
|
// potential_energy = mass * potential always negative
|
|
//
|
|
// positive total_energy means the particle is able to escape
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::unbind()
|
|
{
|
|
int rootIndx = this->candidates.size() - 2;
|
|
int cIndx = rootIndx;
|
|
while (cIndx >= 0 && this->candidates[cIndx]->parent == -1) {
|
|
unbindCandidate(cIndx);
|
|
cIndx--;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Recursive method to count bound particles on every candidate in tree
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::unbindCandidate(int cIndx)
|
|
{
|
|
int child1 = this->candidates[cIndx]->child1;
|
|
int child2 = this->candidates[cIndx]->child2;
|
|
int count = this->candidates[cIndx]->count;
|
|
|
|
// Unbind particles in the candidate
|
|
if (count > 0) {
|
|
unbindParticles(cIndx);
|
|
}
|
|
|
|
if (child1 != -1 && child2 != -1) {
|
|
if (this->candidates[child1]->totalCount >
|
|
this->candidates[child2]->totalCount) {
|
|
unbindCandidate(child2);
|
|
unbindCandidate(child1);
|
|
} else {
|
|
unbindCandidate(child1);
|
|
unbindCandidate(child2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Recursive method to print the subhalo candidate tree with counts
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::printCandidate(int cIndx, int indent)
|
|
{
|
|
cout << setw(7) << indent << ": "
|
|
<< "Candidate " << setw(7) << cIndx
|
|
<< " total " << setw(8) << this->candidates[cIndx]->totalCount
|
|
<< " count " << setw(8) << this->candidates[cIndx]->count
|
|
<< " parent " << setw(7) << this->candidates[cIndx]->parent
|
|
<< " child1 " << setw(7) << this->candidates[cIndx]->child1
|
|
<< " child2 " << setw(7) << this->candidates[cIndx]->child2
|
|
<< " partner " << setw(7) << this->candidates[cIndx]->partner
|
|
<< " cut " << setw(3) << this->candidates[cIndx]->cut
|
|
<< " subhalos " << endl;
|
|
|
|
if (this->candidates[cIndx]->child1 != -1)
|
|
printCandidate(this->candidates[cIndx]->child1, indent + 1);
|
|
|
|
if (this->candidates[cIndx]->child2 != -1)
|
|
printCandidate(this->candidates[cIndx]->child2, indent + 1);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Recursive method to print the subhalo candidate tree after unbinding
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::printSubHalo(int cIndx, int indent)
|
|
{
|
|
if (this->candidates[cIndx]->count > 0) {
|
|
cout << setw(7) << indent << ": "
|
|
<< "Subhalo " << setw(7) << cIndx
|
|
<< " total " << setw(8) << this->candidates[cIndx]->totalCount
|
|
<< " count " << setw(8) << this->candidates[cIndx]->count
|
|
<< " parent " << setw(7) << this->candidates[cIndx]->parent
|
|
<< " child1 " << setw(7) << this->candidates[cIndx]->child1
|
|
<< " child2 " << setw(7) << this->candidates[cIndx]->child2
|
|
<< " partner " << setw(7) << this->candidates[cIndx]->partner
|
|
<< " cut " << setw(3) << this->candidates[cIndx]->cut << endl;
|
|
}
|
|
|
|
if (this->candidates[cIndx]->child1 != -1)
|
|
printSubHalo(this->candidates[cIndx]->child1, indent + 1);
|
|
|
|
if (this->candidates[cIndx]->child2 != -1)
|
|
printSubHalo(this->candidates[cIndx]->child2, indent + 1);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Recursive method to gather the total counts for the subhalo candidates
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
int SubHaloFinder::collectTotal(int cIndx)
|
|
{
|
|
if (this->candidates[cIndx]->child1 == -1)
|
|
return this->candidates[cIndx]->count;
|
|
|
|
int totalChild1 = collectTotal(this->candidates[cIndx]->child1);
|
|
int totalChild2 = collectTotal(this->candidates[cIndx]->child2);
|
|
|
|
return (totalChild1 + totalChild2);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Recursive method to gather the total counts for the subhalo candidates
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
int SubHaloFinder::collectAllTotals(int cIndx)
|
|
{
|
|
int totalChild1 = 0;
|
|
int totalChild2 = 0;
|
|
|
|
if (this->candidates[cIndx]->child1 != -1)
|
|
totalChild1 = collectAllTotals(this->candidates[cIndx]->child1);
|
|
if (this->candidates[cIndx]->child2 != -1)
|
|
totalChild2 = collectAllTotals(this->candidates[cIndx]->child2);
|
|
|
|
this->candidates[cIndx]->totalCount = this->candidates[cIndx]->count +
|
|
totalChild1 + totalChild2;
|
|
|
|
return this->candidates[cIndx]->totalCount;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Count the number of bound particles in a candidate with no children
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::unbindParticles(int cIndx)
|
|
{
|
|
int numberOfParticles = this->candidates[cIndx]->count;
|
|
int numberLeft = numberOfParticles;
|
|
|
|
int massivePartner = this->candidates[cIndx]->partner;
|
|
|
|
POTENTIAL_T* lpot = new POTENTIAL_T[numberOfParticles];
|
|
int* valid = new int[numberOfParticles];
|
|
int* id = new int[numberOfParticles];
|
|
POSVEL_T* xLoc = new POSVEL_T[numberOfParticles];
|
|
POSVEL_T* yLoc = new POSVEL_T[numberOfParticles];
|
|
POSVEL_T* zLoc = new POSVEL_T[numberOfParticles];
|
|
POSVEL_T* xVel = new POSVEL_T[numberOfParticles];
|
|
POSVEL_T* yVel = new POSVEL_T[numberOfParticles];
|
|
POSVEL_T* zVel = new POSVEL_T[numberOfParticles];
|
|
POSVEL_T* MASS = new POSVEL_T[numberOfParticles];
|
|
|
|
// Store the location and velocity information in arrays
|
|
int p = this->candidates[cIndx]->first;
|
|
int indx = 0;
|
|
while (p != -1) {
|
|
xLoc[indx] = this->xx[p];
|
|
yLoc[indx] = this->yy[p];
|
|
zLoc[indx] = this->zz[p];
|
|
xVel[indx] = this->vx[p];
|
|
yVel[indx] = this->vy[p];
|
|
zVel[indx] = this->vz[p];
|
|
MASS[indx] = this->mass[p];
|
|
valid[indx] = 1;
|
|
id[indx] = p;
|
|
p = this->particleList[p];
|
|
indx++;
|
|
}
|
|
|
|
int bindDone = 0;
|
|
if (numberLeft > MAX_UNBIND_3)
|
|
bindDone = 1;
|
|
|
|
while (numberLeft >= this->minCandidateSize && bindDone == 0) {
|
|
|
|
vector<ValueInfo>* totalEnergy = new vector<ValueInfo>[numberLeft];
|
|
|
|
// Calculate the average velocity of the body of particles
|
|
POSVEL_T xAvg = 0.0;
|
|
POSVEL_T yAvg = 0.0;
|
|
POSVEL_T zAvg = 0.0;
|
|
for (int i = 0; i < numberOfParticles; i++) {
|
|
if (valid[i] == 1) {
|
|
xAvg += xVel[i];
|
|
yAvg += yVel[i];
|
|
zAvg += zVel[i];
|
|
}
|
|
}
|
|
xAvg /= numberLeft;
|
|
yAvg /= numberLeft;
|
|
zAvg /= numberLeft;
|
|
|
|
// Calculate the potential of each particle within the body of particles
|
|
for (int i = 0; i < numberOfParticles; i++)
|
|
lpot[i] = 0.0;
|
|
|
|
// First particle in halo to calculate minimum potential on
|
|
for (int i = 0; i < numberOfParticles; i++) {
|
|
|
|
// Next particle in halo in minimum potential loop
|
|
for (int j = i+1; j < numberOfParticles; j++) {
|
|
|
|
if (valid[i] == 1 && valid[j] == 1) {
|
|
POSVEL_T xdist = (POSVEL_T) fabs(xLoc[i] - xLoc[j]);
|
|
POSVEL_T ydist = (POSVEL_T) fabs(yLoc[i] - yLoc[j]);
|
|
POSVEL_T zdist = (POSVEL_T) fabs(zLoc[i] - zLoc[j]);
|
|
|
|
POSVEL_T r = sqrt((xdist*xdist) + (ydist*ydist) + (zdist*zdist));
|
|
|
|
if (r != 0.0) {
|
|
lpot[i] = (POTENTIAL_T)(lpot[i] - (1.0 / r));
|
|
lpot[j] = (POTENTIAL_T)(lpot[j] - (1.0 / r));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate total_energy = kinetic_energy + potential+energy
|
|
int positiveTECount = 0;
|
|
for (int i = 0; i < numberOfParticles; i++) {
|
|
if (valid[i] == 1) {
|
|
POSVEL_T XVel = xVel[i] - xAvg;
|
|
POSVEL_T YVel = yVel[i] - yAvg;
|
|
POSVEL_T ZVel = zVel[i] - zAvg;
|
|
|
|
POSVEL_T v = sqrt(XVel * XVel + YVel * YVel + ZVel * ZVel);
|
|
POSVEL_T kineticEnergy = (v * v) / 2.0;
|
|
POSVEL_T potentialEnergy = lpot[i] * this->potentialFactor;
|
|
POSVEL_T totEnergy = kineticEnergy + potentialEnergy;
|
|
|
|
if (totEnergy > 0.0) {
|
|
positiveTECount++;
|
|
ValueInfo info;
|
|
info.particleId = i;
|
|
info.value = totEnergy;
|
|
(*totalEnergy).push_back(info);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort the positive total energy along with particle index if any
|
|
// Mark as invalid the top 10% of positive total energy particles
|
|
if (positiveTECount > 0) {
|
|
sort(totalEnergy->begin(), totalEnergy->end(), ValueGT());
|
|
|
|
int maxToDelete = 1;
|
|
if (numberLeft > MAX_UNBIND_1 && numberLeft < MAX_UNBIND_2)
|
|
maxToDelete = (positiveTECount / FACTOR_UNBIND_1) + 1;
|
|
else if (numberLeft >= MAX_UNBIND_2 && numberLeft < MAX_UNBIND_3)
|
|
maxToDelete = (positiveTECount / FACTOR_UNBIND_2) + 1;
|
|
#ifdef DEBUG
|
|
cout << "Unbind at most " << maxToDelete
|
|
<< " particles numberLeft " << numberLeft
|
|
<< " numberOfParticles " << numberOfParticles
|
|
<< " total energy " << (*totalEnergy)[maxToDelete - 1].value << endl;
|
|
#endif
|
|
|
|
for (int i = 0; i < maxToDelete; i++) {
|
|
int pid = (*totalEnergy)[i].particleId;
|
|
valid[pid] = 0;
|
|
}
|
|
numberLeft -= maxToDelete;
|
|
|
|
// If we are almost done and the halo is large enough just quit
|
|
if (numberLeft > MAX_UNBIND_2 && maxToDelete <= MAX_UNBIND_DELETE)
|
|
bindDone = 1;
|
|
|
|
} else {
|
|
bindDone = 1;
|
|
}
|
|
|
|
totalEnergy->clear();
|
|
}
|
|
|
|
// Discard any unbound particles
|
|
int numberOfBoundParticles = numberLeft;
|
|
|
|
// Not enough bound particles so move all to the massive partner
|
|
if (numberOfBoundParticles < this->minCandidateSize) {
|
|
#ifdef DEBUG
|
|
cout << "\tDISCARD move to partner " << massivePartner
|
|
<< " number of particles " << numberOfBoundParticles << endl;
|
|
#endif
|
|
if (massivePartner >= 0)
|
|
combineCandidate(massivePartner, cIndx);
|
|
else
|
|
combineCandidate(this->fuzz, cIndx);
|
|
}
|
|
|
|
// Enough bound particles for subhalo, move only the unbound particles
|
|
else {
|
|
#ifdef DEBUG
|
|
cout << "UNBIND " << numberOfBoundParticles
|
|
<< " particles to massive partner " << massivePartner << endl;
|
|
#endif
|
|
for (int i = 0; i < numberOfParticles; i++) {
|
|
if (valid[i] == 0) {
|
|
removeParticleFromCandidate(id[i], cIndx);
|
|
|
|
if (massivePartner != -1) {
|
|
addParticleToCandidate(id[i], massivePartner);
|
|
} else {
|
|
addParticleToCandidate(id[i], this->fuzz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete [] valid;
|
|
delete [] id;
|
|
delete [] xLoc;
|
|
delete [] yLoc;
|
|
delete [] zLoc;
|
|
delete [] xVel;
|
|
delete [] yVel;
|
|
delete [] zVel;
|
|
delete [] MASS;
|
|
delete [] lpot;
|
|
|
|
#ifdef DEBUG
|
|
cout << "UNBIND CANDIDATE " << setw(7) << cIndx
|
|
<< " totalcount " << setw(8) << this->candidates[cIndx]->totalCount
|
|
<< " count " << setw(8) << this->candidates[cIndx]->count
|
|
<< " partner " << setw(8) << massivePartner
|
|
<< " cut " << this->candidates[cIndx]->cut
|
|
<< " #bound " << numberLeft << endl;
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Write a .cosmo file for ParaView with location, velocity and subhalo
|
|
// candidate number in place of the mass variable
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::writeSubhaloCosmoFile(const string& outFile)
|
|
{
|
|
// Collect the candidates with particles for sorting and remapping
|
|
// Fuzz is the last candidate
|
|
int numberOfCandidates = this->candidates.size();
|
|
vector<ValueInfo> groups;
|
|
|
|
this->numberOfSubhalos = 0;
|
|
for (int i = 0; i < (numberOfCandidates-1); i++) {
|
|
if (this->candidates[i]->count > 0) {
|
|
this->numberOfSubhalos++;
|
|
ValueInfo info;
|
|
info.particleId = i;
|
|
info.value = (float) this->candidates[i]->count;
|
|
groups.push_back(info);
|
|
}
|
|
}
|
|
|
|
// Sort the valid subhalo candidates (not the fuzz candidate) by size
|
|
sort(groups.begin(), groups.end(), ValueGT());
|
|
|
|
// Map the actual candidate index into an index based on size
|
|
int* mapCandidate = new int[numberOfCandidates];
|
|
for (int i = 0; i < numberOfCandidates; i++)
|
|
mapCandidate[i] = -1;
|
|
|
|
for (int i = 0; i < this->numberOfSubhalos; i++) {
|
|
int oldCandidate = groups[i].particleId;
|
|
mapCandidate[oldCandidate] = i;
|
|
#ifdef DEBUG
|
|
cout << "Map candidate " << setw(7) << groups[i].particleId
|
|
<< " to subhalo " << setw(7) << i
|
|
<< " with count " << setw(10) << groups[i].value << endl;
|
|
#endif
|
|
}
|
|
|
|
// Set the fuzz candidate to the last subhalo
|
|
mapCandidate[fuzz] = numberOfSubhalos;
|
|
#ifdef DEBUG
|
|
cout << "Map fuzz candidate " << fuzz
|
|
<< " to candidate " << numberOfSubhalos
|
|
<< " with count " << this->candidates[fuzz]->count << endl;
|
|
#endif
|
|
|
|
// Write the particles with mapped candidate numbers
|
|
ofstream cStream(outFile.c_str(), ios::out|ios::binary);
|
|
|
|
float fBlock[COSMO_FLOAT];
|
|
int iBlock[COSMO_INT];
|
|
|
|
for (int p = 0; p < this->particleCount; p++) {
|
|
fBlock[0] = this->xx[p];
|
|
fBlock[1] = this->vx[p];
|
|
fBlock[2] = this->yy[p];
|
|
fBlock[3] = this->vy[p];
|
|
fBlock[4] = this->zz[p];
|
|
fBlock[5] = this->vz[p];
|
|
fBlock[6] = (float) mapCandidate[this->candidateIndx[p]];
|
|
cStream.write(reinterpret_cast<char*>(fBlock),
|
|
COSMO_FLOAT * sizeof(POSVEL_T));
|
|
iBlock[0] = this->tag[p];
|
|
cStream.write(reinterpret_cast<char*>(iBlock),
|
|
COSMO_INT * sizeof(ID_T));
|
|
}
|
|
cStream.close();
|
|
delete [] mapCandidate;
|
|
}
|
|
|
|
void SubHaloFinder::getSubhaloCosmoData(
|
|
ID_T hid,
|
|
std::vector<POSVEL_T> &shX,
|
|
std::vector<POSVEL_T> &shY,
|
|
std::vector<POSVEL_T> &shZ,
|
|
std::vector<POSVEL_T> &shVX,
|
|
std::vector<POSVEL_T> &shVY,
|
|
std::vector<POSVEL_T> &shVZ,
|
|
std::vector<ID_T> &shTag,
|
|
std::vector<ID_T> &shHID,
|
|
std::vector<ID_T> &shID) {
|
|
// Collect the candidates with particles for sorting and remapping
|
|
// Fuzz is the last candidate
|
|
int numberOfCandidates = this->candidates.size();
|
|
vector<ValueInfo> groups;
|
|
|
|
this->numberOfSubhalos = 0;
|
|
for (int i = 0; i < (numberOfCandidates-1); i++) {
|
|
if (this->candidates[i]->count > 0) {
|
|
this->numberOfSubhalos++;
|
|
ValueInfo info;
|
|
info.particleId = i;
|
|
info.value = (float) this->candidates[i]->count;
|
|
groups.push_back(info);
|
|
}
|
|
}
|
|
|
|
// Sort the valid subhalo candidates (not the fuzz candidate) by size
|
|
sort(groups.begin(), groups.end(), ValueGT());
|
|
|
|
// Map the actual candidate index into an index based on size
|
|
int* mapCandidate = new int[numberOfCandidates];
|
|
for (int i = 0; i < numberOfCandidates; i++)
|
|
mapCandidate[i] = -1;
|
|
|
|
for (int i = 0; i < this->numberOfSubhalos; i++) {
|
|
int oldCandidate = groups[i].particleId;
|
|
mapCandidate[oldCandidate] = i;
|
|
#ifdef DEBUG
|
|
cout << "Map candidate " << setw(7) << groups[i].particleId
|
|
<< " to subhalo " << setw(7) << i
|
|
<< " with count " << setw(10) << groups[i].value << endl;
|
|
#endif
|
|
}
|
|
|
|
// Set the fuzz candidate to the last subhalo
|
|
mapCandidate[fuzz] = numberOfSubhalos;
|
|
#ifdef DEBUG
|
|
cout << "Map fuzz candidate " << fuzz
|
|
<< " to candidate " << numberOfSubhalos
|
|
<< " with count " << this->candidates[fuzz]->count << endl;
|
|
#endif
|
|
|
|
for (int p = 0; p < this->particleCount; p++) {
|
|
shX.push_back(this->xx[p]);
|
|
shY.push_back(this->yy[p]);
|
|
shZ.push_back(this->zz[p]);
|
|
shVX.push_back(this->vx[p]);
|
|
shVY.push_back(this->vy[p]);
|
|
shVZ.push_back(this->vz[p]);
|
|
shTag.push_back(this->tag[p]);
|
|
shHID.push_back(hid);
|
|
shID.push_back(mapCandidate[this->candidateIndx[p]]);
|
|
}
|
|
delete [] mapCandidate;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Gather candidates that were legal subhalos, renumber starting with 0
|
|
// for the largest subhalo, update particleIndx to correspond to the
|
|
// found subhalos. This can be returned such that FOFHaloProperties can
|
|
// run on each FOF halo's subhalos. Also we should be able to put the
|
|
// original FOF halos that were too small together with the subhalos of
|
|
// other FOF halos into a single structure for display or analysis.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SubHaloFinder::createSubhaloStructure()
|
|
{
|
|
// Collect the candidates with particles for sorting and remapping
|
|
// Fuzz is the last candidate and is ignored
|
|
int numberOfCandidates = this->candidates.size() - 1;
|
|
vector<ValueInfo> groups;
|
|
|
|
this->numberOfSubhalos = 0;
|
|
for (int i = 0; i < numberOfCandidates; i++) {
|
|
if (this->candidates[i]->count > 0) {
|
|
this->numberOfSubhalos++;
|
|
ValueInfo info;
|
|
info.particleId = i;
|
|
info.value = (float) this->candidates[i]->count;
|
|
groups.push_back(info);
|
|
}
|
|
}
|
|
|
|
// Sort the valid subhalo candidates (not the fuzz candidate) by size
|
|
sort(groups.begin(), groups.end(), ValueGT());
|
|
|
|
// Map the actual candidate index into an index based on size
|
|
int* mapCandidate = new int[numberOfCandidates];
|
|
for (int i = 0; i < numberOfCandidates; i++)
|
|
mapCandidate[i] = -1;
|
|
|
|
for (int i = 0; i < this->numberOfSubhalos; i++) {
|
|
int oldCandidate = groups[i].particleId;
|
|
mapCandidate[oldCandidate] = i;
|
|
}
|
|
|
|
// Allocate structures similar to FOF structures for returning answer
|
|
this->subhaloCount = new int[this->numberOfSubhalos];
|
|
this->subhalos = new int[this->numberOfSubhalos];
|
|
|
|
// Need array of counts of particles within each subhalo
|
|
for (int cindx = 0; cindx < numberOfCandidates; cindx++) {
|
|
int haloIndx = mapCandidate[cindx];
|
|
if (this->candidates[cindx]->count > 0) {
|
|
this->subhaloCount[haloIndx] = this->candidates[cindx]->count;
|
|
this->subhalos[haloIndx] = this->candidates[cindx]->first;
|
|
}
|
|
}
|
|
|
|
// Equivalent of haloList from FOF is the particleList which through
|
|
// indirect addressing locates every particle in a subhalo
|
|
delete [] mapCandidate;
|
|
}
|
|
|
|
}
|