[docs]deflower_convex_hull(global_grid,state_variables,conds_keys,phase_record_factory,result_array):""" Find the simplices on the lower convex hull satisfying the specified conditions in the result array. Parameters ---------- global_grid : Dataset A sample of the energy surface of the system. state_variables : List[v.StateVariable] A list of the state variables (e.g., P, T) used in this calculation. conds_keys : List A list of the keys of the conditions used in this calculation. phase_record_factory : PhaseRecordFactory PhaseRecordFactory object corresponding to this calculation. result_array : Dataset This object will be modified! Coordinates correspond to conditions axes. Returns ------- None. Results are written to result_array. Notes ----- This routine will not check if any simplex is degenerate. Degenerate simplices will manifest with duplicate or NaN indices. Examples -------- None yet. """state_variables=sorted(state_variables,key=str)local_conds_keys=[cforcinconds_keysifgetattr(c,'phase_name',None)isnotNone]str_conds_keys=[str(c)forcinconds_keys]# factored out via profilingresult_array_GM_values=result_array.GMresult_array_points_values=result_array.pointsresult_array_MU_values=result_array.MUresult_array_NP_values=result_array.NPresult_array_X_values=result_array.Xresult_array_Y_values=result_array.Yresult_array_Phase_values=result_array.Phaseglobal_grid_GM_values=global_grid.GMglobal_grid_X_values=global_grid.Xglobal_grid_Y_values=global_grid.Yglobal_grid_Phase_values=global_grid.Phasenum_comps=len(result_array.coords['component'])it=np.nditer(result_array_GM_values,flags=['multi_index'])whilenotit.finished:primary_index=it.multi_indexgrid_index=[]# Relies on being orderedforlcinlocal_conds_keys:coord_idx=conds_keys.index(lc)grid_index.append(primary_index[coord_idx])forsvinstate_variables:ifsvinconds_keys:coord_idx=conds_keys.index(sv)grid_index.append(primary_index[coord_idx])else:# free state variablegrid_index.append(0)grid_index=tuple(grid_index)idx_global_grid_X_values=global_grid_X_values[grid_index]idx_global_grid_GM_values=global_grid_GM_values[grid_index]idx_result_array_MU_values=result_array_MU_values[it.multi_index]idx_result_array_MU_values[:]=0idx_fixed_lincomb_molefrac_coefs=[]idx_fixed_lincomb_molefrac_rhs=[]idx_fixed_chempot_indices=[]forcoord_idx,str_cond_keyinenumerate(sorted(result_array.coords.keys())):try:cond_key=conds_keys[str_conds_keys.index(str_cond_key)]exceptValueError:continuerhs=result_array.coords[str_cond_key][primary_index[coord_idx]]ifisinstance(cond_key,IndependentPotential):# Already handled above in construction of grid_indexcontinueifisinstance(cond_key,SiteFraction):# Already handled above in construction of grid_indexcontinueelifisinstance(cond_key,ChemicalPotential):component_idx=result_array.coords['component'].index(str(cond_key.species))idx_fixed_chempot_indices.append(component_idx)idx_result_array_MU_values[component_idx]=rhselifisinstance(cond_key,MassFraction):# wA = k -> (1-k)*MWA*xA - k*MWB*xB - k*MWC*xC = 0component_idx=result_array.coords['component'].index(str(cond_key.species))coef_vector=np.zeros(num_comps)coef_vector-=rhscoef_vector[component_idx]+=1# multiply coef_vector times a vector of molecular weightscoef_vector=np.multiply(coef_vector,phase_record_factory.molar_masses)idx_fixed_lincomb_molefrac_coefs.append(coef_vector)idx_fixed_lincomb_molefrac_rhs.append(0.)elifisinstance(cond_key,MoleFraction):ifcond_key.phase_nameisnotNone:# Phase-local condition already handled in construction of gridcontinuecomponent_idx=result_array.coords['component'].index(str(cond_key.species))coef_vector=np.zeros(num_comps)coef_vector[component_idx]=1idx_fixed_lincomb_molefrac_coefs.append(coef_vector)idx_fixed_lincomb_molefrac_rhs.append(rhs)elifisinstance(cond_key,SystemMolesType):coef_vector=np.ones(num_comps)idx_fixed_lincomb_molefrac_coefs.append(coef_vector)idx_fixed_lincomb_molefrac_rhs.append(rhs)elifisinstance(cond_key,LinearCombination):coef_vector=np.zeros(num_comps)ifcond_key.denominator==1:forsymbol_idx,symbolinenumerate(cond_key.symbols):ifsymbol!=1:coef_idx=result_array.coords['component'].index(str(symbol.species))coef_vector[coef_idx]=cond_key.coefs[symbol_idx]else:idx_fixed_lincomb_molefrac_rhs.append(rhs-cond_key.coefs[symbol_idx])else:# This is a molar ratiodenominator_idx=cond_key.symbols.index(cond_key.denominator)forsymbol_idx,symbolinenumerate(cond_key.symbols):ifsymbol_idx==denominator_idx:coef_idx=result_array.coords['component'].index(str(symbol.species))coef_vector[coef_idx]=cond_key.coefs[symbol_idx]-rhselifsymbol!=1:coef_idx=result_array.coords['component'].index(str(symbol.species))coef_vector[coef_idx]=cond_key.coefs[symbol_idx]else:ifcond_key.coefs[symbol_idx]!=0:# Constant term for molar ratio should be zeroraiseValueError(f'Unsupported condition {cond_key}')idx_fixed_lincomb_molefrac_rhs.append(-cond_key.coefs[symbol_idx])idx_fixed_lincomb_molefrac_coefs.append(coef_vector)else:raiseValueError(f'Unsupported condition {cond_key}')idx_fixed_lincomb_molefrac_coefs=np.atleast_2d(idx_fixed_lincomb_molefrac_coefs)idx_fixed_lincomb_molefrac_rhs=np.atleast_1d(idx_fixed_lincomb_molefrac_rhs)idx_fixed_chempot_indices=np.array(idx_fixed_chempot_indices,dtype=np.uintp)idx_result_array_NP_values=result_array_NP_values[it.multi_index]idx_result_array_points_values=result_array_points_values[it.multi_index]result_array_GM_values[it.multi_index]= \
hyperplane(idx_global_grid_X_values,idx_global_grid_GM_values,idx_result_array_MU_values,idx_fixed_chempot_indices,idx_fixed_lincomb_molefrac_coefs,idx_fixed_lincomb_molefrac_rhs,idx_result_array_NP_values,idx_result_array_points_values)# Copy phase values outpoints=result_array_points_values[it.multi_index]result_array_Phase_values[it.multi_index][:num_comps]=global_grid_Phase_values[grid_index].take(points,axis=0)[:num_comps]result_array_X_values[it.multi_index][:num_comps]=global_grid_X_values[grid_index].take(points,axis=0)[:num_comps]result_array_Y_values[it.multi_index][:num_comps,:global_grid_Y_values.shape[-1]]= \
global_grid_Y_values[grid_index].take(points,axis=0)[:num_comps]# Special case: Sometimes fictitious points slip into the resultif'_FAKE_'inresult_array_Phase_values[it.multi_index]:new_energy=0.molesum=0.foridxinrange(len(result_array_Phase_values[it.multi_index])):midx=it.multi_index+(idx,)ifresult_array_Phase_values[midx]=='_FAKE_':result_array_Phase_values[midx]=''result_array_X_values[midx]=np.nanresult_array_Y_values[midx]=np.nanidx_result_array_NP_values[idx]=np.nanelse:new_energy+=idx_result_array_NP_values[idx]*global_grid.GM[np.index_exp[grid_index+(points[idx],)]]molesum+=idx_result_array_NP_values[idx]ifmolesum!=0:result_array_GM_values[it.multi_index]=new_energy/molesumit.iternext()result_array.remove('points')returnresult_array