Skip to content
Snippets Groups Projects
shed.py 3.88 KiB
Newer Older
Jarrod Pas's avatar
Jarrod Pas committed
"""Example to run a batch of simlations on SHED data."""

__authors__ = 'Jarrod Pas <j.pas@usask.ca>, Hunter McConnell <hunter.mcconnell@usask.ca>'
import os
from random import randint
Jarrod Pas's avatar
Jarrod Pas committed
import sys
import csv
Jarrod Pas's avatar
Jarrod Pas committed
from argparse import ArgumentParser
Jarrod Pas's avatar
Jarrod Pas committed
from collections import namedtuple
Jarrod Pas's avatar
Jarrod Pas committed
from multiprocessing import Pool
Jarrod Pas's avatar
Jarrod Pas committed
from os import path
from pprint import pprint
Jarrod Pas's avatar
Jarrod Pas committed
from pydtnsim import Network, RandomTraffic, Node, EpidemicNode, CSVTrace
from pydtnsim.community import BubbleKCliqueNode, BubbleLouvainNode
from pydtnsim.community import HCBFKCliqueNode, HCBFLouvainNode
Jarrod Pas's avatar
Jarrod Pas committed
Simulation = namedtuple('Simulation', ['trace', 'node_type', 'seed'])
Jarrod Pas's avatar
Jarrod Pas committed
def run_simulation(simulation):
    """Run a simulation."""
    seed = simulation.seed
Jarrod Pas's avatar
Jarrod Pas committed
    csv = path.join(simulation.trace, 'contact.csv')
    metadata = path.join(simulation.trace, 'metadata.json')
    trace = CSVTrace(csv, metadata=metadata)
Jarrod Pas's avatar
Jarrod Pas committed

    epoch = 7*24*60*60  # 7 days

Jarrod Pas's avatar
Jarrod Pas committed
    node_type = simulation.node_type
Jarrod Pas's avatar
Jarrod Pas committed
    node_options = {
        'tick_rate': 5 * 60,  # 5 mins
Jarrod Pas's avatar
Jarrod Pas committed
        'epoch': epoch,
        'k': 3,
Jarrod Pas's avatar
Jarrod Pas committed
    }
    nodes = {
Jarrod Pas's avatar
Jarrod Pas committed
        node_id: simulation.node_type(**node_options)
Jarrod Pas's avatar
Jarrod Pas committed
        for node_id in range(trace.nodes)
Jarrod Pas's avatar
Jarrod Pas committed
    traffic_options = {
        'seed': seed,
        'start': epoch,
Jarrod Pas's avatar
Jarrod Pas committed
        'step': 60 * 60,  # 1 packet every 1 mins
    traffic = RandomTraffic(nodes, **traffic_options)
Jarrod Pas's avatar
Jarrod Pas committed

    network = Network(nodes, traffic=traffic, trace=trace)
    network.run()

    stats = {
Jarrod Pas's avatar
Jarrod Pas committed
        'trace': simulation.trace,
Jarrod Pas's avatar
Jarrod Pas committed
        'node_type': node_type.__name__,
        'seed': seed,
    }
    stats.update(network.stats_summary)
Jarrod Pas's avatar
Jarrod Pas committed

    # return stats because we can't pickle the network as it is a generator.
Jarrod Pas's avatar
Jarrod Pas committed
    return stats


def main(args):
Jarrod Pas's avatar
Jarrod Pas committed
    """Run simulation for each seed in args."""
Jarrod Pas's avatar
Jarrod Pas committed
    log = pprint if args['pretty'] else print
Jarrod Pas's avatar
Jarrod Pas committed
    pool = Pool()
Jarrod Pas's avatar
Jarrod Pas committed
    simulations = []
Jarrod Pas's avatar
Jarrod Pas committed

    trace = args['shed']
Jarrod Pas's avatar
Jarrod Pas committed
    node_types = [
        Node,               # direct delivery
Jarrod Pas's avatar
Jarrod Pas committed
        EpidemicNode,
        BubbleKCliqueNode,
        HCBFKCliqueNode,
        BubbleLouvainNode,
        HCBFLouvainNode,
    if args['batch'] > 1:               # batch mode with random seeds
        for _ in range(args['batch']):
            seed = randint(0, 500)
            for node_type in node_types:
                sim = Simulation(trace=trace, node_type=node_type, seed=seed)
                simulations.append(sim)
    else:
        for seed in args['seeds']:      # seed mode with inputted seeds
            for node_type in node_types:
                sim = Simulation(trace=trace, node_type=node_type, seed=seed)
                simulations.append(sim)
    results = {}

Jarrod Pas's avatar
Jarrod Pas committed
    for stats in pool.imap_unordered(run_simulation, simulations):
        if not args['quiet']:
            log(stats)
        type = stats['node_type']
        if type not in results:
            results[type] = []
        results[type].append(stats)

    # find unused filename
    i = 0
    while os.path.exists(f"results{i}.csv"):
        i += 1

    # dump sim stats in csv
    with open(f"results{i}.csv", 'w', newline='') as results_file:
        for node_type in results:
            fieldnames = results[node_type][0].keys()
            writer = csv.DictWriter(results_file, fieldnames=fieldnames)
            writer.writeheader()
            for result in results[node_type]:
                writer.writerow(result)
Jarrod Pas's avatar
Jarrod Pas committed


def parse_args(args):
Jarrod Pas's avatar
Jarrod Pas committed
    """Parse arguments."""
Jarrod Pas's avatar
Jarrod Pas committed
    parser = ArgumentParser()

    parser.add_argument('shed')
Jarrod Pas's avatar
Jarrod Pas committed
    parser.add_argument('--pretty', action='store_true')
    parser.add_argument('--quiet', '-q', action='store_true')
    parser.add_argument('--batch', '-b', 
                        metavar='BATCH', type=int, default=1)
Jarrod Pas's avatar
Jarrod Pas committed
    parser.add_argument('--seeds', '-s',
                        action='append', metavar='SEED', type=int, default=[None])
Jarrod Pas's avatar
Jarrod Pas committed

    args = parser.parse_args(args)
    return vars(args)


if __name__ == '__main__':
Jarrod Pas's avatar
Jarrod Pas committed
    sys.exit(main(parse_args(sys.argv[1:])))