function setup_infection_sim_full(data_params){
const {nodes, edges, node_quadtree} = generate_network_data(
data_params.num_nodes.val,
build_poisson_mixture([
{mean: data_params.avg_num_edges.val, size: 1 - data_params.prop_popular_nodes.val},
{mean: data_params.avg_num_edges_pop.val, size: data_params.prop_popular_nodes.val}
])
);
function* run_simulation(sim_params, initial_infections = [], sim_speed = 1){
let time = 1;
const hist = [{
total_infections: initial_infections.length,
new_infections: initial_infections,
n_new: initial_infections.length,
potential_infectors: initial_infections.length,
time: time++
}];
nodes.forEach(node => {
node.infected = false;
node.inactive = false;
node.time_infected = -1;
node.uninfected_neighbors = [...node.neighbors];
});
initial_infections.forEach(id => {
const node = nodes[id];
node.infected = true;
node.time_infected = time;
node.uninfected_neighbors = node.neighbors.filter(n => !(initial_infections.includes(n.id)));
});
const potential_infectors = new Set(initial_infections);
const infections = new Set(initial_infections);
while(potential_infectors.size > 0 && infections.size < nodes.length) {
const no_longer_infecting = [];
const new_infections = new Set();
const expose_node = node => {
const was_infected = Math.random() < sim_params.prob_of_infect.val;
if(was_infected) new_infections.add(node.id);
};
potential_infectors.forEach(id => {
const node = nodes[id];
node.uninfected_neighbors.forEach(expose_node);
const has_long_edge = Math.random() < sim_params.prob_of_long_edge.val;
if (has_long_edge){
const random_node = nodes[Math.floor(Math.random() * nodes.length)];
const is_infectable = !(random_node.id === node.id || random_node.infected);
if(is_infectable) expose_node(random_node);
}
if(time >= node.time_infected + sim_params.contagious_period.val) no_longer_infecting.push(id);
});
const set_inactive = id => {
nodes[id].inactive = true;
potential_infectors.delete(id);
}
no_longer_infecting.forEach(set_inactive);
new_infections.forEach( id => {
infections.add(id);
const node = nodes[id];
node.infected = true;
node.time_infected = time;
node.uninfected_neighbors = node.neighbors.filter(n => !(n.infected || new_infections.has(n.id)));
if(node.uninfected_neighbors.length !== 0) potential_infectors.add(id);
node.neighbors.forEach(neighbor => {
neighbor.uninfected_neighbors = neighbor.uninfected_neighbors.filter(n => n.id !== id);
if(neighbor.uninfected_neighbors.length == 0) set_inactive(neighbor.id);
});
});
hist.push({total_infections: infections.size,
new_infections: [...new_infections],
n_new: new_infections.size,
potential_infectors: potential_infectors.size,
time: time});
yield Promises.tick(sim_speed, {ids: [...infections],
history: hist,
timestep: time++,
running: true,
settings: sim_params,
status: "running"});
}
yield Promises.tick(sim_speed, {ids: [...infections],
history: hist,
timestep: hist.length,
settings: sim_params,
status: hist.length == 1 ? "waiting": "finished"});
}
return {nodes,
edges,
node_quadtree,
run_simulation,
settings: data_params};
}