Newer
Older
ecologia / analysis / demography / population.py
#!/usr/bin/python3
'''
This script takes an Ecologia log file as its input, extracts the population
levels at each update and outputs this as a .csv file which can then be converted
to .ods or further analysed with an R script.

Written for Ecologia 1.0

@author Daniel Vedder
@date 6/4/2015
'''

import os
import sys

global version
global update_count
global generation_count
global carnivore_count
global herbivore_count
global kill_rate
global grass_density
global src_file
global out_file

version = "0.3"


'''
Load the Ecologia log file, extracting the interesting lines.
'''
def load_log():
    global src_file
    global update_count, generation_count
    global herbivore_count, carnivore_count
    global kill_rate, grass_density
    try:
        log_file = open(src_file, "r")
        line = log_file.readline()
        while line:
            if "Executing update" in line:
                next_count = line[line.find("Executing update ")+17:-1]
                update_count.append(int(next_count))
            elif "Herbivore count" in line:
                next_count = line[line.find("Herbivore count: ")+17:-1]
                herbivore_count.append(int(next_count))
            elif "Carnivore count" in line:
                next_count = line[line.find("Carnivore count: ")+17:-1]
                carnivore_count.append(int(next_count))
            elif "Carnivore hunt success rate: " in line:
                next_count = line[line.find("rate: ")+6:-2]
                kill_rate.append(int(next_count))
            elif "Average grass density: " in line:
                next_count = line[line.find("density: ")+9:-2]
                grass_density.append(next_count)
            elif "Generation counter: " in line:
                next_count = line[line.find("counter: ")+9:-1]
                generation_count.append(next_count)
            line = log_file.readline()
        log_file.close()
    except IOError:
        print("Reading file '"+src_file+"' failed!")
        sys.exit()
            
'''
If we have the data of long runs, we need to compact them or they won't fit onto
a single spreadsheet. (LibreOffice can only deal with ~1000 data points.)
'''
def compact():
    global update_count, generation_count
    global herbivore_count, carnivore_count
    global kill_rate, grass_density
    if len(update_count) < 1000:
        return
    new_update_count = [update_count[0]]
    new_herbivore_count = [herbivore_count[0]]
    new_carnivore_count = [carnivore_count[0]]
    new_kill_rate = [kill_rate[0]]
    new_grass_density = [grass_density[0]]
    new_generation_count = [generation_count[0]]
    for i in range(1, len(update_count)):
        if (i+1)%10 == 0:
            new_update_count.append(update_count[i])
            new_herbivore_count.append(herbivore_count[i])
            new_carnivore_count.append(carnivore_count[i])
            new_kill_rate.append(kill_rate[i])
            new_grass_density.append(grass_density[i])
            new_generation_count.append(generation_count[i])
    update_count = new_update_count
    herbivore_count = new_herbivore_count
    carnivore_count = new_carnivore_count
    kill_rate = new_kill_rate
    grass_density = new_grass_density
    generation_count = new_generation_count
    compact() #Keep going until the data set is small enough

'''
Save the accumulated data to a .csv file
'''
def save_csv():
    global out_file
    global update_count, generation_count
    global herbivore_count, carnivore_count
    global kill_rate, grass_density
    #Accumulate all the data
    data = "Updates"
    for u in update_count:
        data = data+","+str(u)
    data = data+"\nHerbivores"
    for h in herbivore_count:
        data = data+","+str(h)
    data = data+"\nCarnivores"
    for c in carnivore_count:
        data = data+","+str(c)
    data = data+"\nKill rate"
    for k in kill_rate:
        data = data+","+str(k)
    data = data+"\nGrass density"
    for g in grass_density:
        data = data+","+str(g)
    data = data+"\nGenerations"
    for n in generation_count:
        data = data+","+str(n)
    #Then write it to file
    try:
        csv_file = open(out_file, "w")
        csv_file.write(data)
        csv_file.close()
    except IOError:
        print("Writing "+out_file+" failed!")
        sys.exit()

'''
Save the data into a table (.txt) for R
'''
def save_table():
    global out_file
    global update_count, generation_count
    global herbivore_count, carnivore_count
    global kill_rate, grass_density
    #Accumulate the data
    data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations"
    for i in range(len(update_count)):
        data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i])
        data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i])
    #Then write it to file
    try:
        table_file = open(out_file, "w")
        table_file.write(data)
        table_file.close()
    except IOError:
        print("Writing "+out_file+" failed!")
        sys.exit()

'''
Print a help text
'''
def print_help():
    help_text = """Ecologia population tracker, version """+version+"""

Commandline parameters:
    --version -v		Show the version number and exit
	--help -h 			Show this help text and exit
	
	--logfile <file>	Use <file> as the input logfile
	--outfile <file>	Output to <file>
	--table	  			Output data as a table (.txt)
	--csv				Output data as a csv file
	--ods			Convert the data to ods via csv
	--compact		Compact large data sets (automatic for ods)"""
    print(help_text)

def main():
    global src_file
    global out_file
    global update_count, generation_count
    global carnivore_count, herbivore_count
    global kill_rate, grass_density
    src_file = "ecologia.log"
    #Choose the right default output file name
    if "--table" in sys.argv: out_file = "populations.txt"
    elif "--csv" in sys.argv or "--ods" in sys.argv: out_file = "populations.csv"
    else: raise Exception("Invalid file type specified!")
    #Let the user override default options
    if "--logfile" in sys.argv:
        src_file = sys.argv[sys.argv.index("--logfile")+1]
    if "--out-file" in sys.argv:
        out_file = sys.argv[sys.argv.index("--out-file")+1]
        #The chosen file ending determines the output type
        if out_file[-4:] == ".txt":
            sys.argv.append("--table")
        elif out_file[-4:] == ".csv" or outfile[-4:] == ".ods":
            sys.argv.append("--csv")
        #XXX This might give a conflict with the section above
        if "--table" in sys.argv and out_file[-4:] != ".txt":
            out_file = out_file+".txt"
        elif "--csv" in out_file[-4:] != ".csv":
            out_file = out_file+".csv"
    #Initialise variables
    update_count, generation_count = [], []
    carnivore_count, herbivore_count = [], []
    kill_rate, grass_density = [], []
    #Do the actual work
    load_log()
    if "--compact" in sys.argv or "--ods" in sys.argv: compact()
    if "--table" in sys.argv: save_table()
    elif "--csv" in sys.argv or "--ods" in sys.argv:
        save_csv()
        if "--ods" in sys.argv:
            os.system("libreoffice --headless --convert-to ods "+out_file)

if __name__ == "__main__":
    if "--version" in sys.argv or "-v" in sys.argv:
        print("Ecologia population tracker, version "+version)
        sys.exit()
    elif "--help" in sys.argv or "-h" in sys.argv:
        print_help()
        sys.exit()
    else:
        main()