Lennard-Jones / WCA Potential Simulation - c++

i am trying to implement a molecular dynamics simulation with the Lennard Jones potential.
I have the time evolution of the positions and velocities of the particles in multiple config files (n = 0,...,99) in steps of dt, such that t=n dt. So the actual simulation part is taken care of in that sense, for now i only have to calculate the potential energy and the force on each particle.
I already implemented a function to read in the config.dat files and put them in vectors, that part works as far as i know without an error. Then i wrote functions that calculate the force and the potential energy with a given distance r_ij between two particles (also used Newtown's third law so that i don't have to calculate the forces multiple times for the same interaction).
I also (hopefully correctly) implemented the periodic boundary conditions so that the particles can interact with their own images in the image boxes.
To test if my code works, i wanted to plot the total potential energy for all t=n dt.
However that does not work as intended because for some reason the potential energy that is written into the output files is always zero (the function for the potential energy returns zero if r_ij > r_cut, r_cut is where the potential is set to zero).
#include <iostream>
#include <math.h>
#include <fstream>
#include <stdlib.h>
#include <vector>
#include <string>
#include <utility>
#include <stdexcept>
#include <sstream>
using namespace std;
// Python >> C/C++
// Reads the initial states from the files
// The whole simulation in Python would have been as long as the function in c++ that just reads in the input
void read_input(int num_file, vector<double>& n, vector<double>& x, vector<double>& y, vector<double>& vx, vector<double>& vy, double& lx, double& ly) {
// Variable file name + opening it
ifstream file("configurations/config_" + to_string(num_file) + ".dat");
// Temp variables for reading in the file
double num, temp_x, temp_y, temp_vx, temp_vy;
// Looping over it
if (file.is_open()) {
string line;
while (getline(file, line)) {
// For the header; The header contains only the dimensions, e.g, 14 14, there are only 5 characters
if (line.length() == 5) {
stringstream dim_str(line);
dim_str >> lx >> ly;
continue;
}
// For the rest files
stringstream temp_str(line);
temp_str >> num >> temp_x >> temp_y >> temp_vx >> temp_vy;
n.push_back(num);
x.push_back(temp_x);
y.push_back(temp_y);
vx.push_back(temp_vx);
vy.push_back(temp_vy);
}
file.close();
}
}
// Calculates the potential for rij
double calc_pot(double r) {
double sigma = 1.0;
double epsilon = 1.0;
if (r <= pow(2, (1.0 / 6)) * sigma) {
double res = 4.0 * epsilon * (pow(sigma / r, 12) - pow(sigma / r, 6)) + epsilon;
return res;
}
else {
return 0;
}
}
// Calculates the force for rij
double calc_force(double r) {
double sigma = 1.0;
// Replaced the sigma^n with 1 bcs sigma = 1
double epsilon = 1.0;
if (r <= pow(2, (1.0 / 6)) * sigma) {
double res = (48.0 * epsilon / pow(r, 13)) - (24 * epsilon / pow(r, 7));
return res;
}
else {
return 0;
}
}
// Calculates the distance of the
double dist(double rx, double ry) {
return sqrt(rx * rx + ry * ry);
}
int main() {
// Misc. parameters
int N = 144;
double mass = 1.0;
double sigma = 1.0;
double epsilon = 1.0;
// Tau = sqrt(mass*sigma^2/epsilon) = (here) 1
double tau = 1.0;
double dt = 0.02;
// Vectors for the read in values for i and i+1
vector<double> n, x, y, vx, vy;
double lx, ly;
// Vector for the potential and two for the force, x and y
vector<double> epot(N), f_x(N), f_y(N);
for (int i = 0; i < N; i++) {
epot[i], f_x[i], f_y[i] = 0;
}
// Outerloop for time steps (in this case the files n = {0,..,99})
for (int k = 0; k <= 99; k++) {
string fname = "output/epot_" + to_string(k) + ".txt";
ofstream output(fname);
read_input(k, n, x, y, vx, vy, lx, ly);
// Inner two loops to accses every possible interaction without doing them twice
for (int i = 0; i < N - 1; i++) {
// Vecctor for particle i
double rix = x[i];
double riy = y[i];
for (int j = i + 1; j < N; j++) {
// Vector for particle i+1
double rjx = x[j];
double rjy = y[j];
// Periodic boundary cond.
if (rix > lx) {
rix -= lx;
}
if (riy > ly) {
riy -= ly;
}
if (rjx > lx) {
rjx -= lx;
}
if (rjy > ly) {
rjy -= ly;
}
if (rix < 0) {
rix += lx;
}
if (riy < 0) {
riy += ly;
}
if (rjx < 0) {
rjx += lx;
}
if (rjy < 0) {
rjy += ly;
}
// Component wise distance for the force
double dist_x = rix - rjx;
double dist_y = riy - rjy;
// Minimum image convention
if (abs(dist_x) > lx / 2) {
dist_x = (lx - abs(dist_x)) * (-dist_x) / abs(dist_x);
}
if (abs(dist_y) > ly / 2) {
dist_y = (ly - abs(dist_y)) * (-dist_y) / abs(dist_y);
}
// Normalized Force/R
f_x[i] += calc_force(dist_x) * (1 / dist(dist_x, dist_y));
f_y[i] += calc_force(dist_y) * (1 / dist(dist_x, dist_y));
f_y[j] += -calc_force(dist_x) * (1 / dist(dist_x, dist_y));
f_y[j] += -calc_force(dist_y) * (1 / dist(dist_x, dist_y));
// Potential energy
epot[i] += calc_pot(dist(dist_x, dist_y));
}
// Potential energy per particle
output << fixed << std::setprecision(4) << epot[i] / (N) << endl;
}
}
}
A config file looks something like this
14 14
0 0 0 1.0292605474705 0.394157727758591
1 0 1.16666666666667 1.05721528014223 1.9850461002085
2 0 2.33333333333333 1.18385526103892 0.143930912297367
3 0 3.5 -0.938850340823852 1.71993225409788
4 0 4.66666666666667 1.99468650405917 0.952210892864475
5 0 5.83333333333333 -0.985361963654284 3.05201529674118
6 0 7 2.84071317501321 0.0689241023507716
7 0 8.16666666666667 3.56152464385237 2.88858201933488
8 0 9.33333333333333 0.147896423269195 1.40592679110988
The header contains the dimensions of the simulation box, here (14,14).
Then all the lines have the corresponding values of {#Particle, x, y, velocity x, Velocity y).
The file above shows this for the first 9 particles.
I am relatively new to c/c++ so have mercy with me 😄.
Also i am aware that the code has still potential to be optimised but i will deal with that when i can calculate the force on each particle correctly.
Edit:
Here is the formula for the potential energy:
The force can be calculated via F= -d/dr U(r).

Related

How to create a Gaussian kernel of arbitrary width?

How to create a Gaussian kernel by only specifying its width w (3,5,7,9...), and without specifying its variance sigma?
In other word, how to adapt sigma so that the Gaussian distribution 'fits well' w?
I would be interested in a C++ implementation:
void create_gaussian_kernel(int w, std::vector<std::vector<float>>& kernel)
{
kernel = std::vector<std::vector<float>>(w, std::vector<float>(w, 0.f)); // 2D array of size w x w
const Scalar sigma = 1.0; // how to adapt sigma to w ???
const int hw = (w-1)/2; // half width
for(int di = -hw; di <= +hw; ++di)
{
const int i = hw + di;
for(int dj = -hw; dj <= +hw; ++dj)
{
const int j = hw + dj;
kernel[i][j] = gauss2D(di, dj, sigma);
}
}
}
Everything I see on the Internet use a fixed size w and a fixed variance sigma :
geeksforgeeks.org/gaussian-filter-generation-c/
tutorialspoint.com/gaussian-filter-generation-in-cplusplus
stackoverflow.com/a/8204880/5317819
stackoverflow.com/q/42186498/5317819
stackoverflow.com/a/54615770/5317819
I found a simple (arbitrary) relation between sigma and w.
I want the next value outside the kernel (along one axis) below a very small value epsilon:
exp( - (half_width + 1)^2 / (2 * sigma^2) ) < epsilon
with half_width the kernel 'half width'.
The result is
sigma^2 = - (half_width + 1)^2 / (2 * log(epsilon))
I use the following c++ code:
#include <vector>
#include <cmath>
#include <cassert>
using Matrix = std::vector<std::vector<float>>;
// compute sigma^2 that 'fit' the kernel half width
float compute_squared_variance(int half_width, float epsilon = 0.001)
{
assert(0 < epsilon && epsilon < 1); // small value required
return - (half_width + 1.0) * (half_width + 1.0) / 2.0 / std::log(epsilon);
}
float gaussian_exp(float y, float x, float sigma2)
{
assert(0 < sigma2);
return std::exp( - (x*x + y*y) / (2 * sigma2) );
}
// create a Gaussian kernel of size 2*half_width+1 x 2*half_width+1
Matrix make_gaussian_kernel(int half_width)
{
if(half_width <= 0)
{
// kernel of size 1 x 1
Matrix kernel(1, std::vector<float>(1, 1.0));
return kernel;
}
Matrix kernel(2*half_width+1, std::vector<float>(2*half_width+1, 0.0));
const float sigma2 = compute_squared_variance(half_width, 0.1);
float sum = 0;
for(int di = -half_width; di <= +half_width; ++di)
{
const int i = half_width + di;
for(int dj = -half_width; dj <= +half_width; ++dj)
{
const int j = half_width + dj;
kernel[i][j] = gaussian_exp(di, dj, sigma2);
sum += kernel[i][j];
}
}
assert(0 < sum);
// normalize
for(int i=0; i<2*half_width+1; ++i)
{
for(int j=0; j<2*half_width+1; ++j)
{
kernel[i][j] /= sum;
}
}
return kernel;
}

How can i get interpolated Polar coordinates?

I have a problem to get a discrete Sequence of a contour.
My idea: I want to place an anchor in a middle of a closed contour in an image and use polar coordinates to get a length for each degree of the polar coordinates.
I already have created a vector of fixed length 360 and iterate through all contour points(ca. 4000) with length l=contour.length/360. Here i get 360 values along the contour with length l. But i want to have a discrete value for each integer degree from 1 to 360.
Can i interpolate my array to fix values from 1 to 360?
vector<cv::Point> cn;
double theta = 0;
double dis = 0;
int polsize = 360;
int psize = 0;
for (int k = 0; k < cnts[0].size(); k++) {
cn.push_back(cnts[0].at(k));
}
double pstep = cn.size() / polsize;
for (int m = 1; m < polsize; m++) {
psize = (int)(m * pstep);
polar(cn[psize].x, cn[psize].y, &dis, &theta);
outputFile << theta << "/" << dis << ";";
}
void polar(int x, int y, double* r, double* theta)
{
double toDegrees = 180 / 3.141593;
*r = sqrt((pow(x, 2)) + (pow(y, 2)));
double xt = x, yt = y;
yt = 1024 - yt;
if (xt == 0) xt = 0.1;
if (yt == 0) yt = 0.1;
*theta = atan(yt / xt) * toDegrees;
if (*theta < 0) *theta = *theta+180;
return;
}
You seem to miss some of the C++ basics. E.g.
1) If you use at() you add unnecessary range checking. As you are looping until cnts[0].size() you're doing that twice now.
2) you don't need to use return in void functions.
3) don't use pointers for return. This is C++, not C. Use references, or std::tuple return type.
Then you're actually replicating the std::complex type.
The code can be really simple.
#include <vector>
//#include <algorithm> // if using std::copy
#include <cmath>
#include <sstream> // only for the temporary output.
static constexpr auto toDeg = 180 / 3.141593;
struct Point{
double x,y;
double abs() const {
return std::sqrt(std::pow(x,2) + std::pow(y,2));
}
double arg() const {
return std::atan2(y, x) * toDeg;
}
};
int main(){
std::vector<std::vector<Point>> cnts = {{{1,1}}};
// copy constructor
std::vector<Point> cn(cnts[0]);
// range-based constructor
//std::vector<Point> cn(std::cbegin(cnts[0]), std::cend(cnts[0]));
// or copy-insert
//std::vector<Point> cn
//cn.reserve(cnts[0].size());
//std::copy(std::cbegin(cnts[0]), std::cend(cnts[0]), std::back_inserter(cn));
std::stringstream outputFile; // temp
for (auto const& el : cn) {
outputFile << el.arg() << "/" << el.abs() << ";";
}
}

