Skip to content

Instantly share code, notes, and snippets.

@stepheneb
Last active January 14, 2020 16:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stepheneb/1338696 to your computer and use it in GitHub Desktop.
Save stepheneb/1338696 to your computer and use it in GitHub Desktop.
Lennard-Jones Potential in Interactive Graph

Lennard-Jones Potential

See this example live

About the Lennard-Jones potential graph

The Lennard-Jones potential is a simple model describing the forces between a pair of neutral atoms or molecules.

In the graph above you can change the force as a function of distance by either changing the depth of the potential well by dragging epsilon up and down or changing the zero point for the potential by dragging sigma left and right.

References:

Wikipedia: Lennard-Jones Potential

source: gist.github.com/gists/1338696

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Simple Beam</title>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
<style type="text/css">
body {
font: 13px sans-serif;
}
rect {
fill: #fff;
}
#chart {
background-color: #F7F2C5;
width: 960px;
height: 500px;
}
circle, .line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
circle {
fill: #fff;
fill-opacity: .2;
cursor: move;
}
circle.selected {
fill: #ff7f0e;
stroke: #ff7f0e;
}
</style>
</head>
<body>
<div id="chart"></div>
<label for="interpolate">Interpolate:</label>
<select id="interpolate"></select>
<script type="text/javascript">
// graph = new grapher.lennardJonesSampleGraph();
var graph = {};
graph.epsilon = -0.5; // depth of the potential well
graph.sigma = 2; // finite distance at which the inter-particle potential is zero
graph.xmax = graph.sigma * 4;
graph.xmin = graph.sigma * 0.5;
graph.ymax = 0.6;
graph.ymin = -0.6;
graph.title = "Lennard-Jones potential";
graph.xlabel = "Radius";
graph.ylabel = "Potential Energy";
graph.lennard_jones_potential = [];
update_coefficients();
graph.coefficients = [
{ coefficient:"epsilon", x:graph.r_min, y:graph.epsilon },
{ coefficient:"sigma", x:graph.sigma, y:0 }
];
function update_epsilon(e) {
graph.epsilon = e;
update_coefficients();
}
function update_sigma(s) {
graph.sigma = s;
update_coefficients();
}
function update_coefficients() {
graph.r_min = Math.pow(2, 1/6) * graph.sigma; // distance at which the potential well reaches its minimum
graph.lennard_jones_potential = []
graph.alpha = 4 * graph.epsilon * Math.pow(graph.sigma, 12);
graph.beta = 4 * graph.epsilon * Math.pow(graph.sigma, 6);
for(var r = graph.xmin; r < graph.xmax; r += 0.05) {
graph.lennard_jones_potential.push({
x: r,
y: (graph.alpha/Math.pow(r, 12) - graph.beta/Math.pow(r, 6)) * -1
})
}
}
var chart = document.getElementById("chart"),
cx = chart.clientWidth,
cy = chart.clientHeight,
padding = {
"top": graph.title ? 40 : 20,
"right": 30,
"bottom": graph.xlabel ? 50 : 10,
"left": graph.ylabel ? 70 : 45
},
size = {
"width": cx - padding.left - padding.right,
"height": cy - padding.top - padding.bottom
},
mw = size.width,
mh = size.height,
tx = function(d) { return "translate(" + x(d) + ",0)"; },
ty = function(d) { return "translate(0," + y(d) + ")"; },
stroke = function(d) { return d ? "#ccc" : "#666"; };
// x-scale
x = d3.scale.linear()
.domain([graph.xmin, graph.xmax])
.range([0, mw]),
// drag x-axis logic
downscalex = x.copy(),
downx = Math.NaN,
// y-scale (inverted domain)
y = d3.scale.linear()
.domain([graph.ymax, graph.ymin])
.nice()
.range([0, mh])
.nice(),
line = d3.svg.line()
.x(function(d, i) { return x(graph.lennard_jones_potential[i].x); })
.y(function(d, i) { return y(graph.lennard_jones_potential[i].y); }),
// drag x-axis logic
downscaley = y.copy(),
downy = Math.NaN,
dragged = null,
selected = graph.coefficients[0];
var vis = d3.select("#chart").append("svg:svg")
.attr("width", cx)
.attr("height", cy)
.append("svg:g")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")");
var plot = vis.append("svg:rect")
.attr("width", size.width)
.attr("height", size.height)
.style("fill", "#EEEEEE")
.attr("pointer-events", "all")
.call(d3.behavior.zoom().on("zoom", redraw))
.on("mousedown", function() {
if (d3.event.altKey) {
points.push(selected = dragged = d3.svg.mouse(vis.node()));
update();
d3.event.preventDefault();
d3.event.stopPropagation();
}
});
vis.append("svg:svg")
.attr("top", 0)
.attr("left", 0)
.attr("width", size.width)
.attr("height", size.height)
.attr("viewBox", "0 0 "+size.width+" "+size.height)
.append("svg:path")
.attr("class", "line")
.attr("d", line(graph.lennard_jones_potential))
// add Chart Title
if (graph.title) {
vis.append("svg:text")
.text(graph.title)
.attr("x", size.width/2)
.attr("dy","-1em")
.style("text-anchor","middle");
}
// Add the x-axis label
if (graph.xlabel) {
vis.append("svg:text")
.text(graph.xlabel)
.attr("x", size.width/2)
.attr("y", size.height)
.attr("dy","2.4em")
.style("text-anchor","middle");
}
// add y-axis label
if (graph.ylabel) {
vis.append("svg:g")
.append("svg:text")
.text( graph.ylabel)
.style("text-anchor","middle")
.attr("transform","translate(" + -50 + " " + size.height/2+") rotate(-90)");
}
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup);
//
// draw the data
//
function update() {
var epsilon_circle = vis.select("svg").selectAll("circle")
.data(graph.coefficients, function(d) { return d });
var lines = vis.select("path").attr("d", line(graph.lennard_jones_potential)),
x_extent = x.domain()[1] - x.domain()[0];
epsilon_circle.enter().append("svg:circle")
.attr("class", function(d) { return d === selected ? "selected" : null; })
.attr("cx", function(d) {
return x(d.x); })
.attr("cy", function(d) {
return y(d.y); })
.attr("r", 8.0)
// .attr("r", function(d) { return 800 / (x.domain()[1] - x.domain()[0]); })
.on("mousedown", function(d) {
if (d.coefficient == "epsilon") {
d.x = graph.r_min;
} else {
d.y = 0
}
selected = dragged = d;
update();
});
epsilon_circle
.attr("class", function(d) { return d === selected ? "selected" : null; })
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); });
epsilon_circle.exit().remove();
if (d3.event && d3.event.keyCode) {
d3.event.preventDefault();
d3.event.stopPropagation();
}
}
graph.coefficients[0].y;
function mousemove() {
if (!dragged) return;
var m = d3.svg.mouse(vis.node()),
newx, newy;
if (dragged.coefficient == "epsilon") {
newx = graph.r_min;
newy = y.invert(Math.max(0, Math.min(size.height, m[1])));
if (newy > 0) { newy = 0 };
update_epsilon(newy);
} else {
newy = 0;
newx = x.invert(Math.max(0, Math.min(size.width, m[0])));
if (newx < 1.1) { newx = 1.1 };
if (newx > 5.0) { newx = 5.0 };
update_sigma(newx);
graph.coefficients[0].x = graph.r_min;
}
dragged.x = newx;
dragged.y = newy;
update();
}
function mouseup() {
if (!dragged) return;
mousemove();
dragged = null;
}
redraw();
function redraw() {
if (d3.event && d3.event.transform && isNaN(downx) && isNaN(downy)) {
d3.event.transform(x, y);
};
var fx = x.tickFormat(10),
fy = y.tickFormat(10);
// Regenerate x-ticks…
var gx = vis.selectAll("g.x")
.data(x.ticks(10), String)
.attr("transform", tx);
gx.select("text")
.text(fx);
var gxe = gx.enter().insert("svg:g", "a")
.attr("class", "x")
.attr("transform", tx);
gxe.append("svg:line")
.attr("stroke", stroke)
.attr("y1", 0)
.attr("y2", size.height);
gxe.append("svg:text")
.attr("y", size.height)
.attr("dy", "1em")
.attr("text-anchor", "middle")
.text(fx)
.on("mouseover", function(d) { d3.select(this).style("font-weight", "bold");})
.on("mouseout", function(d) { d3.select(this).style("font-weight", "normal");})
.on("mousedown", function(d) {
var p = d3.svg.mouse(vis[0][0]);
downx = x.invert(p[0]);
downscalex = null;
downscalex = x.copy();
// d3.behavior.zoom().off("zoom", redraw);
});
gx.exit().remove();
// Regenerate y-ticks…
var gy = vis.selectAll("g.y")
.data(y.ticks(10), String)
.attr("transform", ty);
gy.select("text")
.text(fy);
var gye = gy.enter().insert("svg:g", "a")
.attr("class", "y")
.attr("transform", ty)
.attr("background-fill", "#FFEEB6");
gye.append("svg:line")
.attr("stroke", stroke)
.attr("x1", 0)
.attr("x2", size.width);
gye.append("svg:text")
.attr("x", -3)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(fy)
.on("mouseover", function(d) { d3.select(this).style("font-weight", "bold");})
.on("mouseout", function(d) { d3.select(this).style("font-weight", "normal");})
.on("mousedown", function(d) {
var p = d3.svg.mouse(vis[0][0]);
downy = y.invert(p[1]);
downscaley = y.copy();
// d3.behavior.zoom().off("zoom", redraw);
});
gy.exit().remove();
update();
}
//
// axis scaling
//
// attach the mousemove and mouseup to the body
// in case one wanders off the axis line
d3.select('body')
.on("mousemove", function(d) {
var p = d3.svg.mouse(vis[0][0]);
if (!isNaN(downx)) {
var rupx = downscalex.invert(p[0]),
xaxis1 = downscalex.domain()[0],
xaxis2 = downscalex.domain()[1],
xextent = xaxis2 - xaxis1;
if (rupx !== 0) {
var changex, dragx_factor, new_domain;
dragx_factor = xextent/downx;
changex = 1 + (downx / rupx - 1) * (xextent/(downx-xaxis1))/dragx_factor;
new_domain = [xaxis1, xaxis1 + (xextent * changex)];
x.domain(new_domain);
redraw();
}
d3.event.preventDefault();
d3.event.stopPropagation();
}
if (!isNaN(downy)) {
var rupy = downscaley.invert(p[1]),
yaxis1 = downscaley.domain()[1],
yaxis2 = downscaley.domain()[0],
yextent = yaxis2 - yaxis1;
if (rupy !== 0) {
var changey, dragy_factor, new_range;
dragy_factor = yextent/downy;
changey = 1 - (rupy / downy - 1) * (yextent/(downy-yaxis1))/dragy_factor;
new_range = [yaxis1 + (yextent * changey), yaxis1];
y.domain(new_range);
redraw();
}
d3.event.preventDefault();
d3.event.stopPropagation();
}
})
.on("mouseup", function(d) {
if (!isNaN(downx)) {
redraw();
downx = Math.NaN;
d3.event.preventDefault();
d3.event.stopPropagation();
}
if (!isNaN(downy)) {
redraw();
downy = Math.NaN;
d3.event.preventDefault();
d3.event.stopPropagation();
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment