/*! \file System.cxx * \brief This file implements functions of \ref NBody::System class */ #include using namespace std; using namespace Math; namespace NBody { /*----------------------- System functions -----------------------*/ // Constructors System::System(Int_t num, Double_t t, Double_t *p, Int_t ng, Int_t ns) { time = t; numparts = num; ngas=ng; nstar=ns; ndark=numparts-ngas-nstar; particle = new Particle[ndark]; if (p!=NULL) period=p; else for (int i=0.;i<3;i++)period[i]=0.; if (ng>0) gparticle = new GasParticle[ng]; if (ns>0) sparticle = new StarParticle[ns]; } System::System(Int_t num, Particle *P, Double_t t, Double_t *p, Int_t ng, Int_t ns, GasParticle *gp, StarParticle *sp) { time = t; numparts = num; ngas=ng; nstar=ns; ndark=numparts-ngas-nstar; particle = P; if (p!=NULL) period=p; else for (int i=0.;i<3;i++)period[i]=0.; if (ng>0) gparticle = gp; if (ns>0) sparticle = sp; } // Copy constructor System::System(const System &s) { numparts = s.numparts; time = s.time; period = s.period; ndark = s.ndark; ngas = s.ngas; nstar = s.nstar; particle = new Particle[ndark]; for (Int_t i = 0; i < ndark; i++) particle[i] = s.particle[i]; if (ngas>0) { gparticle = new GasParticle[ngas]; for (Int_t i = 0; i < ngas; i++) gparticle[i] = s.gparticle[i]; } if (nstar>0) { sparticle = new StarParticle[nstar]; for (Int_t i = 0; i < nstar; i++) sparticle[i] = s.sparticle[i]; } } // OTHER FUNCTIONS //Properties of the system Double_t System::TotalMass() const { Double_t tm=0; for (Int_t i=0;ifabs(Part(i).Y())) { if (fabs(Part(i).X())>fabs(Part(i).Z())) temp=fabs(Part(i).X()); else temp=fabs(Part(i).Z()); } else if (fabs(Part(i).Y())>fabs(Part(i).Z())) { temp=fabs(Part(i).Y()); } else { temp=fabs(Part(i).Z()); } if(lr)r=temp; } } else { r=particle[0].Radius(); for (Int_t i=1;ir)r=temp; } } return r; } Double_t System::MinRadius(bool cmframe) const { Double_t temp, r; if (!cmframe) { System S(*this); S.AdjustForCM(); r=S[0].Radius(); for (Int_t i=1;ir[2])r[2]=temp; r[1]+=temp*S[i].GetMass(); mtot+=S[i].GetMass(); } #else Int_t i; #pragma omp parallel default(shared) \ private(i) { #pragma omp for reduction(+:averad,mtot) for (i=1;imaxa[omp_get_thread_num()])maxa[omp_get_thread_num()]=temp; } } for (i = 0; i < nthreads; i++) { if (mina[i] < r[0]) r[0] = mina[i]; if (maxa[i] > r[2]) r[2] = maxa[i]; } delete[] mina; delete[] maxa; #endif } else { r[0]=particle[0].Radius(); r[1]=r[0]*particle[0].GetMass(); r[2]=r[0]; averad=r[1]; mtot=particle[0].GetMass(); #ifndef USEOPENMP for (Int_t i=1;ir[2])r[2]=temp; r[1]+=temp*particle[i].GetMass(); mtot+=particle[i].GetMass(); } #else Int_t i; #pragma omp parallel default(shared) \ private(i) { #pragma omp for reduction(+:averad,mtot) for (i=1;imaxa[omp_get_thread_num()])maxa[omp_get_thread_num()]=temp; } } for (i = 0; i < nthreads; i++) { if (mina[i] < r[0]) r[0] = mina[i]; if (maxa[i] > r[2]) r[2] = maxa[i]; } delete[] mina; delete[] maxa; #endif } r[1]/=mtot; return r; } //returns the mass weighted average distance from the CM Double_t System::AverageRadius(bool cmframe) const { Double_t r=0; Int_t i; if (!cmframe) { System S(*this); S.AdjustForCM(); #ifdef USEOPENMP #pragma omp parallel default(shared) \ private(i) { #pragma omp for reduction(+:r) #endif for (i=0;i 1) { // partition inline Particle swap = particle[start + (n-1)/2]; // "middle" element particle[start + (n-1)/2] = particle[start]; particle[start] = swap; Particle pivot = particle[start]; Double_t pivot_rad = pivot.Radius(); int too_big_index = start + 1; int too_small_index = start + n - 1; while (too_big_index <= too_small_index) { while (particle[too_big_index].Radius() <= pivot_rad && too_big_index < start + n) too_big_index++; while (particle[too_small_index].Radius() > pivot_rad) too_small_index--; if (too_big_index < too_small_index) { swap = particle[too_big_index]; particle[too_big_index] = particle[too_small_index]; particle[too_small_index] = swap; } } particle[start] = particle[too_small_index]; particle[too_small_index] = pivot; pivot_index = too_small_index; // end inline partition n1 = pivot_index - start; n2 = n - n1 - 1; SortByRadius(start, n1); SortByRadius(pivot_index + 1, n2); } } */ // Sort particles in system by distance to a postion void System::SortByDistance(Coordinate c) { this->SortByDistance(c,0,numparts); //qsort(particle, numparts, sizeof(Particle), SystemCompare); } //is a quick sort. Eventually need to template this into //a library of useful template sorts and other useful generic routines. void System::SortByDistance(Coordinate c, int start, int n) { int pivot_index; int n1, n2; if (n > 1) { // partition inline Particle swap = particle[start + (n-1)/2]; // "middle" element particle[start + (n-1)/2] = particle[start]; particle[start] = swap; Particle pivot = particle[start]; Double_t pivot_dist = sqrt(pow(pivot.X()-c[0],(Double_t)2.0)+pow(pivot.Y()-c[1],(Double_t)2.0)+pow(pivot.Z()-c[2],(Double_t)2.0)); //cout < pivot_dist) { too_small_index--; dist=sqrt(pow(particle[too_small_index].X()-c[0],(Double_t)2.0)+pow(particle[too_small_index].Y()-c[1],(Double_t)2.0)+pow(particle[too_small_index].Z()-c[2],(Double_t)2.0)); } if (too_big_index < too_small_index) { swap = particle[too_big_index]; particle[too_big_index] = particle[too_small_index]; particle[too_small_index] = swap; } } particle[start] = particle[too_small_index]; particle[too_small_index] = pivot; pivot_index = too_small_index; // end inline partition n1 = pivot_index - start; n2 = n - n1 - 1; SortByDistance(c,start, n1); SortByDistance(c,pivot_index + 1, n2); } } void System::SortByEnergy(Double_t G, Double_t eps) { SortByEnergy(G,eps,0,numparts); //qsort(particle, numparts, sizeof(Particle), SystemCompare); } //is a quick sort. Eventually need to template this into //a library of useful template sorts and other useful generic routines. void System::SortByEnergy(Double_t G, Double_t eps, int start, int n) { int pivot_index; int n1, n2; if (n > 1) { // partition inline Particle swap = particle[start + (n-1)/2]; // "middle" element particle[start + (n-1)/2] = particle[start]; particle[start] = swap; Particle pivot = particle[start]; Double_t pivot_E = this->KineticEnergy(start)+G*(this->PotentialEnergy(start,eps)); int too_big_index = start + 1; int too_small_index = start + n - 1; while (too_big_index <= too_small_index) { while ((this->KineticEnergy(too_big_index)+G*(this->PotentialEnergy(too_big_index,eps)) <= pivot_E) && (too_big_index < start + n)) too_big_index++; while (this->KineticEnergy(too_small_index)+G*(this->PotentialEnergy(too_small_index,eps)) > pivot_E) too_small_index--; if (too_big_index < too_small_index) { swap = particle[too_big_index]; particle[too_big_index] = particle[too_small_index]; particle[too_small_index] = swap; } } particle[start] = particle[too_small_index]; particle[too_small_index] = pivot; pivot_index = too_small_index; // end inline partition n1 = pivot_index - start; n2 = n - n1 - 1; SortByEnergy(G,eps, start, n1); SortByEnergy(G,eps, pivot_index + 1, n2); } //for (Int_t i=0;iKineticEnergy(i)+G*(this->PotentialEnergy(i,eps)) ; } // Sort particles in system by radius void System::SortByDensity() { SortByDensity(0,numparts); } //is a quick sort. Eventually need to template this into //a library of useful template sorts and other useful generic routines. void System::SortByDensity(int start, int n) { int pivot_index; int n1, n2; if (n > 1) { // partition inline Particle swap = particle[start + (n-1)/2]; // "middle" element particle[start + (n-1)/2] = particle[start]; particle[start] = swap; Particle pivot = particle[start]; Double_t pivot_den = pivot.GetDensity(); int too_big_index = start + 1; int too_small_index = start + n - 1; while (too_big_index <= too_small_index) { while (particle[too_big_index].GetDensity() <= pivot_den && too_big_index < start + n) too_big_index++; while (particle[too_small_index].GetDensity() > pivot_den) too_small_index--; if (too_big_index < too_small_index) { swap = particle[too_big_index]; particle[too_big_index] = particle[too_small_index]; particle[too_small_index] = swap; } } particle[start] = particle[too_small_index]; particle[too_small_index] = pivot; pivot_index = too_small_index; // end inline partition n1 = pivot_index - start; n2 = n - n1 - 1; SortByDensity(start, n1); SortByDensity(pivot_index + 1, n2); } } //CM and reference frame functions // Subtract centre of mass coords ... needs testing, and // need to move to arrays rather than vectors Coord System::CM(Double_t tolerance) const { Coord Rcm = {0, 0, 0}; Double_t TotalMass = 0.0; // Get centre of mass coords for (Int_t i = 0; i < numparts; i++) { for (int j = 0; j < 3; j++) Rcm.pos[j] += (particle[i].GetMass() * particle[i].GetPosition(j)); TotalMass += particle[i].GetMass(); } Double_t temp =1.0/TotalMass; for (int j = 0; j < 3; j++) Rcm.pos[j] *= temp; //if tolerance > 0 us interative technique. if (tolerance > 0.0) { // iterate to get a better c/m Coord Rcm_last = {Rcm.pos[0], Rcm.pos[1], Rcm.pos[2]}; // find maximum radius of system centered on c/m Double_t x = particle[0].X() - Rcm.pos[0]; Double_t y = particle[0].Y() - Rcm.pos[1]; Double_t z = particle[0].Z() - Rcm.pos[2]; Double_t rmax = sqrt(x * x + y * y + z * z); for (Int_t i = 1; i < numparts; i++) { x = particle[i].X() - Rcm.pos[0]; y = particle[i].Y() - Rcm.pos[1]; z = particle[i].Z() - Rcm.pos[2]; Double_t test = sqrt(x * x + y * y + z * z); if (test > rmax) rmax = test; } Double_t ri = rmax; bool done = false; while (!done) { ri = 0.9 * ri; Double_t change = 1e32; while (change > tolerance) { // find c/m of all particles within ri Int_t Ninside = 0; Double_t EncMass = 0.0; for (int k = 0; k < 3; k++) Rcm.pos[k]=0.; for (Int_t j = 0; j < numparts; j++) { x = particle[j].X() - Rcm_last.pos[0]; y = particle[j].Y() - Rcm_last.pos[1]; z = particle[j].Z() - Rcm_last.pos[2]; if (sqrt(x*x + y*y + z*z) <= ri) { for (int k = 0; k < 3; k++) Rcm.pos[k] += particle[j].GetMass() * particle[j].GetPosition(k); EncMass += particle[j].GetMass(); Ninside++; } } for (int k = 0; k < 3; k++) Rcm.pos[k] /= EncMass; // keep making radius smaller until there's // less than 1% of the particle inside if (EncMass < 0.01 * TotalMass) {done = true;break;} // check for convergence change = fabs(Rcm.pos[0] - Rcm_last.pos[0]); for (int k = 1; k < 3; k++) if (fabs(Rcm.pos[k]-Rcm_last.pos[k])>change) change=fabs(Rcm.pos[k]-Rcm_last.pos[k]); for (int k = 0; k < 3; k++) Rcm_last.pos[k] = Rcm.pos[k]; } } } return Rcm; } // Subtract centre of mass coords // need to optimize this void System::AdjustForCM(Double_t tolerance) { Coord Rcm=CM(tolerance); // Adjust particle positions for (Int_t i = 0; i < numparts; i++) particle[i].SetPosition(particle[i].GetPosition(0) - Rcm.pos[0], particle[i].GetPosition(1) - Rcm.pos[1], particle[i].GetPosition(2) - Rcm.pos[2]); } //returns velocity of centre of mass. Coord System::CMVel(Double_t tolerance) const { Coord Vcm = {0, 0, 0}; Double_t TotalMass = 0.0; // Get centre of mass coords for (Int_t i = 0; i < numparts; i++) { for (int j = 0; j < 3; j++) Vcm.pos[j] += (particle[i].GetMass() * particle[i].GetVelocity(j)); TotalMass += particle[i].GetMass(); } Double_t temp =1.0/TotalMass; for (int j = 0; j < 3; j++) Vcm.pos[j]*= temp; return Vcm; } // Subtract centre of mass velocity... needs testing, and // need to move to arrays rather than vectors void System::AdjustForCMVel(Double_t tolerance) { Coord Vcm = CMVel(); // Adjust particle positions for (Int_t i = 0; i < numparts; i++) particle[i].SetVelocity(particle[i].GetVelocity(0) - Vcm.pos[0], particle[i].GetVelocity(1) - Vcm.pos[1], particle[i].GetVelocity(2) - Vcm.pos[2]); } // Adjust particle positions // Subtract position (non-relativistic) void System::AdjustPosition(Coord R) { Int_t i; #ifdef USEOPENMP #pragma omp parallel default(shared) \ private(i) { #pragma omp for schedule(dynamic,1) nowait #endif for (i = 0; i < numparts; i++) particle[i].SetPosition(particle[i].GetPosition(0) - R.pos[0], particle[i].GetPosition(1) - R.pos[1], particle[i].GetPosition(2) - R.pos[2]); #ifdef USEOPENMP } #endif } void System::AdjustPosition(Coordinate R) { Int_t i; #ifdef USEOPENMP #pragma omp parallel default(shared) \ private(i) { #pragma omp for schedule(dynamic,1) nowait #endif for (i = 0; i < numparts; i++) particle[i].SetPosition(particle[i].GetPosition(0) - R[0], particle[i].GetPosition(1) - R[1], particle[i].GetPosition(2) - R[2]); #ifdef USEOPENMP } #endif } // Subtract velocity (non-relativistic) void System::AdjustVelocity(Coord V) { Int_t i; #ifdef USEOPENMP #pragma omp parallel default(shared) \ private(i) { #pragma omp for schedule(dynamic,1) nowait #endif for (i = 0; i < numparts; i++) particle[i].SetVelocity(particle[i].GetVelocity(0) - V.pos[0], particle[i].GetVelocity(1) - V.pos[1], particle[i].GetVelocity(2) - V.pos[2]); #ifdef USEOPENMP } #endif } // Subtract velocity (non-relativistic) void System::AdjustVelocity(Coordinate V) { Int_t i; #ifdef USEOPENMP #pragma omp parallel default(shared) \ private(i) { #pragma omp for schedule(dynamic,1) nowait #endif for (i = 0; i < numparts; i++) particle[i].SetVelocity(particle[i].GetVelocity(0) - V[0], particle[i].GetVelocity(1) - V[1], particle[i].GetVelocity(2) - V[2]); #ifdef USEOPENMP } #endif } //Angular Momentum Coord System::AngularMomentum(Coord o) const { Coord J = {0, 0, 0}; Coord x = {0, 0, 0}; Coord v = {0, 0, 0}; for (Int_t i = 0; i < numparts; i++) { for (int j = 0; j < 3; j++){ x.pos[j]=particle[i].GetPosition(j)-o.pos[0]; v.pos[j]=particle[i].GetVelocity(j); } J.pos[0]+= particle[i].GetMass()*(x.pos[1]*v.pos[2]-x.pos[2]*v.pos[1]); J.pos[1]+= -particle[i].GetMass()*(x.pos[0]*v.pos[2]-x.pos[2]*v.pos[0]); J.pos[2]+= particle[i].GetMass()*(x.pos[0]*v.pos[1]-x.pos[1]*v.pos[0]); } return J; } Coord System::AngularMomentum(Coordinate o) const { Coord newo ={o[0],o[1],o[2]}; Coord J = AngularMomentum(newo); return J; } //with no argument assumes center of mass Coord System::AngularMomentum() const { Coord o =this->CM(); Coord J = AngularMomentum(o); return J; } //Energy Functions //Calculates the kinetic energy of the ith particle about the CM //Note that this KE has units of M*L^2/T^2 Double_t System::KineticEnergy(Int_t i) const { //System S(*this); //S.AdjustForCMVel(); Coord cmvel = CMVel(); return 0.5*particle[i].GetMass()*(pow(particle[i].Vx()-cmvel.pos[0],2) +pow(particle[i].Vy()-cmvel.pos[1],2)+pow(particle[i].Vz()-cmvel.pos[2],2)); } //Calculates the kinetic energy of the particles about the CM. //Note that this KE has units of M*L^2/T^2 Double_t System::KineticEnergy() const { //note that making a copy of the system and adjusting its coordinates may use more memory but //might be more computationally efficient. //System S(*this); //S.AdjustForCMVel(); Coord cmvel = CMVel(); Double_t KE=0; for (Int_t i=0;i0) { Int_t i=FindParticle(part); if (i==-1) cerr << "Can't find particle.\n"; else { for (Int_t j=i;j0) && (i>=0) && (i 0) { for (Int_t i = 0; i < S.numparts; i++) outs << S.particle[i] << endl; } outs.unsetf(ios::floatfield); return outs; } /* // Gas System //total mass in gas Double_t GasSystem::TotalGasMass() const { Double_t mt=0.; for (Int_t i=0;i