How do I fix this bug with arctanx function in c++

I've recently been given a problem by my teacher about some mathematical equation / formula called the arctanx formula. The question is:
According to the Arctanx(x) = x - ((x ^ 3) / 3) + ((x ^ 5) / 5) - ((x ^
7) / 7) + ...and π = 6 * arctanx(1 / sqrt(3)), Create function arctanx(x)
, and find pi when the last "number"(like this ((x ^ y) / y)) is right before
and bigger than 10 ^ -6, or you can say that no "number" can be smaller than
that number without being smaller than 10 ^ -6.
I tried to code it out, but there is a bug in it.
# include<iostream>
# include<math.h>
using namespace std;
float arctanx() {
long double pi = 3.1415926535897;
int i = 0; // 0 = +, 1 = -
float sum = 0;
float lsum;
for (int y = 1; y < pi; y += 2) {
if (lsum > 0.000001) {
if (i == 0) {
lsum = pow(1 / sqrt(3), y) / y;
sum += pow(1 / sqrt(3), y) / y;
i++;
} else if (i == 1) {
lsum = pow(1 / sqrt(3), y) / y;
sum -= pow(1 / sqrt(3), y) / y;
i--;
}
} else {
break;
}
}
sum = sum * 6;
return sum;
}
int main() {
cout << arctanx();
return 0;
}
It should have a output of some number not equal to zero, but I got 0 from running this.
Your program has Undefined Behavior because you are using the uninitialized float lsum; in the comparison if (lsum > 0.000001).
What probably happens in your case is that lsum happens to be less than or equal to 0.000001 and your for immediately breaks without doing anything causing your function to return 0 * 6 which is obviously 0.
Create function arctanx(x)
The function defined in the posted code doesn't accept any parameter, it just uses the hardwired (and repeated) value 1 / sqrt(3) and tries to return an approximated value of π instead of the arctangent of x.
It also has undefined behavior, beeing lsum uninitialized (therefore having an indeterminate value) when it is first used in the comparison inside the loop.
Consider this implementation, but be advised that this particular polinomial expansion diverges for values of x greater than 1.
#include <iostream>
#include <iomanip>
#include <cmath>
double arctanx(double x);
int main()
{
double pi = 6.0 * arctanx(1.0 / std::sqrt(3));
std::cout << std::setprecision(8) << pi << '\n';
}
double arctanx(double x)
{
// You can take advantage of a running power, instad of calculating
// pow(x, i) at every iteration
double sq_x = x * x;
double pow_x = x * sq_x;
double err = 1e-6;
// Instead of keeping track of the alternating sign, you can use
// two separate partial sums
double sum_pos_term = x;
double sum_neg_term = 0.0;
for (int i = 3; i < 33; i += 2) // <- Limit the number of iterations
{
if (pow_x < err * i)
break;
sum_neg_term += pow_x / i;
i += 2;
pow_x *= sq_x;
if (pow_x < err * i)
break;
sum_pos_term += pow_x / i;
pow_x *= sq_x;
}
return sum_pos_term - sum_neg_term;
}

