Mathematical models of disease often help us understand the epidemiology of disease—how a disease epidemic spreads within a population and how measures such as vaccination can control that spread. This post, however, considers the virology and immunology of HIV rather than its epidemiology. The discussion that follows focuses on the actions of HIV within a single human host.
xxxxxxxxxx
<html>
<head>
<meta charset='utf-8'>
<title>Mathematical Models of HIV, Part 1</title>
<link href='https://fonts.googleapis.com/css?family=Varela'
rel='stylesheet' type='text/css'>
<style>
body { font-family: Varela,sans-serif; color: #444; }
</style>
</head>
<body>
<script src='https://d3js.org/d3.v3.min.js'></script>
<script>
// Define the dimensions of the visualization. The
// width and height dimensons are conventional for
// visualizations displayed on https://jsDataV.is.
var margin = {top: 20, right: 10, bottom: 48, left: 60},
width = 636 - margin.left - margin.right,
height = 476 - margin.top - margin.bottom;
// Create the SVG stage for the visualization and
// define its dimensions. Web browsers don't require
// the full SVG attributes, but adding them makes it
// easier to extract the SVG from a web page and
// insert it into a stand-alone file.
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("viewBox", "0 0 " +
(width + margin.left + margin.right) + " " +
(height + margin.top + margin.bottom) )
.attr("version", "1.1")
.attr("xmlns", "https://www.w3.org/2000/svg");
// Within the SVG container, add a group element (<g>)
// that can be transformed via a translation to account
// for the margins. This element is restricted to the
// clipping path.
var g = svg.append("g")
.attr("transform", "translate(" + margin.left +
"," + margin.top + ")");
// Parameters for the simulated system. Explanations
// for these parameters may be found in the associated
// text (README.md).
var d = 0.01, // death rate of healthy cells
a = 1.00, // death rate of infected cells
u = 23.0, // death rate of free virus particles
k = 1000, // rate of virus generation
lambda = 100000, // rate of healthy cell generation
beta = 2e-8; // viral efficiency
// The derived parameters that the simulation actually
// used.
var alpha = a / d, // α = a/d
epsilon = d / u; // ε = d/u
// Although we're going to try different values for R0
// in the graph construction, go ahead and define the
// realistic derived value as a pladeholder.
var R0 = (k * lambda * beta) / (a * d * u);
// We're using a range of values for R0. Generate a
// logarithmic range from 0.1 to 10 with 1.0 in the
// center. In addition to the R0 values, we also define
// a diverging color scale.
var R0Range = d3.range(-1,1.25,0.25).map(function(i) {
return Math.pow(10,i); }
);
var R0Colors = ['#006363','#2e8080','#619e9d','#98b9b9',
'#888','#d4a3b8','#ce749a','#bf4278','#a5004b'];
// Since we're going to graph the value of v on a
// logarithmic scale, define a minimum value greater
// than zero.
var vMin = 1e-8;
// Convenient ratios to convert from system state to
// meaningful physical quantities.
var ul2person = 5e6, // adults have about 5L of blood
t2days = 1 / d; // convert system time to days
// The system we're simulating is three differential
// equations. To keep the function definitions clean,
// we get the system parameter values from globals
// instead of function parameters.
function dx_dt(x,y,v) { return 1 - x - R0 * x * v; };
function dy_dt(x,y,v) { return R0 * x * v - alpha * y; };
function dv_dt(x,y,v) { return ( alpha * y - v ) / epsilon; };
// The time increment used in the simation.
var dt = 0.0001;
// Here's the function that actually simulates the system.
// It's inputs are the initial value for v, the number of
// points to generate, and the number of time steps between
// each point.
var simulate = function(v0, numPoints, numSteps) {
var result = [],
prev = { x: 1, y:0, v: v0, t: 0 };
for (var i=0; i<numPoints; i++) {
// At the beginning of each t-cycle, add a new
// data point to the results. Convert from the
// derived units used in the simulation to
// meaningful physical quantities when we do so.
result.push({
t: prev.t * t2days,
v: prev.v * ul2person
});
// Now quickly step through all the individual
// delta-t values for the current t-cycle.
for (var j=0; j<numSteps; j++) {
// Make sure none of the values go negative
// (and that v stays above the minimum).
var x = prev.x + dx_dt(prev.x, prev.y, prev.v) * dt,
y = prev.y + dy_dt(prev.x, prev.y, prev.v) * dt,
v = prev.v + dv_dt(prev.x, prev.y, prev.v) * dt;
// Make sure none of the values go negative
// (and that v stays above the minimum).
prev.x = Math.max(0,x);
prev.y = Math.max(0,y);
prev.v = Math.max(vMin,v);
prev.t += dt;
}
}
return result;
};
// Generate a separate dataset for each value of R0
// that we're graphing.
var datasets = R0Range.map(function(r0, i) {
// If we're less than half-way through the R0
// range, then start with a large virus population
// (a billion free virus particles). Otherwise,
// start with a single virus particle in the body.
var v0 = i <= (R0Range.length/2) ? 1e9/ul2person : 1/ul2person;
// Set the value for R0 and run the simulation.
R0 = r0;
return simulate(v0, width, 25);
});
// Define the scales that map a data value to a
// position on the vertical axis and to a time
// on the horizontal axis.
var v = d3.scale.log(),
t = d3.scale.linear();
// Set the scales
v.domain([d3.min(datasets, function(data) {
return d3.min(data, function(d) { return d.v; });
}), d3.max(datasets, function(data) {
return d3.max(data, function(d) { return d.v; });
})]).range([height,0]);
t.domain([0, datasets[0][datasets[0].length-1].t])
.range([0, width]);
// Define a convenience function to create a line on
// the chart. The horizontal axis (which D3 refers
// to as `x`) are the time values, and the vertical
// axis (which D3 refers to as `y`) are the x-values.
// The result is that `line` is function that, when
// passed a selection with an associated array of
// data points, returns an SVG path whose coordinates
// match the t- and x-scales of the chart.
var line = d3.svg.line()
.x(function(d) { return t(d.t); })
.y(function(d) { return v(d.v); });
// Create a selection for the data set
// and add the data to it. For each dataset,
// add a line to the chart.
var lines = g.selectAll(".line")
.data(datasets);
lines.enter().append("path")
.classed("line", true)
.attr("fill", "none")
.attr("stroke-width", "1px")
.attr("stroke", function(d,i) { return R0Colors[i]; })
.attr("d", line);
// Define a function that will create the axes
// when passed a data selection.
var vAxis = d3.svg.axis()
.scale(v)
.orient("left")
.ticks(5,"s");
var tAxis = d3.svg.axis()
.scale(t)
.tickSize(5, 0)
.orient("bottom");
// Add the v-axis.
svg.append("g")
.attr("class", "v axis")
.attr("transform", "translate("+margin.left+","+margin.top+")")
.call(vAxis)
.append("text")
.attr("x", -50)
.attr("y", -10)
.text("Free Virus Particles");
// Add the t-axis.
svg.append("g")
.attr("class", "t axis")
.attr("transform", "translate("+margin.left+","+(height+margin.top)+")")
.call(tAxis)
.append("text")
.attr("x", width/2)
.attr("y", 36)
.attr("text-anchor", "middle")
.text("Days Since Infection");
// Style the axis. We could do this with CSS, but
// by using JavaScript we ensure that the styles
// are embedded in the SVG itself. That's helpful
// if we ever want to extract the SVG from the web
// page as an image.
svg.selectAll(".axis line, .axis path")
.attr("fill", "none")
.attr("stroke", "#bbbbbb")
.attr("stroke-width", "2px")
.attr("shape-rendering", "crispEdges");
svg.selectAll(".axis text")
.attr("fill", "#444")
.attr("font-size", "14");
svg.selectAll(".axis .tick line")
.attr("stroke", "#d0d0d0")
.attr("stroke-width", "1");
// Finally, add the legends.
var legend = svg.append("g")
.attr("transform","matrix(1,0,0,1,60,428)");
legend.append("g")
.attr("transform","matrix(0.970129,0,0,1,-45.2062,-428)")
.append("rect")
.attr("x","533.172")
.attr("y","235.802")
.attr("width","95.9953")
.attr("height","184.219")
.attr("fill","none")
.attr("stroke-width","1.35")
.attr("stroke","#d0d0d0");
var label = legend.append("g")
.attr("transform","matrix(1,0,0,1,545.146,-174.78)");
label.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("R");
label.append("text")
.attr("x","-57.393")
.attr("y","4.662")
.attr("font-size","8.162")
.attr("fill","#444444")
.text("0");
label.append("text")
.attr("x","-52.2673")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text(":");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-174.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("0.10");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-74.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("1.78");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-154.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("0.18");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-54.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("3.16");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-134.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("0.32");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-34.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("5.62");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-114.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("0.56");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-94.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("1.00");
legend.append("g")
.attr("transform","matrix(1,0,0,1,571.361,-14.78)")
.append("text")
.attr("x","-67.361")
.attr("y","0")
.attr("font-size","14")
.attr("fill","#444444")
.text("10.0");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1996,-460.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#006363");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1629,-360.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#d4a3b8");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1996,-440.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#2e8080");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1629,-340.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#ce749a");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1996,-420.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#619e9d");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1629,-320.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#bf4278");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1996,-400.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#98b9b9");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1629,-300.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#a5004b");
legend.append("g")
.attr("transform","matrix(0.972202,-6.80854e-18,-6.80854e-18,1,19.1996,-380.396)")
.append("path")
.attr("d","M532.644,280.396l20.5719,0")
.attr("fill","none")
.attr("stroke-width","2.7")
.attr("stroke","#888888");
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js