def update_other_restriction_dict(self): """"Run when updating other restrictions (via other restriction editor)""" for i in self.mod.other_dict: disp_other = self.other_restr_editor.display_other_restrictions[i] ot = Other( disp_other.name_entry.get(), ast.literal_eval(disp_other.numerator_coefs_entry.get() ), # Converts the string into a dictionary ast.literal_eval(disp_other.normalization_entry.get()), disp_other.def_low_entry.get(), disp_other.def_upp_entry.get(), disp_other.dec_pt_entry.get()) self.mod.other_dict[i] = ot self.mod.restr_dict['other_' + i].name = ot.name self.mod.restr_dict['other_' + i].normalization = ot.normalization self.mod.restr_dict['other_' + i].default_low = ot.def_low self.mod.restr_dict['other_' + i].default_upp = ot.def_upp self.mod.restr_dict['other_' + i].dec_pt = ot.dec_pt self.mw.other_select_button[i].config(text=prettify(ot.name)) self.display_restr_dict['other_' + i].set_name(prettify(ot.name)) self.display_restr_dict['other_' + i].set_default_low(ot.def_low) self.display_restr_dict['other_' + i].set_default_upp(ot.def_upp) old_variables = copy.copy(self.mod.current_recipe.variables) # Reinsert stars next to other restrictions that are variables: for t, res in old_variables.items(): if res[0:5] == 'other': self.display_restr_dict[res].select(t) self.mod.json_write_other() self.mod.json_write_restrictions() self.reset_relations = True
def calc_restrictions(self, recipe, restr_dict): # first update recipe. # Should be able to construct a reduced restr_dict from recipe t0 = time.process_time() # First, test for obvious errors if sum(self.oxide_dict[ox].flux for ox in recipe.oxides) == 0: messagebox.showerror(" ", 'No flux! You have to give a flux.') return # Run tests to see if the denominators of other restrictions are identically zero? for key in recipe.restriction_keys: if recipe.lower_bounds[key] > recipe.upper_bounds[key]: res = restr_dict[key] messagebox.showerror(" ", 'Incompatible ' + print_res_type(res.normalization) + 'bounds on ' + prettify(res.name)) return delta = 0.1**9 selected_fluxes = recipe.fluxes() sum_UMF_low = sum(recipe.lower_bounds['umf_'+ox] for ox in selected_fluxes) if sum_UMF_low > 1 + delta: messagebox.showerror(" ", 'The sum of the UMF flux lower bounds is '+str(sum_UMF_low) +'. It should be at most 1. Decrease one of the lower bounds by '+str(sum_UMF_low-1) +' or more.') #will be a problem if they're all < sum_UMF_low-1)) return sum_UMF_upp = sum(recipe.upper_bounds['umf_'+ox] for ox in selected_fluxes) if sum_UMF_upp < 1 - delta: messagebox.showerror(" ", 'The sum of the UMF flux upper bounds is '+str(sum_UMF_upp) +'. It should be at least 1. Increase one of the upper bounds by '+str(1-sum_UMF_low) +' or more.') return for t in ['mass_perc_', 'mole_perc_']: sum_t_low = sum(recipe.lower_bounds[t+ox] for ox in recipe.oxides) if sum_t_low > 100 + delta: messagebox.showerror(" ", 'The sum of the ' + prettify(t) + ' lower bounds is '+str(sum_t_low) +'. It should be at most 100. Decrease one of the lower bounds by '+str(sum_t_low-100) +' or more.') #will be a problem if they're all < sum_t_low-100) return sum_t_upp = sum(recipe.upper_bounds[t+ox] for ox in recipe.oxides) if sum_t_upp < 100 - delta: messagebox.showerror(" ", 'The sum of the ' + prettify(t) + ' upper bounds is '+str(sum_t_upp) +'. It should be at least 100. Increase one of the upper bounds by '+str(100-sum_t_upp) +' or more.') return sum_ing_low = sum(recipe.lower_bounds['ingredient_'+index] for index in recipe.ingredients) if sum_ing_low > 100 + delta: messagebox.showerror(" ", 'The sum of the ingredient lower bounds is '+str(sum_ing_low) +'. It should be at most 100. Decrease one of the lower bounds by '+str(sum_ing_low-100) +' or more.') #will be a problem if they're all < sum_ing_low-100) return sum_ing_upp = sum(recipe.upper_bounds['ingredient_'+index] for index in recipe.ingredients) if sum_ing_upp < 100 - delta: messagebox.showerror(" ", 'The sum of the ingredient upper bounds is '+str(sum_ing_upp) +'. It should be at least 100. Increase one of the upper bounds by '+str(100-sum_ing_upp) +' or more.') return #t0 = time.process_time() for index in self.ingredient_dict: ing = 'ingredient_'+index if index in recipe.ingredients: ing_low = 0.01*recipe.lower_bounds[ing] ing_upp = 0.01*recipe.upper_bounds[ing] else: ing_low = 0 ing_upp = 0 self.constraints[ing+'_lower'] = self.lp_var[ing] >= ing_low*self.lp_var['ingredient_total'] # ingredient lower bounds self.constraints[ing+'_upper'] = self.lp_var[ing] <= ing_upp*self.lp_var['ingredient_total'] # ingredient upper bounds t1 = time.process_time() # The next section takes a while, perhaps because the dictionary self.lp_var is long. # May be better to split it. for ox in self.oxide_dict: if ox in recipe.oxides: self.constraints[ox+'_umf_lower'] = self.lp_var['mole_'+ox] >= recipe.lower_bounds['umf_'+ox]*self.lp_var['fluxes_total'] # oxide UMF lower bounds self.constraints[ox+'_umf_upper'] = self.lp_var['mole_'+ox] <= recipe.upper_bounds['umf_'+ox]*self.lp_var['fluxes_total'] # oxide UMF upper bounds self.constraints[ox+'_wt_%_lower'] = self.lp_var['mass_'+ox] >= 0.01*recipe.lower_bounds['mass_perc_'+ox]*self.lp_var['ox_mass_total'] # oxide weight % lower bounds self.constraints[ox+'_wt_%_upper'] = self.lp_var['mass_'+ox] <= 0.01*recipe.upper_bounds['mass_perc_'+ox]*self.lp_var['ox_mass_total'] # oxide weight % upper bounds self.constraints[ox+'_mol_%_lower'] = self.lp_var['mole_'+ox] >= 0.01*recipe.lower_bounds['mole_perc_'+ox]*self.lp_var['ox_mole_total'] # oxide mol % lower bounds self.constraints[ox+'_mol_%_upper'] = self.lp_var['mole_'+ox] <= 0.01*recipe.upper_bounds['mole_perc_'+ox]*self.lp_var['ox_mole_total'] # oxide mol % upper bounds else: try: del self.constraints[ox+'_umf_lower'] del self.constraints[ox+'_umf_upper'] del self.constraints[ox+'_wt_%_lower'] del self.constraints[ox+'_wt_%_upper'] del self.constraints[ox+'_mol_%_lower'] del self.constraints[ox+'_mol_%_upper'] except: pass ## if 'KNaO' in self.oxides: ## prob += self.lp_var['KNaO_umf'] == self.lp_var['K2O_umf'] + self.lp_var['Na2O_umf'] ## prob += self.lp_var['KNaO_wt_%'] == lp_var['K2O_wt_%'] + lp_var['Na2O_wt_%'] for index in self.other_dict: if index in recipe.other: other_norm = self.linear_combination(self.other_dict[index].normalization) self.constraints['other_'+index+'_lower'] = self.lp_var['other_'+index] >= recipe.lower_bounds['other_'+index]*other_norm # lower bound self.constraints['other_'+index+'_upper'] = self.lp_var['other_'+index] <= recipe.upper_bounds['other_'+index]*other_norm # upper bound else: try: del self.constraints['other_'+index+'_lower'] del self.constraints['other_'+index+'_upper'] except: pass # Finally, we're ready to calculate the upper and lower bounds imposed on all the variables calc_bounds = {-1:{}, 1:{}} for key in recipe.restriction_keys: res = restr_dict[key] norm = self.linear_combination(res.normalization) self.constraints['normalization'] = norm == 1 # Apply the normalization of the restriction in question # Apparently this doesn't slow things down a whole lot for eps in [1, -1]: # calculate lower and upper bounds. self += eps*self.lp_var[res.objective_func], res.name self.writeLP('constraints.lp') self.solve(solver) if self.status == 1: calc_bounds[eps][key] = eps*pulp.value(self.objective) #prob.writeLP('constraints.lp') else: messagebox.showerror(" ", LpStatus[self.status]) self.writeLP('constraints.lp') return t2 = time.process_time() #print(t2 - t0) return {'lower':calc_bounds[-1], 'upper':calc_bounds[1]}
def __init__(self): self.mod = Model() self.mw = MainWindow() for button, t in [(self.mw.unity_radio_button, 'umf_'), \ (self.mw.percent_wt_radio_button, 'mass_perc_'), \ (self.mw.percent_mol_radio_button, 'mole_perc_')]: button.config(command=partial(self.update_oxide_entry_type, t)) self.mw.file_menu.add_command(label="Recipes", command=self.open_recipe_menu) self.mw.file_menu.add_command(label="Save", command=self.save_recipe) self.mw.file_menu.add_command(label="Save as new recipe", command=self.save_new_recipe) #self.mw.options_menu.add_command(label="Edit Oxides", command=None) self.mw.options_menu.add_command(label="Edit Ingredients", command=self.open_ingredient_editor) self.mw.options_menu.add_command( label="Edit Other Restrictions", command=self.open_other_restriction_editor) #self.mw.options_menu.add_command(label="Restriction Settings", command=self.open_ingredient_editor) self.mw.calc_button.config(command=self.calc_restr) # Create and grid ingredient selection buttons: for r, i in enumerate(self.mod.order["ingredients"]): self.mw.ingredient_select_button[i] = ttk.Button(self.mw.ingredient_vsf.interior, text=self.mod.ingredient_dict[i].name, \ width=20, command=partial(self.toggle_ingredient, i)) self.mw.ingredient_select_button[i].grid(row=r) # Create and grid other selection buttons: for r, j in enumerate(self.mod.order["other"]): self.mw.other_select_button[j] = ttk.Button(self.mw.other_vsf.interior, text=prettify(self.mod.other_dict[j].name), \ width=20, command=partial(self.toggle_other, j)) self.mw.other_select_button[j].grid(row=r + 1) # Create DisplayRestriction dictionary self.display_restr_dict = {} for key in self.mod.restr_keys(): self.display_restr_dict[key] = DisplayRestriction(self.mw.restriction_sf.interior, self.mw.x_lab, self.mw.y_lab, \ key, self.mod.restr_dict[key].name, self.mod.restr_dict[key].default_low, self.mod.restr_dict[key].default_upp) # Open default recipe. self.open_recipe('0') self.reset_relations = True
def calc_restrictions(self, recipe, restr_dict): # First, test for obvious errors for key in recipe.restriction_keys: if recipe.lower_bounds[key] > recipe.upper_bounds[key]: res = restr_dict[key] messagebox.showerror( " ", 'Incompatible ' + print_res_type(res.normalization) + 'bounds on ' + prettify(res.name)) return delta = 0.1**9 selected_fluxes = recipe.fluxes() sum_UMF_low = sum(recipe.lower_bounds['umf_' + ox] for ox in selected_fluxes) if sum_UMF_low > 1 + delta: messagebox.showerror( " ", 'The sum of the UMF flux lower bounds is ' + str(sum_UMF_low) + '. It should be at most 1. Decrease one of the lower bounds by ' + str(sum_UMF_low - 1) + ' or more.' ) #will be a problem if they're all < sum_UMF_low-1)) return sum_UMF_upp = sum(recipe.upper_bounds['umf_' + ox] for ox in selected_fluxes) if sum_UMF_upp < 1 - delta: messagebox.showerror( " ", 'The sum of the UMF flux upper bounds is ' + str(sum_UMF_upp) + '. It should be at least 1. Increase one of the upper bounds by ' + str(1 - sum_UMF_low) + ' or more.') return for t in ['mass_perc_', 'mole_perc_']: sum_t_low = sum(recipe.lower_bounds[t + ox] for ox in recipe.oxides) if sum_t_low > 100 + delta: messagebox.showerror( " ", 'The sum of the ' + prettify(t) + ' lower bounds is ' + str(sum_t_low) + '. It should be at most 100. Decrease one of the lower bounds by ' + str(sum_t_low - 100) + ' or more.' ) #will be a problem if they're all < sum_t_low-100) return sum_t_upp = sum(recipe.upper_bounds[t + ox] for ox in recipe.oxides) if sum_t_upp < 100 - delta: messagebox.showerror( " ", 'The sum of the ' + prettify(t) + ' upper bounds is ' + str(sum_t_upp) + '. It should be at least 100. Increase one of the upper bounds by ' + str(100 - sum_t_upp) + ' or more.') return sum_ing_low = sum(recipe.lower_bounds['ingredient_' + index] for index in recipe.ingredients) if sum_ing_low > 100 + delta: messagebox.showerror( " ", 'The sum of the ingredient lower bounds is ' + str(sum_ing_low) + '. It should be at most 100. Decrease one of the lower bounds by ' + str(sum_ing_low - 100) + ' or more.' ) #will be a problem if they're all < sum_ing_low-100) return sum_ing_upp = sum(recipe.upper_bounds['ingredient_' + index] for index in recipe.ingredients) if sum_ing_upp < 100 - delta: messagebox.showerror( " ", 'The sum of the ingredient upper bounds is ' + str(sum_ing_upp) + '. It should be at least 100. Increase one of the upper bounds by ' + str(100 - sum_ing_upp) + ' or more.') return t1 = time.process_time() # Inequalities: self.inequalities = [] #ing_low = self._make_matrix(recipe.lower_bounds['ingredients']) #ing_upp = self._make_matrix(recipe.upper_bounds['ingredients']) ing_low = matrix([ recipe.lower_bounds['ingredient_' + str(i)] / 100 for i in self.current_ingredients ]) ing_upp = matrix([ recipe.upper_bounds['ingredient_' + str(i)] / 100 for i in self.current_ingredients ]) self.inequalities.append( self.lp_var['ingredient'] >= ing_low * self.lp_var['ingredient_total']) # ingredient lower bounds self.inequalities.append( self.lp_var['ingredient'] <= ing_upp * self.lp_var['ingredient_total']) # ingredient lower bounds #umf_lower = self._make_matrix(recipe.lower_bounds['umf']) #umf_upper = self._make_matrix(recipe.upper_bounds['umf']) umf_lower = matrix( [recipe.lower_bounds['umf_' + ox] for ox in self.current_oxides]) umf_upper = matrix( [recipe.upper_bounds['umf_' + ox] for ox in self.current_oxides]) self.inequalities.append( self.lp_var['mole'] >= umf_lower * self.lp_var['fluxes_total']) self.inequalities.append( self.lp_var['mole'] <= umf_upper * self.lp_var['fluxes_total']) #mol_perc_lower = self._make_matrix(0.01*recipe.lower_bounds['mole']) #mol_perc_upper = self._make_matrix(0.01*recipe.upper_bounds['mole']) mole_perc_lower = matrix([ recipe.lower_bounds['mole_perc_' + ox] / 100 for ox in self.current_oxides ]) mole_perc_upper = matrix([ recipe.upper_bounds['mole_perc_' + ox] / 100 for ox in self.current_oxides ]) self.inequalities.append( self.lp_var['mole'] >= mole_perc_lower * self.lp_var['ox_mole_total']) # oxide mol % lower bounds self.inequalities.append( self.lp_var['mole'] <= mole_perc_upper * self.lp_var['ox_mole_total']) # oxide mol % upper bounds #wt_perc_lower = self._make_matrix(0.01*recipe.lower_bounds['mass']) #wt_perc_upper = self._make_matrix(0.01*recipe.upper_bounds['mass']) mass_perc_lower = matrix([ recipe.lower_bounds['mass_perc_' + ox] / 100 for ox in self.current_oxides ]) mass_perc_upper = matrix([ recipe.upper_bounds['mass_perc_' + ox] / 100 for ox in self.current_oxides ]) self.inequalities.append(self.lp_var['mass'] >= mass_perc_lower * self.lp_var['ox_mass_total']) self.inequalities.append(self.lp_var['mass'] <= mass_perc_upper * self.lp_var['ox_mass_total']) for i, j in enumerate(self.current_other): res = restr_dict['other_' + j] normalization = self._linear_combination(res.normalization) other_lower = recipe.lower_bounds['other_' + j] other_upper = recipe.upper_bounds['other_' + j] self.inequalities.append( self.lp_var['other'][i] >= other_lower * normalization) self.inequalities.append( self.lp_var['other'][i] <= other_upper * normalization) # Calculate the upper and lower bounds imposed on all the variables: calc_bounds = { } #{-1:{}, 1:{}} # -1 for lower bounds, 1 for upper bounds for key in recipe.restriction_keys: calc_bounds[key] = {} res = restr_dict[key] norm = self._linear_combination(res.normalization) normalization = ( norm == 1 ) # Normalization of the restriction in question. There's a lot of # unnecessary repetition of this step, but it seems this doesn't # slow things down a whole lot opt_var = self._parser(res.objective_func) for eps, bound in zip( [1, -1], ['lower', 'upper']): # calculate lower and upper bounds. lp = op(eps * opt_var, self.relations + self.inequalities + [normalization]) lp.solve(solver='glpk', options={'glpk': { 'msg_lev': 'GLP_MSG_OFF' }}) if lp.status == 'optimal': calc_bounds[key][bound] = eps * lp.objective.value()[0] else: messagebox.showerror(" ", lp.status) return 0 return calc_bounds