Trapezoidal integration program doesn't return a reasonable value

Why doesn't this code to integrate the area under a sin curve return a reasonable value? (edited to include a bunch of suggestions)
//I want to write a program that takes the area under a curve by outputting the sum of the areas of n rectangles
#include <vector>
#include <iostream>
#include <cmath>
#include <numeric>
double interval(double d, double n)
{
return d / n;
}
using namespace std;
int main()
{
double xmax = 20; //upper bound
double xmin = 2; //lower bound
double line_length = xmax - xmin; //range of curve
double n = 1000; //number of rectangles
vector<double> areas;
double interval_length = interval(line_length, n);
for (double i = 0; i < n; ++i)
{
double fvalue = xmin + i;
areas.push_back((interval_length * sin(fvalue)) + (0.5 * interval_length * (sin(fvalue + 1) - sin(fvalue))));
//idea is to use A = b*h1 + 1/2 b*h2 to approximate the area under a curve using trapezoid area
}
I added fvalue, interval_length and fixed the logic a bit
double sum_areas = accumulate(areas.begin(), areas.end(), 0.0);
//accumulate takes each element in areas and adds them together, beginning with double 0.0
cout << "The approximate area under the curve is " << '\n';
cout << sum_areas << '\n';
//this program outputs the value 0.353875, the actual value is -.82423
return 0;
}
The code below doesn't mix using loop variable and x. It has a drawback (same as yours code) that error summing dx is accumulated i.e. dx*n != xmax-xmin. To account for this particular error one should calculate current x as function of i (loop variable) on each iteration as x = xmin + (xmax - xmin)*i/n.
#include <iostream>
#include <cmath>
double sum(double xmin, double xmax, double dx)
{
double rv = 0;
for (double x = xmin + dx; x <= xmax; x += dx)
rv += (sin(x) + sin(x-dx)) * dx / 2;
return rv;
}
int main()
{
int n = 1000;
double xmin = 0;
double xmax = 3.1415926;
std::cout << sum(xmin, xmax, (xmax - xmin)/n) << std::endl;
return 0;
}
You are forgetting the interval length in the function argument
double fvalue = xmin + i;
areas.push_back((interval_length * sin(fvalue)) + (0.5 * interval_length * (sin(fvalue + 1) - sin(fvalue))));
Should instead be
double fvalue = xmin + i*interval_length;
areas.push_back((interval_length * sin(fvalue)) + (0.5 * interval_length * (sin(fvalue + interval_length) - sin(fvalue))));
The second line can be better written as
areas.push_back(interval_length * 0.5 * (sin(fvalue + interval_length) + sin(fvalue));

C++ Function being Skipped [duplicate]

This question already has answers here:
Calling a function in main
(4 answers)
Closed 4 years ago.
So I'm trying to make a simple pool ball simulation, and when trying to check the collision between balls, my bounce function is being skipped in the loop. There should be a display on the console with the random letters in the function bounce in the PoolTable.cpp file, but its skipped and doesn't process the hits or output the text to the console. Not sure why its not running the function. No warnings. No errors. compiles fine. Im on windows machine, using code blocks, and the GLUT library/project.
Walkthrough
So I initialize and place the balls with the constructor. Then I draw the balls on the screen with the drawBalls function. After drawing the balls, i update their positions and move them with moveBalls function. After moving each ball, while still in the moveball function, I check for collisions with checkCollisions function. checkCollisions then starts two for loops, but never runs the bounce function, as the balls don't bounce off eachother, and the cout isn't printed in the terminal. for some reason it is skipped.
PoolTable.cpp
#include "PoolTable.h"
#include "poolball.h"
#include "Graphics.h"
#include <iostream>
using namespace std;
#include <cmath>
PoolTable::PoolTable( int x){
placeBalls( x );
}
void PoolTable::placeBalls( int x ){
number_of_balls = x;
for( int i = 0; i < x; i++){
balls[i].setX( balls[i].getRadius() + i * 20 );
balls[i].setY( balls[i].getRadius() + i * 30 );
}
}
double find_angle(double vx, double vy) {
// determine the angle between poolballs when they collide
double t; double PI = acos(-1.0);
if(vx < 0) // vertical collision
t = PI + atan(vy/vx);
else if(vx > 0.0 && vy >= 0.0) // 1st quardant collision
t = atan(vy/vx);
else if(vx > 0.0 && vy < 0.0) //
t = 2.0*PI + atan(vy/vx);
else if( vx == 0.0 && vy == 0.0)
t = 0.0;
else if(vx == 0 && vy >= 0.0)
t = PI/2.0;
else
t = 1.5 * PI;
return t;
}
void PoolTable::bounce(int i, int j) {
cout << "klasdjflkadsjflkasjfsadk" << endl;
double PI = acos(-1.0);
double x1 = balls[i].getX();
double y1 = balls[i].getY();
double x2 = balls[j].getX();
double y2 = balls[j].getY();
double dx = x2 - x1;
double dy = y2 - y1;
double dist = sqrt(dx*dx+dy*dy);
// did a collision occur
if(dist <= 2 * balls[i].getRadius()) {
double phi; // angle between the two ball centers
if(dx == 0.0)
phi = PI/2.0;
else
phi = atan2 (dy, dx);
// now compute the total velocities of the two balls
double vx1 = balls[i].xSpeed;
double vy1 = balls[i].getYSpeed();
double v1total = sqrt(vx1*vx1 + vy1*vy1);
double vx2 = balls[j].getXSpeed();
double vy2 = balls[j].getYSpeed();
double v2total = sqrt(vx2*vx2 + vy2*vy2);
// find the angle of each ball's velocity
double ang1 = find_angle(vx1,vy1);
double ang2 = find_angle(vx2,vy2);
// transform velocities into normal.tangential components
double v1xr = v1total * cos(ang1 - phi);
double v1yr = v1total * sin(ang1 - phi);
double v2xr = v2total * cos(ang2 - phi);
double v2yr = v2total * sin(ang2 - phi);
// now find the final velocities (assuming equal mass)
double v1fxr = v2xr;
double v2fxr = v1xr;
double v1fyr = v1yr;
double v2fyr = v2yr;
// reset the velocities
balls[i].setXSpeed(cos(phi)*v1fxr + cos(phi+PI/2)*v1fyr);
balls[i].setYSpeed(sin(phi)*v1fxr + sin(phi+PI/2)*v1fyr);
balls[j].setXSpeed(cos(phi)*v2fxr + cos(phi+PI/2)*v2fyr);
balls[j].setYSpeed(sin(phi)*v2fxr + sin(phi+PI/2)*v2fyr);
}
}
void PoolTable::checkCollisions(void){
for( int i = 0; i < number_of_balls; i++){
for( int j = i + 1; j < number_of_balls; j++){
bounce(i, j);
}
}
}
void PoolTable::moveBalls(void){
for( int i = 0; i < number_of_balls; i++){
balls[i].move();
void checkCollisions();
}
}
void PoolTable::drawBalls(void){
for( int i = 0; i < number_of_balls; i++){
balls[i].draw();
}
}
void checkCollisions(); (in moveBalls) is a function prototype, not a function call. Remove the void.