Skip to content

Instantly share code, notes, and snippets.

@flashton2003
Created March 22, 2021 15:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save flashton2003/d6014412bf6345a17331abeace97ddce to your computer and use it in GitHub Desktop.
Save flashton2003/d6014412bf6345a17331abeace97ddce to your computer and use it in GitHub Desktop.
script to calculate costs for nanopore sequencing
import math
from copy import deepcopy
def how_many_packs(number_flow_cells, option1):
# pack_sizes is nanopore flowcell pack sizes, sorted from high to low
pack_sizes = sorted([300, 48, 24, 12, 1], reverse = True)
# pack to buy is the largest pack which is less than or equal to the number we need
pack_to_buy = [x for x in pack_sizes if x <= number_flow_cells][0]
# make a note that we need one of that pack size in the option1 dict
if pack_to_buy in option1:
option1[pack_to_buy] += 1
else:
option1[pack_to_buy] = 1
# since we have "purhcased" that many flowcells, remove them from the
# total we need
number_flow_cells -= pack_to_buy
return number_flow_cells, option1
def get_option2(packs_to_buy):
option_2 = deepcopy(packs_to_buy)
# get the smallest pack size in our current distribution
smallest = sorted(list(option_2.keys()))[0]
pack_sizes = sorted([300, 48, 24, 12, 1], reverse = True)
next_up = None
# 300 is biggest, so cant get bigger obvs
if smallest != 300:
# get the pack size up
next_up = pack_sizes[pack_sizes.index(smallest) - 1]
# delete the smallest
del option_2[smallest]
# add another pack of the size above (next_up)
if next_up in option_2:
option_2[next_up] += 1
else:
option_2[next_up] = 1
return option_2
def calculate_cost(packs):
total_cost = 0
total_number_flowcells = 0
pack_cost = {1:650, 12:6857, 24:11718, 48:17361, 300:103048}
# print(packs)
for pack in packs:
total_cost += pack_cost[pack] * packs[pack]
total_number_flowcells += pack * packs[pack]
return total_cost, total_number_flowcells
def calc_flow_cell_cost(number_flow_cells):
# going to "chip away" at the number of flow cells to figure out the
# pack size distribution we need, so take a deepcopy, so that we have original
i = deepcopy(number_flow_cells)
# use how_many_packs to figure out how many packs and of what size we need
option1 = {}
while i > 0:
i, option1 = how_many_packs(i, option1)
# give an option to pay a larger pack size, with more flowcells than we need,
# but might be a good idea because of the packsize discount
option2 = get_option2(option1)
# convert pack size distribution to actual cost, and the total number of flowcells with each option
option1_cost, option1_packs = calculate_cost(option1)
option2_cost, option2_packs = calculate_cost(option2)
# try:
option2_cost_per_extra_flowcell = (option2_cost - option1_cost) / (option2_packs - number_flow_cells)
print('*********** Flow cell costs ********************')
print('There are two options for flow cells (format is {flowcell_batch_size: number_of_packs_of_that_batch_size_required}):')
print(f'\tOption1: {option1}, which costs {option1_cost} and leaves us {option1_packs - number_flow_cells} spare flow cells')
print(f'\tOption2: {option2}, which costs {option2_cost} and leaves us {option2_packs - number_flow_cells} spare flow cells, cost per extra flowcell is {option2_cost_per_extra_flowcell}')
print('************************************************')
return option1_cost, option2_cost
def main():
# per_sample_costs = ((reagent_name, number_of_samples_in_that_pack, cost_per_pack))
# all costs are in GBP
# per sample costs are for everything that scales perfectly with number of samples
per_sample_costs = (
('qubit_reagents', 500, 195),
('qubit_tubes', 500, 51.50),
('genfind_v3_dna_extraction', 50, 235),
('nanopore_rapid_library_prep', 72, 469),
['nanopore_flow_cells', 12, 650],
('xld_plates', 1, 2)
)
# other_costs are for other bits and bobs you need around, but not
# for every sample
other_costs = (('ampure', 1200, 992),)
number_of_samples_todo = 220
total_cost = 0
for reagent in per_sample_costs:
# math.ceil finds integer above number, because if we need 4.1 packs, then we need to buy 5 packs
# reagent[1] is how many samples can process per pack
# pack_count is how many packs we need
pack_count = math.ceil(number_of_samples_todo / reagent[1])
# flowcells are complex because of discounted pricing with larger packs
if reagent[0] == 'nanopore_flow_cells':
option1_cost, option2_cost = calc_flow_cell_cost(pack_count)
# non-flowcells pretty straightforward
else:
# reagent[2] is the per pack price, pack_count is how mnay packs we need
reagent_cost = pack_count * reagent[2]
# how much leftover reagent will we have?
left_over_reagent = (pack_count * reagent[1]) - number_of_samples_todo
print(f'We need {pack_count} {reagent[0]}, at a cost of {reagent_cost}. We will have {left_over_reagent} samples worth of reagent left.')
# add to total cost
total_cost += reagent_cost
for reagent in other_costs:
print(f'We also need {reagent[0]}, which costs {reagent[2]}')
total_cost += reagent[2]
print(f'\nTotal cost of option 1 is {total_cost + option1_cost}')
print(f'Total cost of option 2 is {total_cost + option2_cost}')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment