import random as rnd import pandas as pd import numpy as np import math from tqdm import tqdm class AutoVivification(dict): def __getitem__(self, item): try: return dict.__getitem__(self, item) except KeyError: value = self[item] = type(self)() return value # import route matrix as pandas dataframe df_route = pd.read_excel('routeMatrix.xlsx') # convert dataframe into nested dictionary dict_route = df_route.groupby('odx')[['ddx','tt','dst']].apply(lambda x: x.set_index('ddx').to_dict(orient='index')).to_dict() # print(dict_route[0][0]['tt']) default_max_weeks=50 max_weeks = int(input("Max weeks (50) : ") or default_max_weeks) # Percentage of new pwd default_p_new=0.2 p_new = input("New PWD % (0.2) : ") or default_p_new p_ar = 0.5 # Demand hours for old PWD and new PWD demand_new=[i for i in range(6,11)] demand_old=[i for i in range(2,7)] # Number of coaches and availability # num_coach = 2 coach_hour_low = 7 coach_hour_high = 7 default_coach_rate = 17 coach_rate = int(input("Coach Rate (17) : ") or default_coach_rate) work_hours = 7 default_cost_billable_hr = 36.2 cost_billable_hr = int(input("Billable Rate (36.2) : ") or default_cost_billable_hr) # Session time for PWDs in hours new_ses_time = 2 old_ses_time = 1 # AR settings # full_life = 2 #year partial_life = 0.5 #year percent_fail = 0.2 percent_func = 0.8 default_ar_unit_cost = 1100 ar_unit_cost = int(input("AR unit cost (1100) : ") or default_ar_unit_cost) default_wifi_plan = 70 #dollars wifi_plan = int(input("Wifi Plan cost/month (70) : ") or default_wifi_plan) def demand(): # generation pwd_id = 0 nvisit = 0 demand = [] dict_demand = {} i = 0 df_week_data = pd.DataFrame() for w in range(0, max_weeks): # Generate number of PWD pwd_weekly = rnd.randint(max_pwd_week - 3, max_pwd_week) new_pwd = int(p_new * pwd_weekly) old_pwd = int(pwd_weekly - new_pwd) tot_new_dem = 0 tot_old_dem = 0 # ar_sets = math.floor(p_ar * max_pwd_week) if max_pwd_week in range(max_pwds[0]-2, max_pwds[0]+2): dem_level = 'L' ar_sets = 15 if max_pwd_week in range(max_pwds[1]-2, max_pwds[1]+2): dem_level = 'M' ar_sets = 20 if max_pwd_week in range(max_pwds[2]-2, max_pwds[2]+2): dem_level = 'H' ar_sets = 25 ar_weekly = math.ceil(p_ar * pwd_weekly) new_ar = rnd.randint(0, new_pwd) old_ar = int(ar_weekly - new_ar) new_ip = int(new_pwd - new_ar) old_ip = int(old_pwd - old_ar) # Split new and old PWD # Generate hours per week for a in range(new_ar): dem = rnd.choice(demand_new) nvisits = int(dem / new_ses_time) min_visit = min(5, nvisits) day_demand = np.zeros(5) day_demand[:min_visit] = 2 np.random.shuffle(day_demand) location = rnd.choice(range(0, 81)) # Putting demand into dataframe for d in range(0, 5): tot_new_dem += day_demand[d] ar = 1 if ar_sets > 0 else 0 dict_demand[i] = {'week': w, 'day': d, 'pwd_id': pwd_id, 'pwd_type': 'new', 'demand': day_demand[d], 'location': location, 'train_type': 1, 'ar_set': ar, 'serviced': 0} i = i + 1 pwd_id += 1 ar_sets -= 1 for p in range(new_ip): dem = rnd.choice(demand_new) nvisits = int(dem / new_ses_time) min_visit = min(5, nvisits) day_demand = np.zeros(5) day_demand[:min_visit] = 2 np.random.shuffle(day_demand) location = rnd.choice(range(0, 81)) # Putting demand into dataframe for d in range(0, 5): tot_new_dem += day_demand[d] dict_demand[i] = {'week': w, 'day': d, 'pwd_id': pwd_id, 'pwd_type': 'new', 'demand': day_demand[d], 'location': location, 'train_type': 0, 'ar_set': 0, 'serviced': 0} i = i + 1 pwd_id += 1 nvisits = 0 for p in range(old_ar): dem = rnd.choice(demand_old) nvisits = int(dem / old_ses_time) min_visit = min(5, nvisits) day_demand = np.zeros(5) day_demand[:min_visit] = 1 np.random.shuffle(day_demand) location = rnd.choice(range(0, 81)) # Putting demand into dictionary first_train = True for d in range(0, 5): tot_new_dem += day_demand[d] ar = 1 if ar_sets > 0 else 0 if day_demand[d] != 0 and first_train == True: dict_demand[i] = {'week': w, 'day': d, 'pwd_id': pwd_id, 'pwd_type': 'old', 'demand': day_demand[d], 'location': location, 'train_type': 1, 'ar_set': ar, 'serviced': 0} first_train = False else: dict_demand[i] = {'week': w, 'day': d, 'pwd_id': pwd_id, 'pwd_type': 'old', 'demand': day_demand[d], 'location': location, 'train_type': 1, 'ar_set': ar, 'serviced': 0} i = i + 1 pwd_id += 1 ar_sets -= 1 for p in range(old_ip): dem = rnd.choice(demand_old) nvisits = int(dem / old_ses_time) min_visit = min(5, nvisits) day_demand = np.zeros(5) day_demand[:min_visit] = 1 np.random.shuffle(day_demand) location = rnd.choice(range(0, 81)) # Putting demand into dictionary first_train = True for d in range(0, 5): tot_new_dem += day_demand[d] if day_demand[d] != 0 and first_train == True: dict_demand[i] = {'week': w, 'day': d, 'pwd_id': pwd_id, 'pwd_type': 'old', 'demand': day_demand[d], 'location': location, 'train_type': 0, 'ar_set': 0, 'serviced': 0} first_train = False else: dict_demand[i] = {'week': w, 'day': d, 'pwd_id': pwd_id, 'pwd_type': 'old', 'demand': day_demand[d], 'location': location, 'train_type': 0, 'ar_set': 0, 'serviced': 0} i = i + 1 pwd_id += 1 week_demand = tot_new_dem + tot_old_dem demand.append(week_demand) df_week_data['demand'] = demand # Convert dataframe to dictionary # dict_demand = df_demand.to_dict('index') df_demand = pd.DataFrame.from_dict(dict_demand, "index") # Dataframe sorting week and day in ascending order df_demand = df_demand.sort_values(by=['week', 'day'], ascending=[True, True]) df_demand = df_demand[df_demand['demand'] != 0] df_demand = df_demand.reset_index(drop=True) df_demand.to_csv('demand_data.csv') return df_demand, df_week_data def service(df_demand, df_week_data, dict_route): # Shuffle dataframe rows to random order df_demand = df_demand.sample(frac=1) # Filter out demands of 0 hours df_demand = df_demand[df_demand['demand'] > 0] # Convert dataframe to dictionary dict_demand = df_demand.to_dict('index') df_day_data = pd.DataFrame(columns=['week', 'day', 'service', 'travel', 'availability']) tot_service = 0 tot_travel = 0 tot_travel_dst = 0 tot_work_hours = 0 service = [] availability = [] coach_capacity = AutoVivification() dict_half_serve = AutoVivification() dict_service = dict_demand dict_tot_data = {} avail_coach_hours = 0 half_service = 0 start_loc = 0 i = 0 # i is used for half service dict msg1 = 'week: {0} -- day:{1} -- start:{2} -- end:{3} travel:{4} -- dst:{5} -- service:{6} -- coach load:{7} -- capacity:{8}' msg2 = 'Half Service--week: {0} -- day:{1} -- start:{2} -- end:{3} travel:{4} -- dst:{5} -- service:{6} -- coach load:{7} -- capacity:{8}' for w in range(0, max_weeks): tot_week_hours = 0 tot_week_service = 0 tot_week_travel = 0 tot_week_avail = 0 tot_week_dst = 0 for d in range(0, 5): for c in range(0, num_coach): start_BAC = True tot_day_service = 0 tot_day_travel = 0 tot_day_avail = 0 # Random coach availability coach_capacity[w][d][c] = rnd.randint(coach_hour_low, coach_hour_high) coach_load = 0 sum_service = 0 sum_travel = 0 sum_dst = 0 coach_ok = True while coach_ok: restart = True while restart: restart = False # dict_service = {i: v for i, v in enumerate(dict_service.values())} for k in dict_service.keys(): if dict_service[k]['week'] == w and dict_service[k]['day'] == d and dict_service[k]['serviced'] == 0: if start_BAC == True: start_loc = 81 start_BAC = False req_loc = dict_service[k]['location'] travel_t = round((dict_route[start_loc][req_loc]['tt']) / 3600, 2) # Divide by 3600 to convert from second to hour if coach_load < coach_capacity[w][d][c] and coach_capacity[w][d][c] - coach_load >= \ dict_service[k]['demand'] + travel_t: service_t = dict_service[k]['demand'] travel_dst = round((dict_route[start_loc][req_loc]['dst']) * 0.000621371, 2) # Multiply by 0.000621371 to convert from meters to miles setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) sum_service += service_t sum_travel += travel_t sum_dst += travel_dst coach_load += service_t + travel_t + setup_t + paperwork_t start_loc = req_loc # del dict_service[k] dict_service[k]['serviced'] = 1 restart = True break if coach_capacity[w][d][c] - coach_load >= (dict_service[k]['demand']) / 2 + travel_t: service_t = (dict_service[k]['demand']) / 2 req_loc = dict_service[k]['location'] # travel_t = round((dict_route[start_loc][req_loc]['tt']) / 3600, 2) # Divide by 3600 to convert from second to hour travel_dst = round((dict_route[start_loc][req_loc]['dst']) * 0.000621371, 2) # Multiply by 0.000621371 to convert from meters to miles setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) sum_service += service_t sum_travel += travel_t sum_dst += travel_dst coach_load += service_t + travel_t + setup_t + paperwork_t # Record half serve request in a dictionary dict_half_serve[i]['week'] = w dict_half_serve[i]['day'] = d dict_half_serve[i]['pwd_id'] = dict_service[k]['pwd_id'] dict_half_serve[i]['type'] = dict_service[k]['pwd_type'] dict_half_serve[i]['demand'] = (dict_service[k]['demand']) / 2 i += 1 # del dict_service[k] dict_service[k]['serviced'] = 1 half_service += 1 restart = True break else: coach_ok = False break tot_day_service += sum_service tot_day_travel += sum_travel tot_day_avail += coach_capacity[w][d][c] df_day_data = df_day_data.append( {'week': w, 'day': d, 'service': tot_day_service, 'travel': tot_day_travel, 'availability': tot_day_avail}, ignore_index=True) avail_coach_hours += coach_capacity[w][d][c] tot_week_avail += coach_capacity[w][d][c] tot_week_service += sum_service tot_week_travel += sum_travel tot_week_hours += coach_load tot_week_dst += sum_dst availability.append(tot_week_avail) service.append(tot_week_service) tot_work_hours += tot_week_hours tot_service += tot_week_service tot_travel += tot_week_travel tot_travel_dst += tot_week_dst dict_tot_data = {'tot_work_hours': tot_work_hours, 'tot_service': tot_service, 'tot_travel': tot_travel, 'tot_travel_dst': tot_travel_dst, 'avail_coach_hours': avail_coach_hours} df_week_data['service'] = service df_week_data['availability'] = availability return df_day_data, dict_demand, dict_service, dict_tot_data, dict_half_serve def ar_service(df_demand, df_week_data): # Shuffle dataframe rows to random order df_demand = df_demand.sample(frac=1) # Filter out demands of 0 hours df_demand = df_demand[df_demand['demand'] > 0] # Convert dataframe to dictionary dict_demand = df_demand.to_dict('index') df_day_data = pd.DataFrame(columns=['week', 'day', 'service', 'travel', 'availability']) tot_service = 0 tot_work_hours = 0 service = [] availability = [] coach_capacity = AutoVivification() dict_half_serve = AutoVivification() dict_service = dict_demand dict_tot_data = {} avail_coach_hours = 0 half_service = 0 i = 0 # i is used for half service dict for w in range(0, max_weeks): tot_week_hours = 0 tot_week_service = 0 tot_week_avail = 0 for d in range(0, 5): for c in range(0, num_coach): tot_day_service = 0 tot_day_avail = 0 # Random coach availability coach_capacity[w][d][c] = rnd.randint(coach_hour_low, coach_hour_high) coach_load = 0 sum_service = 0 coach_ok = True while coach_ok: restart = True while restart: restart = False # dict_service = {i: v for i, v in enumerate(dict_service.values())} for k in dict_service.keys(): if dict_service[k]['week'] == w and dict_service[k]['day'] == d and dict_service[k]['serviced'] == 0: if coach_load < coach_capacity[w][d][c] and coach_capacity[w][d][c] - coach_load >= \ dict_service[k]['demand']: service_t = dict_service[k]['demand'] sum_service += service_t setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) coach_load += service_t + setup_t + paperwork_t # del dict_service[k] dict_service[k]['serviced'] = 1 restart = True break if coach_capacity[w][d][c] - coach_load >= (dict_service[k]['demand']) / 2: service_t = (dict_service[k]['demand']) / 2 sum_service += service_t + setup_t + paperwork_t setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) coach_load += service_t dict_half_serve[i]['week'] = w dict_half_serve[i]['day'] = d dict_half_serve[i]['pwd_id'] = dict_service[k]['pwd_id'] dict_half_serve[i]['type'] = dict_service[k]['pwd_type'] dict_half_serve[i]['demand'] = (dict_service[k]['demand']) / 2 i += 1 # del dict_service[k] dict_service[k]['serviced'] = 1 half_service += 1 restart = True break else: coach_ok = False break avail_coach_hours += coach_capacity[w][d][c] tot_week_avail += coach_capacity[w][d][c] tot_week_service += sum_service tot_week_hours += coach_load tot_day_service += sum_service tot_day_avail += coach_capacity[w][d][c] df_day_data = df_day_data.append({'week': w, 'day': d, 'service': tot_day_service, 'availability': tot_day_avail}, ignore_index=True) availability.append(tot_week_avail) service.append(tot_week_service) tot_work_hours += tot_week_hours tot_service += tot_week_service dict_tot_data = {'tot_work_hours': tot_work_hours, 'tot_service': tot_service, 'avail_coach_hours': avail_coach_hours} df_week_data['service'] = service df_week_data['availability'] = availability return df_day_data, dict_demand, dict_service, dict_tot_data, dict_half_serve def partial_ar_service(df_demand, df_week_data): # Shuffle dataframe rows to random order df_demand = df_demand.sample(frac=1) # Filter out demands of 0 hours df_demand = df_demand[df_demand['demand'] > 0] # Convert dataframe to dictionary dict_demand = df_demand.to_dict('index') df_day_data = pd.DataFrame(columns=['week', 'day', 'service', 'travel', 'availability']) tot_service = 0 tot_work_hours = 0 tot_travel = 0 tot_travel_dst = 0 service = [] availability = [] coach_capacity = AutoVivification() dict_half_serve = AutoVivification() dict_service = dict_demand dict_tot_data = {} avail_coach_hours = 0 half_service = 0 start_loc = 0 i = 0 # i is used for half service dict for w in range(0, max_weeks): tot_week_hours = 0 tot_week_service = 0 tot_week_avail = 0 tot_week_travel = 0 tot_week_dst = 0 for d in range(0, 5): for c in range(0, num_coach): start_BAC = True tot_day_service = 0 tot_day_avail = 0 # Random coach availability coach_capacity[w][d][c] = rnd.randint(coach_hour_low, coach_hour_high) coach_load = 0 sum_service = 0 sum_travel = 0 sum_dst = 0 coach_ok = True while coach_ok: restart = True while restart: restart = False # dict_service = {i: v for i, v in enumerate(dict_service.values())} for k in dict_service.keys(): if dict_service[k]['week'] == w and dict_service[k]['day'] == d and dict_service[k]['serviced'] == 0: if start_BAC == True: start_loc = 81 start_BAC = False # For PWDs that are supposed to be trained in person if dict_service[k]['train_type'] == 0: req_loc = dict_service[k]['location'] travel_t = round((dict_route[start_loc][req_loc]['tt']) / 3600, 2) # Divide by 3600 to convert from second to hour if coach_load < coach_capacity[w][d][c] and coach_capacity[w][d][c] - coach_load >= \ dict_service[k]['demand'] + travel_t: service_t = dict_service[k]['demand'] travel_dst = round((dict_route[start_loc][req_loc]['dst']) * 0.000621371, 2) # Multiply by 0.000621371 to convert from meters to miles setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) sum_service += service_t sum_travel += travel_t sum_dst += travel_dst coach_load += service_t + travel_t + setup_t + paperwork_t start_loc = req_loc # del dict_service[k] dict_service[k]['serviced'] = 1 restart = True break if coach_capacity[w][d][c] - coach_load >= (dict_service[k]['demand']) / 2: service_t = (dict_service[k]['demand']) / 2 travel_dst = round((dict_route[start_loc][req_loc]['dst']) * 0.000621371, 2) # Multiply by 0.000621371 to convert from meters to miles sum_service += service_t setup_t = round((rnd.randint(8, 15)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) coach_load += service_t + travel_t + setup_t + paperwork_t dict_half_serve[i]['week'] = w dict_half_serve[i]['day'] = d dict_half_serve[i]['pwd_id'] = dict_service[k]['pwd_id'] dict_half_serve[i]['type'] = dict_service[k]['pwd_type'] dict_half_serve[i]['demand'] = (dict_service[k]['demand']) / 2 i += 1 # del dict_service[k] dict_service[k]['serviced'] = 1 half_service += 1 restart = True break # For PWDs that are supposed to be trained with AR and receive AR equipment if dict_service[k]['train_type'] == 1 and dict_service[k]['ar_set'] == 1: if coach_load < coach_capacity[w][d][c] and coach_capacity[w][d][c] - coach_load >= \ dict_service[k]['demand']: service_t = dict_service[k]['demand'] sum_service += service_t setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) coach_load += service_t + setup_t + paperwork_t # del dict_service[k] dict_service[k]['serviced'] = 1 restart = True break if coach_capacity[w][d][c] - coach_load >= (dict_service[k]['demand']) / 2: service_t = (dict_service[k]['demand']) / 2 sum_service += service_t + setup_t + paperwork_t setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) coach_load += service_t dict_half_serve[i]['week'] = w dict_half_serve[i]['day'] = d dict_half_serve[i]['pwd_id'] = dict_service[k]['pwd_id'] dict_half_serve[i]['type'] = dict_service[k]['pwd_type'] dict_half_serve[i]['demand'] = (dict_service[k]['demand']) / 2 i += 1 # del dict_service[k] dict_service[k]['serviced'] = 1 half_service += 1 restart = True break # For PWDs that are supposed to be trained with AR but did not receive AR equipment if dict_service[k]['train_type'] == 1 and dict_service[k]['ar_set'] == 0: req_loc = dict_service[k]['location'] travel_t = round((dict_route[start_loc][req_loc]['tt']) / 3600, 2) # Divide by 3600 to convert from second to hour if coach_load < coach_capacity[w][d][c] and coach_capacity[w][d][ c] - coach_load >= \ dict_service[k]['demand'] + travel_t: service_t = dict_service[k]['demand'] travel_dst = round((dict_route[start_loc][req_loc]['dst']) * 0.000621371, 2) # Multiply by 0.000621371 to convert from meters to miles setup_t = round((rnd.randint(10, 20)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) sum_service += service_t sum_travel += travel_t sum_dst += travel_dst coach_load += service_t + travel_t + setup_t + paperwork_t start_loc = req_loc # del dict_service[k] dict_service[k]['serviced'] = 1 restart = True break if coach_capacity[w][d][c] - coach_load >= (dict_service[k]['demand']) / 2: service_t = (dict_service[k]['demand']) / 2 travel_dst = round((dict_route[start_loc][req_loc]['dst']) * 0.000621371, 2) # Multiply by 0.000621371 to convert from meters to miles sum_service += service_t setup_t = round((rnd.randint(8, 15)) / 60, 2) * 0 paperwork_t = round((rnd.randint(7, 15)) / 60, 2) coach_load += service_t + travel_t + setup_t + paperwork_t dict_half_serve[i]['week'] = w dict_half_serve[i]['day'] = d dict_half_serve[i]['pwd_id'] = dict_service[k]['pwd_id'] dict_half_serve[i]['type'] = dict_service[k]['pwd_type'] dict_half_serve[i]['demand'] = (dict_service[k]['demand']) / 2 i += 1 # del dict_service[k] dict_service[k]['serviced'] = 1 half_service += 1 restart = True break else: coach_ok = False break avail_coach_hours += coach_capacity[w][d][c] tot_week_avail += coach_capacity[w][d][c] tot_week_service += sum_service tot_week_hours += coach_load tot_week_dst += sum_dst tot_week_travel += sum_travel tot_day_service += sum_service tot_day_avail += coach_capacity[w][d][c] df_day_data = df_day_data.append({'week': w, 'day': d, 'service': tot_day_service, 'availability': tot_day_avail}, ignore_index=True) availability.append(tot_week_avail) service.append(tot_week_service) tot_work_hours += tot_week_hours tot_service += tot_week_service tot_travel += tot_week_travel tot_travel_dst += tot_week_dst dict_tot_data = {'tot_work_hours': tot_work_hours, 'tot_service': tot_service, 'tot_travel':tot_travel,'tot_travel_dst':tot_travel_dst,'avail_coach_hours': avail_coach_hours} df_week_data['service'] = service df_week_data['availability'] = availability df_demand = pd.DataFrame.from_dict(dict_service, orient='index') df_demand = df_demand.sort_values(by=['week', 'day'], ascending=[True, True]) df_demand = df_demand[df_demand['demand'] != 0] df_demand = df_demand.reset_index(drop=True) df_demand.to_csv('demand_serv_data.csv') return df_day_data, dict_demand, dict_service, dict_tot_data, dict_half_serve def service_result(dict_demand, dict_service, df_week_data, dict_tot_data, dict_half_serve, print_result=False): tot_work_hours = dict_tot_data.get('tot_work_hours') tot_service = dict_tot_data.get('tot_service') tot_travel = dict_tot_data.get('tot_travel') avail_coach_hours = dict_tot_data.get('avail_coach_hours') tot_travel_dst = dict_tot_data.get('tot_travel_dst') billable_hrs = round(tot_service, 2) revenue = cost_billable_hr * billable_hrs dict_result = {} # PWD Training Request Type Count and demand hours new_pwd = 0 old_pwd = 0 tot_demand = 0 for pwd in dict_service.values(): if pwd.get('pwd_type') == 'new': new_pwd += 1 if pwd.get('pwd_type') == 'old': old_pwd += 1 tot_demand += pwd.get('demand') num_request = len(dict_service) # Unserved PWD Training Type Count and Unserved Hours new_unserved = 0 old_unserved = 0 unserved_hours = 0 num_served = 0 for i in dict_service.values(): if i.get('pwd_type') == 'new' and i.get('serviced') == 0: new_unserved += 1 unserved_hours += i.get('demand') if i.get('pwd_type') == 'old' and i.get('serviced') == 0: old_unserved += 1 unserved_hours += i.get('demand') if i.get('serviced') == 1: num_served += 1 # Cumulate unserved hours from half served request half_serve_hours = 0 for i in dict_half_serve.values(): half_serve_hours += i.get('demand') num_unserved = new_unserved + old_unserved # num_served = num_request - num_unserved tot_unserved_hours = unserved_hours + half_serve_hours num_half_serve = len(dict_half_serve) num_full_serve = num_served - num_half_serve per_half_serve = round((num_half_serve / num_served) * 100, 2) request_coverage = round(num_served / num_request, 3) # Metrics Calculation training_coverage = round(tot_service / tot_demand, 3) travel_time_percentage = round((tot_travel / tot_work_hours) * 100, 2) service_time_percentage = round(100 - travel_time_percentage, 2) coach_utilization = round(tot_work_hours / avail_coach_hours, 2) # Cost Calculations # Default in-person training # Assuming vehicle travels at 30 MPH gas_cost = round(0 * 30 * tot_travel, 2) degradation_cost = round(0.56 * tot_travel_dst, 2) vehicle_cost = round(gas_cost + degradation_cost, 2) # Assuming coach rate is $18/hr # coach_cost = round(18 * tot_work_hours,2) coach_cost = round(num_coach * coach_rate * work_hours * 250) tot_cost = round(coach_cost + vehicle_cost, 2) unit_cost_hour = round(tot_cost / tot_service, 2) profit = revenue - tot_cost if print_result: print('\nIn-Person Training-----------------------------') print("Total PWD training request: ", num_request) print("Number of new PWD trainings: ", new_pwd) print("Number of old PWD trainings: ", old_pwd) print("\nTotal demand: ", tot_demand, " hours") print("Total service: ", round(tot_service, 2), " hours") print("Total travel: ", round(tot_travel, 2), " hours") print("Total availabe coach hours: ", avail_coach_hours) print("Total work hours: ", round(tot_work_hours, 2)) print("Training hours coverage: ", training_coverage) print("Travel time percentage: ", travel_time_percentage, "%") print("Coach utilization: ", coach_utilization) print("\nTotal PWD training requests served: ", num_served) print("Number of request fully served: ", num_full_serve) print("Number of request half served: ", num_half_serve) print("Percent half served: ", per_half_serve, "%") print("Request coverage: ", request_coverage) print("\nUnserved PWD training requests: ", num_unserved) print("Total unserved hours: ", tot_unserved_hours) print("Number of unserved new PWD trainings: ", new_unserved) print("Number of unserved old PWD trainings: ", old_unserved) print('\nGas cost: ', gas_cost) print('Vehicle degradation cost: ', degradation_cost) print('Total vehicle cost: ', vehicle_cost) print('Coach cost: ', coach_cost) print('Total Cost: ', tot_cost) print('Unit cost per hour:', unit_cost_hour) dict_result = {'new_pwd': new_pwd, 'old_pwd': old_pwd, 'tot_demand': tot_demand, 'num_request': num_request, 'new_unserved': new_unserved, 'old_unserved': old_unserved, 'unserved_hours': unserved_hours, 'half_serve_hours': half_serve_hours, 'num_unserved': num_unserved, 'num_served': num_served, 'tot_unserved_hours': tot_unserved_hours, 'num_half_serve': num_half_serve, 'num_full_serve': num_full_serve, 'ar_served' : 0, 'ar_unserved' : 0, 'no_ar_served_ip' : 0, 'no_ar_unserved' : 0, 'ip_served' : num_served, 'ip_unserved' :num_unserved, 'per_half_serve': per_half_serve, 'request_coverage': request_coverage, 'training_coverage': training_coverage, 'service_time_percentage': service_time_percentage, 'travel_time_percentage': travel_time_percentage, 'coach_utilization': coach_utilization, 'tot_work_hours': tot_work_hours, 'tot_service': tot_service, 'tot_travel': tot_travel, 'tot_travel_dst': tot_travel_dst, 'avail_coach_hours': avail_coach_hours, 'tot_cost': tot_cost, 'unit_cost_hour': unit_cost_hour, 'billable_hrs': billable_hrs, 'revenue': revenue, 'profit': profit} return dict_result, tot_cost def ar_service_result(num_ar_unit, dict_demand, dict_service, df_week_data, dict_tot_data, dict_half_serve, rep, print_result=False): tot_work_hours = dict_tot_data.get('tot_work_hours') tot_service = dict_tot_data.get('tot_service') avail_coach_hours = dict_tot_data.get('avail_coach_hours') billable_hrs = round(tot_service, 2) revenue = cost_billable_hr * billable_hrs dict_result = {} # PWD Training Request Type Count and demand hours new_pwd = 0 old_pwd = 0 tot_demand = 0 for pwd in dict_service.values(): if pwd.get('pwd_type') == 'new': new_pwd += 1 if pwd.get('pwd_type') == 'old': old_pwd += 1 tot_demand += pwd.get('demand') num_request = len(dict_service) # Unserved PWD Training Type Count and Unserved Hours new_unserved = 0 old_unserved = 0 unserved_hours = 0 num_served = 0 ar_served = 0 ar_unserved = 0 no_ar_unserved = 0 for i in dict_service.values(): if i.get('pwd_type') == 'new' and i.get('serviced') == 0: new_unserved += 1 unserved_hours += i.get('demand') if i.get('pwd_type') == 'old' and i.get('serviced') == 0: old_unserved += 1 unserved_hours += i.get('demand') if i.get('serviced') == 1: num_served += 1 if i.get('train_type') == 1 and i.get('ar_set') == 1 and i.get('serviced') == 1: ar_served += 1 if i.get('train_type') == 1 and i.get('ar_set') == 1 and i.get('serviced') == 0: ar_unserved += 1 if i.get('train_type') == 1 and i.get('ar_set') == 0 and i.get('serviced') == 0: no_ar_unserved += 1 # Cumulate unserved hours from half served request half_serve_hours = 0 for i in dict_half_serve.values(): half_serve_hours += i.get('demand') num_unserved = new_unserved + old_unserved # num_served = num_request - num_unserved tot_unserved_hours = unserved_hours + half_serve_hours num_half_serve = len(dict_half_serve) num_full_serve = num_served - num_half_serve per_half_serve = round((num_half_serve / num_served) * 100, 2) request_coverage = round(num_served / num_request, 3) # Metrics Calculation training_coverage = round(tot_service / tot_demand, 3) coach_utilization = round(tot_work_hours / avail_coach_hours, 2) # Cost Calculations # AR Training year = rep full_life = ar_life purchase_ar = (((year / full_life)) * math.floor(num_ar_unit * percent_func)) * ar_unit_cost replace_ar = (((year / partial_life)) * math.ceil(num_ar_unit * percent_fail)) * ar_unit_cost tot_ar_cost = purchase_ar + replace_ar # Assume 50% employers provide Wifi, data plan cost $70/month wifi_cost = (max_pwd_week / 2) * wifi_plan * 12 # Assuming coach rate is $18/hr # coach_cost = round(18 * tot_service,2) coach_cost = round(num_coach * coach_rate * work_hours * 250) software_cost = max_pwd_week * 12 * 12 # Assuming software cost is $12 per month for 12 months avg_ar_cost = tot_ar_cost / year tot_cost = coach_cost + avg_ar_cost + wifi_cost + software_cost unit_cost_hour = round(tot_cost / tot_service, 2) profit = revenue - tot_cost if print_result: print('\nAR Training------------------------------------') print("Total PWD training request: ", num_request) print("Number of new PWD trainings: ", new_pwd) print("Number of old PWD trainings: ", old_pwd) print("\nTotal demand: ", tot_demand, " hours") print("Total service: ", round(tot_service, 2), " hours") print("Total availabe coach hours: ", avail_coach_hours) print("Total work hours: ", round(tot_work_hours, 2)) print("Training hours coverage: ", training_coverage) print("Coach utilization: ", coach_utilization) print("\nTotal PWD training requests served: ", num_served) print("Number of request fully served: ", num_full_serve) print("Number of request half served: ", num_half_serve) print("Percent half served: ", per_half_serve, "%") print("Request coverage: ", request_coverage) print("\nUnserved PWD training requests: ", num_unserved) print("Total unserved hours: ", tot_unserved_hours) print("Number of unserved new PWD trainings: ", new_unserved) print("Number of unserved old PWD trainings: ", old_unserved) print('Average AR Equipment Cost: ', avg_ar_cost) print(str(rep) + ' Year Total Cost: ', tot_ar_cost) print('Coach cost: ', coach_cost) print('Wifi cost: ', wifi_cost) print('Total Cost: ', tot_cost) print('Unit cost per hour:', unit_cost_hour) dict_result = {'new_pwd': new_pwd, 'old_pwd': old_pwd, 'tot_demand': tot_demand, 'num_request': num_request, 'new_unserved': new_unserved, 'old_unserved': old_unserved, 'unserved_hours': unserved_hours, 'half_serve_hours': half_serve_hours, 'num_unserved': num_unserved, 'num_served': num_served, 'tot_unserved_hours': tot_unserved_hours, 'num_half_serve': num_half_serve, 'num_full_serve': num_full_serve, 'ar_served' : ar_served, 'ar_unserved' : ar_unserved, 'no_ar_served_ip' : 0, 'no_ar_unserved' : no_ar_unserved, 'ip_served' : 0, 'ip_unserved' : 0, 'per_half_serve': per_half_serve, 'request_coverage': request_coverage, 'training_coverage': training_coverage, 'service_time_percentage': 100, 'travel_time_percentage': 0, 'coach_utilization': coach_utilization, 'tot_work_hours': tot_work_hours, 'tot_service': tot_service, 'tot_travel': 0, 'tot_travel_dst': 0, 'avail_coach_hours': avail_coach_hours, 'tot_cost': tot_cost, 'unit_cost_hour': unit_cost_hour, 'billable_hrs': billable_hrs, 'revenue': revenue, 'profit': profit} return dict_result, tot_cost def partial_ar_service_result(num_ar_unit, dict_demand, dict_service, df_week_data, dict_tot_data, dict_half_serve, rep, print_result=False): tot_work_hours = dict_tot_data.get('tot_work_hours') tot_service = dict_tot_data.get('tot_service') avail_coach_hours = dict_tot_data.get('avail_coach_hours') tot_travel_dst = dict_tot_data.get('tot_travel_dst') tot_travel = dict_tot_data.get('tot_travel') billable_hrs = tot_service revenue = cost_billable_hr * billable_hrs dict_result = {} # PWD Training Request Type Count and demand hours new_pwd = 0 old_pwd = 0 tot_demand = 0 for pwd in dict_service.values(): if pwd.get('pwd_type') == 'new': new_pwd += 1 if pwd.get('pwd_type') == 'old': old_pwd += 1 tot_demand += pwd.get('demand') num_request = len(dict_service) # Unserved PWD Training Type Count and Unserved Hours new_unserved = 0 old_unserved = 0 unserved_hours = 0 num_served = 0 ar_served = 0 ar_unserved = 0 no_ar_served_ip = 0 no_ar_unserved = 0 ip_served = 0 ip_unserved = 0 for i in dict_service.values(): if i.get('pwd_type') == 'new' and i.get('serviced') == 0: new_unserved += 1 unserved_hours += i.get('demand') if i.get('pwd_type') == 'old' and i.get('serviced') == 0: old_unserved += 1 unserved_hours += i.get('demand') if i.get('serviced') == 1: num_served += 1 if i.get('train_type') == 1 and i.get('ar_set') == 1 and i.get('serviced') == 1: ar_served += 1 if i.get('train_type') == 1 and i.get('ar_set') == 1 and i.get('serviced') == 0: ar_unserved += 1 if i.get('train_type') == 1 and i.get('ar_set') == 0 and i.get('serviced') == 1: no_ar_served_ip += 1 if i.get('train_type') == 1 and i.get('ar_set') == 0 and i.get('serviced') == 0: no_ar_unserved += 1 if i.get('train_type') == 0 and i.get('serviced') == 1: ip_served += 1 if i.get('train_type') == 0 and i.get('serviced') == 0: ip_unserved += 1 # Cumulate unserved hours from half served request half_serve_hours = 0 for i in dict_half_serve.values(): half_serve_hours += i.get('demand') num_unserved = new_unserved + old_unserved # num_served = num_request - num_unserved tot_unserved_hours = unserved_hours + half_serve_hours num_half_serve = len(dict_half_serve) num_full_serve = num_served - num_half_serve per_half_serve = round((num_half_serve / num_served) * 100, 2) request_coverage = round(num_served / num_request, 3) # Metrics Calculation training_coverage = round(tot_service / tot_demand, 3) coach_utilization = round(tot_work_hours / avail_coach_hours, 2) travel_time_percentage = round((tot_travel/tot_work_hours)*100,2) service_time_percentage = round(100-travel_time_percentage,2) # Cost Calculations degradation_cost = round(0.56 * tot_travel_dst,2) # AR Training year = rep full_life = ar_life purchase_ar = (((year / full_life)) * math.floor(num_ar_unit * percent_func)) * ar_unit_cost replace_ar = (((year / partial_life)) * math.ceil(num_ar_unit * percent_fail)) * ar_unit_cost tot_ar_cost = purchase_ar + replace_ar # Assume 50% employers provide Wifi, data plan cost $70/month wifi_cost = (max_pwd_week / 2) * wifi_plan * 12 # Assuming coach rate is $18/hr # coach_cost = round(18 * tot_service,2) coach_cost = round(num_coach * coach_rate * work_hours * 250) software_cost = max_pwd_week * 12 * 12 # Assuming software cost is $12 per month for 12 months avg_ar_cost = tot_ar_cost / year tot_cost = coach_cost + avg_ar_cost + wifi_cost + software_cost + degradation_cost unit_cost_hour = round(tot_cost / tot_service, 2) profit = revenue - tot_cost if print_result: print('\nAR Training------------------------------------') print("Total PWD training request: ", num_request) print("Number of new PWD trainings: ", new_pwd) print("Number of old PWD trainings: ", old_pwd) print("\nTotal demand: ", tot_demand, " hours") print("Total service: ", round(tot_service, 2), " hours") print("Total availabe coach hours: ", avail_coach_hours) print("Total work hours: ", round(tot_work_hours, 2)) print("Training hours coverage: ", training_coverage) print("Coach utilization: ", coach_utilization) print("\nTotal PWD training requests served: ", num_served) print("Number of request fully served: ", num_full_serve) print("Number of request half served: ", num_half_serve) print("Percent half served: ", per_half_serve, "%") print("Request coverage: ", request_coverage) print("\nUnserved PWD training requests: ", num_unserved) print("Total unserved hours: ", tot_unserved_hours) print("Number of unserved new PWD trainings: ", new_unserved) print("Number of unserved old PWD trainings: ", old_unserved) print('Average AR Equipment Cost: ', avg_ar_cost) print(str(rep) + ' Year Total Cost: ', tot_ar_cost) print('Coach cost: ', coach_cost) print('Wifi cost: ', wifi_cost) print('Total Cost: ', tot_cost) print('Unit cost per hour:', unit_cost_hour) dict_result = {'new_pwd': new_pwd, 'old_pwd': old_pwd, 'tot_demand': tot_demand, 'num_request': num_request, 'new_unserved': new_unserved, 'old_unserved': old_unserved, 'unserved_hours': unserved_hours, 'half_serve_hours': half_serve_hours, 'num_unserved': num_unserved, 'num_served': num_served, 'tot_unserved_hours': tot_unserved_hours, 'num_half_serve': num_half_serve, 'num_full_serve': num_full_serve, 'ar_served' : ar_served, 'ar_unserved' : ar_unserved, 'no_ar_served_ip' : no_ar_served_ip, 'no_ar_unserved' : no_ar_unserved, 'ip_served' : ip_served, 'ip_unserved' :ip_unserved, 'per_half_serve': per_half_serve, 'request_coverage': request_coverage, 'training_coverage': training_coverage, 'service_time_percentage': service_time_percentage, 'travel_time_percentage': travel_time_percentage, 'coach_utilization': coach_utilization, 'tot_work_hours': tot_work_hours, 'tot_service': tot_service, 'tot_travel': tot_travel, 'tot_travel_dst': tot_travel_dst, 'avail_coach_hours': avail_coach_hours, 'tot_cost': tot_cost, 'unit_cost_hour': unit_cost_hour, 'billable_hrs': billable_hrs, 'revenue': revenue, 'profit': profit} return dict_result, tot_cost def result_to_dict(replication, dem_level, scenario, run, ar_life, dict_result): new_pwd = dict_result.get('new_pwd') old_pwd = dict_result.get('old_pwd') tot_demand = dict_result.get('tot_demand') num_request = dict_result.get('num_request') new_unserved = dict_result.get('new_unserved') old_unserved = dict_result.get('old_unserved') unserved_hours = dict_result.get('unserved_hours') half_serve_hours = dict_result.get('half_serve_hours') num_unserved = dict_result.get('num_unserved') num_served = dict_result.get('num_served') tot_unserved_hours = dict_result.get('tot_unserved_hours') num_half_serve = dict_result.get('num_half_serve') num_full_serve = dict_result.get('num_full_serve') per_half_serve = dict_result.get('per_half_serve') request_coverage = dict_result.get('request_coverage') training_coverage = dict_result.get('training_coverage') service_time_percentage = dict_result.get('service_time_percentage') travel_time_percentage = dict_result.get('travel_time_percentage') coach_utilization = dict_result.get('coach_utilization') tot_work_hours = dict_result.get('tot_work_hours') tot_service = dict_result.get('tot_service') tot_travel = dict_result.get('tot_travel') avail_coach_hours = dict_result.get('avail_coach_hours') tot_cost = dict_result.get('tot_cost') unit_cost_hour = dict_result.get('unit_cost_hour') tot_travel_dst = dict_result.get('tot_travel_dst') billable_hrs = dict_result.get('billable_hrs') revenue = dict_result.get('revenue') profit = dict_result.get('profit') ar_served = dict_result.get('ar_served') ar_unserved = dict_result.get('ar_unserved') no_ar_served_ip = dict_result.get('no_ar_served_ip') no_ar_unserved = dict_result.get('no_ar_unserved') ip_served = dict_result.get('ip_served') ip_unserved = dict_result.get('ip_unserved') dict_rep_result = {'Replication': replication, 'Demand Level': dem_level, 'Scenario': scenario, 'Year': run, 'AR Life': ar_life, 'Max PWD/week': max_pwd_week, 'Num Coach': num_coach, 'Num New PWD': new_pwd, 'Num Old PWD': old_pwd, 'Total Demand': tot_demand, 'Num Request': num_request, 'New Unserved': new_unserved, 'Old Unserved': old_unserved, 'Unserved Hours': unserved_hours, 'Half Served Hours': half_serve_hours, 'Num Unserved': num_unserved, 'Num Served': num_served, 'AR Served': ar_served, 'AR Unserved': ar_unserved, 'No AR Served IP': no_ar_served_ip, 'No AR Unserved': no_ar_unserved, 'IP Served': ip_served, 'IP Unserved': ip_unserved, 'Total Unserved Hrs': tot_unserved_hours, 'Num Half Served': num_half_serve, 'Num Full Serve': num_full_serve, '% Half Serve': per_half_serve, 'Request Coverage': request_coverage, 'Training Coverage': training_coverage, 'Service Time %': service_time_percentage, 'Travel Time %': travel_time_percentage, 'Coach Utilization': coach_utilization, 'Tot Work Hours': tot_work_hours, 'Tot Service': tot_service, 'Tot Travel': tot_travel, 'Tot Distance': tot_travel_dst, 'Avail Coach Hrs': avail_coach_hours, 'Total Cost': tot_cost, 'Unit Cost': unit_cost_hour, 'billable_hrs': billable_hrs, 'revenue': revenue, 'profit': profit} return dict_rep_result # Simulation default_replication = 1 replications = int(input("Number of Replications for Simulation (1) : ") or default_replication) default_years = 5 #number of years rep = int(input("Number of Years for Simulation (5) : ") or default_years) AR_life = [1,2,3,4] #ar life year # Max_pwd_week=[15,20,25] Num_coach = [1,2,3] default_max_pwd = 25 # max_pwd_setting = int(input("Max PWD/Week: ") or default_max_pwd) max_pwds = [15, 20, 25] print('Running Model...') data = {} n = 0 for replication in range(replications): # for dem in tqdm(range(len(Max_pwd_week)), position=3, leave=True): for p in range((len(max_pwds))): max_pwd_setting = max_pwds[p] max_pwd_range = list(range(max_pwd_setting - 2, max_pwd_setting + 2)) max_pwd_week = rnd.choice(max_pwd_range) if max_pwd_week in range(max_pwds[0]-2, max_pwds[0]+2): dem_level = 'L' num_ar_unit = 15 if max_pwd_week in range(max_pwds[1]-2, max_pwds[1]+2): dem_level = 'M' num_ar_unit = 20 if max_pwd_week in range(max_pwds[2]-2, max_pwds[2]+2): dem_level = 'H' num_ar_unit = 25 dfs = [] wds = [] for i in range(rep): df, wd = demand() dfs.append(df) wds.append(wd) for j in tqdm(range(len(AR_life)), position=2, leave=True): for k in tqdm(range(len(Num_coach)), position=1, leave=True): num_coach = Num_coach[k] ar_life = AR_life[j] # num_ar_unit = max_pwd_week + 2 for run in tqdm(range(rep), position=0, leave=False): dfd, d, s, hr, hs = service(dfs[run], wds[run], dict_route) # print('Default-FCFS In Person Scenario------------------') dr, c = service_result(d, s, wds[run], hr, hs) data[n] = result_to_dict(replication, dem_level, 'In Person', run, ar_life, dr) n += 1 # num_ar_unit = max_pwd_week + 2 # Setting number of ar units dfd, d, s, hr, hs = ar_service(dfs[run], wds[run]) # print('\nDefault-FCFS AR Scenario------------------') dr, c = ar_service_result(num_ar_unit, d, s, wds[run], hr, hs, rep) data[n] = result_to_dict(replication, dem_level, 'AR', run, ar_life, dr) n += 1 # num_ar_unit = math.floor(max_pwd_week * p_ar) # Reset number of ar units by percentage dfd, d, s, hr, hs = partial_ar_service(dfs[run], wds[run]) # print('\nDefault-FCFS Hybrid Scenario------------------') dr, c = partial_ar_service_result(num_ar_unit, d, s, wds[run], hr, hs, rep) data[n] = result_to_dict(replication, dem_level, 'Hybrid', run, ar_life, dr) n += 1 df_data = pd.DataFrame.from_dict(data, "index") df_data = df_data.reset_index(drop=True) df_data.to_csv('final_result.csv', index=False)