diff --git a/CMakeLists.txt b/CMakeLists.txt index a6401c510279b036255b9befed570129e031b3e9..52f2c169733a92d2eadea0cd423311f6b1de6b62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,11 +8,13 @@ set(CMAKE_CXX_FLAGS "-O3 -Wall") set(HEADERS lib/nBodySim.hpp lib/node.hpp lib/tree.hpp + lib/utils.hpp ) set(SOURCES lib/nBodySim.cpp lib/node.cpp lib/tree.cpp + lib/utils.cpp ) add_library(nbodyLib ${HEADERS} ${SOURCES}) diff --git a/lib/nBodySim.cpp b/lib/nBodySim.cpp index 6e5bda6e4c219eecfd44685dac76b903f255d316..79523614bea1ae14a3dd1093f4d53505eb5edb70 100644 --- a/lib/nBodySim.cpp +++ b/lib/nBodySim.cpp @@ -1,6 +1,4 @@ #include "nBodySim.hpp" -#include "tree.hpp" -#include "utils.hpp" #include <omp.h> #include <cmath> #include <string> @@ -52,6 +50,8 @@ nBodySim::nBodySim(std::string datafile) { Vector center{0.0, 0.0, 0.0}; tree_ = new Tree(mass_, positions_, forces_, softening_, nParticles_, maxCoord, center); + + utils_ = Utilities(nParticles_, positions_, velocities_, forces_, mass_, softening_, tree_); } nBodySim::~nBodySim() { @@ -73,28 +73,28 @@ void nBodySim::runSimulation(double dt, unsigned nSteps) { for (unsigned i=0; i < nSteps; ++i) { write2file(positions_, positionsFile, 3, skip); - energyFile << calculateTotalEnergy() << std::endl; + energyFile << utils_.calculateTotalEnergy() << std::endl; - // Vector COM = getCenterOfMass(); + // Vector COM = utils_.getCenterOfMass(); // Integrate with leapfrog auto start = std::chrono::high_resolution_clock::now(); kick(dt/2.0); drift(dt); treeUpdateForces(); - // double dev = verifyForces(); + // double dev = utils_.verifyForces(); kick(dt/2.0); updateTree(); auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start); std::cout << std::right << "Step " << std::setw(5) << i - // << ": MFM = " << std::setw(12) << calculateMeanForceMagnitude() + // << ": MFM = " << std::setw(12) << utils_.calculateMeanForceMagnitude() // << ", DEV = " << std::setw(12) << dev - // << ", MCD = " << std::setw(8) << calculateMeanCenterDistance() - // << ", MID = " << std::setw(8) << calculateMeanInterparticleDistance() + // << ", MCD = " << std::setw(8) << utils_.calculateMeanCenterDistance() + // << ", MID = " << std::setw(8) << utils_.calculateMeanInterparticleDistance() // << ", COM = " << "(" << COM[0] << ", " << COM[1] << ", " << COM[2] << ")" - // << ", E = " << std::setw(12) << calculateTotalEnergy() + // << ", E = " << std::setw(12) << utils_.calculateTotalEnergy() << ", T = " << std::setw(8) << duration.count()/1000.0 << " s" << std::endl; } @@ -157,44 +157,6 @@ void nBodySim::treeUpdateForces() { } } -double nBodySim::verifyForces() const { - double avgdev = 0.0; - // loop over each pair of particles and calculate the force between them - #pragma omp parallel for // schedule(guided) - for (unsigned i = 0; i < nParticles_; ++i) { - double px = positions_[3*i]; - double py = positions_[3*i+1]; - double pz = positions_[3*i+2]; - double fx = 0.0; - double fy = 0.0; - double fz = 0.0; - for (unsigned j = 0; j < nParticles_; ++j) { - if (j == i) continue; - // calculate distance between particles - double dx = positions_[3*j+0] - px; - double dy = positions_[3*j+1] - py; - double dz = positions_[3*j+2] - pz; - double r2 = dx*dx + dy*dy + dz*dz; - double r = std::sqrt(r2); - - // calculate forces - double factor = mass_*mass_ / (r2 + softening_*softening_); - double fxij = factor * dx / r; - double fyij = factor * dy / r; - double fzij = factor * dz / r; - fx += fxij; - fy += fyij; - fz += fzij; - } - double f = std::sqrt(fx*fx + fy*fy + fz*fz); - double ft = std::sqrt(forces_[3*i]*forces_[3*i] + forces_[3*i+1]*forces_[3*i+1] + forces_[3*i+2]*forces_[3*i+2]); - double df = std::abs(f - ft); - avgdev += df; - } - avgdev /= nParticles_; - return avgdev; -} - void nBodySim::kick(double dt) { #pragma omp parallel for for (unsigned i=0; i < nParticles_; ++i) { @@ -217,92 +179,6 @@ void nBodySim::updateTree() { tree_->update(); } -double nBodySim::calculateMeanInterparticleDistance() const { - double meanInterparticleDistance = 0.0; - // loop over each pair of particles and calculate the distance between them - #pragma omp parallel for reduction(+:meanInterparticleDistance) // schedule(guided, 64) - for (unsigned i = 0; i < nParticles_; i++) { - double px = positions_[3*i]; - double py = positions_[3*i+1]; - double pz = positions_[3*i+2]; - double localMeanInterparticleDistance = 0.0; - for (unsigned j = i+1; j < nParticles_; j++) { - // calculate distance between particles - double dx = positions_[3*j] - px; - double dy = positions_[3*j+1] - py; - double dz = positions_[3*j+2] - pz; - double r2 = dx*dx + dy*dy + dz*dz; - double r = std::sqrt(r2); - localMeanInterparticleDistance += r; - } - meanInterparticleDistance += localMeanInterparticleDistance; - } - meanInterparticleDistance /= (nParticles_*(nParticles_-1)/2); - return meanInterparticleDistance; -} - -double nBodySim::calculateMeanForceMagnitude() const { - double forceMagnitude = 0.0; - #pragma omp parallel for reduction(+:forceMagnitude) - for (unsigned i = 0; i < nParticles_; i++) { - double fx = forces_[3*i]; - double fy = forces_[3*i+1]; - double fz = forces_[3*i+2]; - double f2 = fx*fx + fy*fy + fz*fz; - double f = std::sqrt(f2); - forceMagnitude += f; - } - forceMagnitude /= nParticles_; - return forceMagnitude; -} - -double nBodySim::calculateMeanCenterDistance() const { - double avgDist = 0.0; - #pragma omp parallel for reduction(+:avgDist) - for (unsigned i = 0; i < nParticles_; i++) { - double x = positions_[3*i]; - double y = positions_[3*i+1]; - double z = positions_[3*i+2]; - double d2 = x*x + y*y + z*z; - double d = std::sqrt(d2); - avgDist += d; - } - avgDist /= nParticles_; - return avgDist; -} - -double nBodySim::calculateTotalEnergy() const { - double totalEnergy = 0.0; - #pragma omp parallel for reduction(+:totalEnergy) - for (unsigned i = 0; i < nParticles_; i++) { - double px = positions_[3*i]; - double py = positions_[3*i+1]; - double pz = positions_[3*i+2]; - double vx = velocities_[3*i]; - double vy = velocities_[3*i+1]; - double vz = velocities_[3*i+2]; - double v2 = vx*vx + vy*vy + vz*vz; - double kineticEnergy = 0.5 * mass_ * v2; - double potentialEnergy = 0.0; - for (unsigned j = 0; j < nParticles_; ++j) { - if (j == i) continue; - // calculate distance between particles - double dx = positions_[3*j] - px; - double dy = positions_[3*j+1] - py; - double dz = positions_[3*j+2] - pz; - double r2 = dx*dx + dy*dy + dz*dz; - double r = std::sqrt(r2); - potentialEnergy -= mass_*mass_ / r; - } - totalEnergy += kineticEnergy + potentialEnergy; - } - return totalEnergy; -} - -Vector nBodySim::getCenterOfMass() const { - return tree_->getCOM(); -} - void nBodySim::write2file(const double* array, std::ofstream& file, unsigned dim, unsigned skip) const { // writes array to file, with N rows and dim columns for (unsigned i = 0; i < nParticles_; i+=skip) { diff --git a/lib/nBodySim.hpp b/lib/nBodySim.hpp index 4b6be7dc9ba9416045eb5966d43a5e956297a489..5f887dbb5c72883a555ae1394d4b3bc15ec3571c 100644 --- a/lib/nBodySim.hpp +++ b/lib/nBodySim.hpp @@ -2,6 +2,7 @@ #define NBODYSIM_HPP #include "tree.hpp" +#include "utils.hpp" #include <string> class nBodySim { @@ -18,8 +19,6 @@ public: void updateForces(); // Use treecode and multipole expansion to calculate forces void treeUpdateForces(); - // Loops over all pairs of particles and calculates forces, checks if forces are correct - double verifyForces() const; // Leapfrog: update velocities void kick(double dt); @@ -27,18 +26,7 @@ public: void drift(double dt); // Update tree void updateTree(); - - // Loop over all pairs of particles and calculate mean interparticle distance - double calculateMeanInterparticleDistance() const; - // Loop over all force vectors and calculate mean force magnitude - double calculateMeanForceMagnitude() const; - // loop over every particle and calculate the mean distance from the center - double calculateMeanCenterDistance() const; - // Loop over all particles and calculate the total energy - double calculateTotalEnergy() const; - // get center of mass - Vector getCenterOfMass() const; - + // Writes data to file, every row is a particle void write2file(const double* array, std::ofstream& file, unsigned dim, unsigned skip) const; // Write current state of simulation to file @@ -53,6 +41,8 @@ public: unsigned getNParticles() const { return nParticles_; } private: + Utilities utils_; + Tree* tree_; unsigned nParticles_; diff --git a/lib/utils.cpp b/lib/utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9c8ac7aff268868b3c78afb5c1b73838aaf8e92 --- /dev/null +++ b/lib/utils.cpp @@ -0,0 +1,146 @@ +#include "utils.hpp" + +#include <cmath> +#include <omp.h> + +Utilities::Utilities() { + nParticles_ = 0; + positions_ = nullptr; + velocities_ = nullptr; + forces_ = nullptr; + mass_ = 0.0; + tree_ = nullptr; +} + +Utilities::Utilities(unsigned nParticles, double* positions, double* velocities, double* forces, double mass, double softening, Tree* tree) { + nParticles_ = nParticles; + positions_ = positions; + velocities_ = velocities; + forces_ = forces; + mass_ = mass; + tree_ = tree; +} + +double Utilities::verifyForces() const { + double avgdev = 0.0; + // loop over each pair of particles and calculate the force between them + #pragma omp parallel for // schedule(guided) + for (unsigned i = 0; i < nParticles_; ++i) { + double px = positions_[3*i]; + double py = positions_[3*i+1]; + double pz = positions_[3*i+2]; + double fx = 0.0; + double fy = 0.0; + double fz = 0.0; + for (unsigned j = 0; j < nParticles_; ++j) { + if (j == i) continue; + // calculate distance between particles + double dx = positions_[3*j+0] - px; + double dy = positions_[3*j+1] - py; + double dz = positions_[3*j+2] - pz; + double r2 = dx*dx + dy*dy + dz*dz; + double r = std::sqrt(r2); + + // calculate forces + double factor = mass_*mass_ / (r2 + softening_*softening_); + double fxij = factor * dx / r; + double fyij = factor * dy / r; + double fzij = factor * dz / r; + fx += fxij; + fy += fyij; + fz += fzij; + } + double f = std::sqrt(fx*fx + fy*fy + fz*fz); + double ft = std::sqrt(forces_[3*i]*forces_[3*i] + forces_[3*i+1]*forces_[3*i+1] + forces_[3*i+2]*forces_[3*i+2]); + double df = std::abs(f - ft); + avgdev += df; + } + avgdev /= nParticles_; + return avgdev; +} + +double Utilities::calculateMeanInterparticleDistance() const { + double meanInterparticleDistance = 0.0; + // loop over each pair of particles and calculate the distance between them + #pragma omp parallel for reduction(+:meanInterparticleDistance) // schedule(guided, 64) + for (unsigned i = 0; i < nParticles_; i++) { + double px = positions_[3*i]; + double py = positions_[3*i+1]; + double pz = positions_[3*i+2]; + double localMeanInterparticleDistance = 0.0; + for (unsigned j = i+1; j < nParticles_; j++) { + // calculate distance between particles + double dx = positions_[3*j] - px; + double dy = positions_[3*j+1] - py; + double dz = positions_[3*j+2] - pz; + double r2 = dx*dx + dy*dy + dz*dz; + double r = std::sqrt(r2); + localMeanInterparticleDistance += r; + } + meanInterparticleDistance += localMeanInterparticleDistance; + } + meanInterparticleDistance /= (nParticles_*(nParticles_-1)/2); + return meanInterparticleDistance; +} + +double Utilities::calculateMeanForceMagnitude() const { + double forceMagnitude = 0.0; + #pragma omp parallel for reduction(+:forceMagnitude) + for (unsigned i = 0; i < nParticles_; i++) { + double fx = forces_[3*i]; + double fy = forces_[3*i+1]; + double fz = forces_[3*i+2]; + double f2 = fx*fx + fy*fy + fz*fz; + double f = std::sqrt(f2); + forceMagnitude += f; + } + forceMagnitude /= nParticles_; + return forceMagnitude; +} + +double Utilities::calculateMeanCenterDistance() const { + double avgDist = 0.0; + #pragma omp parallel for reduction(+:avgDist) + for (unsigned i = 0; i < nParticles_; i++) { + double x = positions_[3*i]; + double y = positions_[3*i+1]; + double z = positions_[3*i+2]; + double d2 = x*x + y*y + z*z; + double d = std::sqrt(d2); + avgDist += d; + } + avgDist /= nParticles_; + return avgDist; +} + +double Utilities::calculateTotalEnergy() const { + double totalEnergy = 0.0; + #pragma omp parallel for reduction(+:totalEnergy) + for (unsigned i = 0; i < nParticles_; i++) { + double px = positions_[3*i]; + double py = positions_[3*i+1]; + double pz = positions_[3*i+2]; + double vx = velocities_[3*i]; + double vy = velocities_[3*i+1]; + double vz = velocities_[3*i+2]; + double v2 = vx*vx + vy*vy + vz*vz; + double kineticEnergy = 0.5 * mass_ * v2; + double potentialEnergy = 0.0; + for (unsigned j = 0; j < nParticles_; ++j) { + if (j == i) continue; + // calculate distance between particles + double dx = positions_[3*j] - px; + double dy = positions_[3*j+1] - py; + double dz = positions_[3*j+2] - pz; + double r2 = dx*dx + dy*dy + dz*dz; + double r = std::sqrt(r2); + potentialEnergy -= mass_*mass_ / r; + } + totalEnergy += kineticEnergy + potentialEnergy; + } + return totalEnergy; +} + +Vector Utilities::getCenterOfMass() const { + return tree_->getCOM(); +} diff --git a/lib/utils.hpp b/lib/utils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8dec913d32c90d19147a03cee1319079aec300e5 --- /dev/null +++ b/lib/utils.hpp @@ -0,0 +1,35 @@ +#ifndef UTILS_HPP +#define UTILS_HPP + +#include "tree.hpp" + +class Utilities { +public: + // Constructor + Utilities(); + Utilities(unsigned nParticles, double* positions, double* velocities, double* forces, double mass, double softening, Tree* tree); + + // Loops over all pairs of particles and calculates forces, checks if forces are correct + double verifyForces() const; + // Loop over all pairs of particles and calculate mean interparticle distance + double calculateMeanInterparticleDistance() const; + // Loop over all force vectors and calculate mean force magnitude + double calculateMeanForceMagnitude() const; + // Loop over every particle and calculate the mean distance from the center + double calculateMeanCenterDistance() const; + // Loop over all particles and calculate the total energy + double calculateTotalEnergy() const; + // Get center of mass + Vector getCenterOfMass() const; + +private: + unsigned nParticles_; + double* positions_; + double* velocities_; + double* forces_; + double mass_; + double softening_; + Tree* tree_; +}; + +#endif /* UTILS_HPP */ \ No newline at end of file