def calculatePartialMolarVolume(self,elementMolarAmounts,endmemberMassDensityLaws, epsilon, constituentToEndmembersConverter=None): #print(elementMolarAmounts, epsilon) # suspend all other phases oc.setPhasesStatus(('* ',), phStat.Suspended) oc.setPhasesStatus(tuple(self.__phasenames), phStat.Entered, 1.0) # set pressure oc.setPressure(self.__P) # set temperature oc.setTemperature(self.__T) # evaluate volume oc.setElementMolarAmounts(elementMolarAmounts) oc.calculateEquilibrium(gmStat.Off) exactVolume = self.__calculateVolume(oc.getPhasesAtEquilibrium().getPhaseConstituentComposition(),oc.getConstituentsDescription(),endmemberMassDensityLaws, constituentToEndmembersConverter) # evaluate (elementwise) partial molar volume (approximation of first order volume derivative by a second-order finite difference formula) partialMolarVolumes={} for element in elementMolarAmounts: # evaluate volume for n[element]+epsilone modifiedElementMolarAmounts=elementMolarAmounts.copy() modifiedElementMolarAmounts[element] += epsilon #print(element, modifiedElementMolarAmounts) oc.setElementMolarAmounts(modifiedElementMolarAmounts) oc.calculateEquilibrium(gmStat.Off) volumePlus = self.__calculateVolume(oc.getPhasesAtEquilibrium().getPhaseConstituentComposition(),oc.getConstituentsDescription(),endmemberMassDensityLaws, constituentToEndmembersConverter) # evaluate volume for n[element]-epsilone modifiedElementMolarAmounts[element] -= 2.0*epsilon #print(element, modifiedElementMolarAmounts) oc.setElementMolarAmounts(modifiedElementMolarAmounts) oc.calculateEquilibrium(gmStat.Off) volumeMinus = self.__calculateVolume(oc.getPhasesAtEquilibrium().getPhaseConstituentComposition(),oc.getConstituentsDescription(),endmemberMassDensityLaws, constituentToEndmembersConverter) partialMolarVolumes[element]=(volumePlus-volumeMinus)/(2.0*epsilon) # calculate approximate volume from partial volumes approxVolume = 0.0 for element, molarAmount in oc.getPhasesAtEquilibrium().getPhaseElementComposition()[list(oc.getPhasesAtEquilibrium().getPhaseElementComposition())[0]].items(): approxVolume+=molarAmount*partialMolarVolumes[element] return partialMolarVolumes,exactVolume,approxVolume
def calculateEquilibria(self, gmStat, setverb, T, P, x0): # setting verbosity (True or False - default), if set to yes, in particular, when getters are called the returned values are displayed in a comprehensive way oc.setVerbosity(setverb) # set pressure oc.setPressure(P) # set temperature oc.setTemperature(T) # set initial molar amounts oc.setElementMolarAmounts(x0) #Equilibrium if gmStat == 'Off': oc.calculateEquilibrium(gmstat.Off) elif gmStat == 'On': oc.calculateEquilibrium(gmstat.On) else: raise ValueError( 'No suitable parameter for gmstat found: Choose from On/Off') self.gibbs = oc.getGibbsEnergy() self.mu = oc.getChemicalPotentials() self.cd = oc.getConstituentsDescription() self.mass = oc.getScalarResult('B') phasesAtEquilibrium = oc.getPhasesAtEquilibrium() self.pec = phasesAtEquilibrium.getPhaseElementComposition() self.pcc = phasesAtEquilibrium.getPhaseConstituentComposition() self.ps = phasesAtEquilibrium.getPhaseSites() self.ma = phasesAtEquilibrium.getPhaseMolarAmounts()
def eqfunc(self, x, calc_value): # setting verbosity (True or False - default), if set to yes, in particular, when getters are called the returned values are displayed in a comprehensive way oc.setVerbosity(self.setverb) # set pressure oc.setPressure(self.P) # set temperature oc.setTemperature(self.T) # set initial molar amounts oc.setElementMolarAmounts(x) #Equilibrium oc.calculateEquilibrium(gmstat.Off) if calc_value == 'gibbs': self.eq_val = oc.getGibbsEnergy() elif calc_value == 'mu': self.eq_val = oc.getChemicalPotentials() elif calc_value == 'cd': self.eq_val = oc.getConstituentsDescription() elif calc_value == 'mass': self.eq_val = oc.getScalarResult('B') elif calc_value == 'pec': self.eq_val = oc.getPhasesAtEquilibrium( ).getPhaseElementComposition() elif calc_value == 'pcc': self.eq_val = oc.getPhasesAtEquilibrium( ).getPhaseConstituentComposition() elif calc_value == 'ps': self.eq_val = oc.getPhasesAtEquilibrium().getPhaseSites() elif calc_value == 'ma': self.eq_val = oc.getPhasesAtEquilibrium().getPhaseMolarAmounts() return self.eq_val
def Gm_bulkphase(temperature, elementMolarAmounts): # set temperature oc.setTemperature(temperature) oc.setElementMolarAmounts(elementMolarAmounts) Gm_bulkphase = {} Gm_bulkphase = oc.calculateEquilibrium(gmStat.On) G = oc.getScalarResult('G') phase_description = oc.getPhasesAtEquilibrium( ).getPhaseConstituentComposition() chemicalpotential = oc.getComponentAssociatedResult('MU')
def phase(self, x, **kwargs): """ The string name of the phase in equilibrium at the conditions. """ phasearray = self.eqfunc(x).Phase.sel(P=self.P, T=self.T, **kwargs) return phasearray.values.flatten() phasesAtEquilibrium=oc.getPhasesAtEquilibrium() self.pec = phasesAtEquilibrium.getPhaseElementComposition() self.pcc = phasesAtEquilibrium.getPhaseConstituentComposition() self.ps = phasesAtEquilibrium.getPhaseSites() self.ma = phasesAtEquilibrium.getPhaseMolarAmounts() self.mu = oc.getChemicalPotentials() self.cd = oc.getConstituentsDescription() self.mass=oc.getScalarResult('B')
def run(): print( '### test U-O coherent interface in the liquid miscibility gap ###\n') # tdb filepath tdbFile = os.environ['TDBDATA_PRIVATE'] + '/feouzr.tdb' #tdbFile=os.environ['TDBDATA_PRIVATE']+'/NUCLEA-17_1_mod.TDB' #tdbFile=os.environ['TDBDATA_PRIVATE']+'/NUCLEA-19_1_mod.TDB' #tdbFile='tests/TAF_uzrofe_V10.TDB' #tdbFile='tests/TAF_uzrofe_V10_only_liquid_UO.TDB' # components comps = ['O', 'U'] # mass density laws (from Barrachin2004) constituentDensityLaws = { 'U1': lambda T: 17270.0 - 1.358 * (T - 1408), 'ZR1': lambda T: 6844.51 - 0.609898 * T + 2.05008E-4 * T**2 - 4.47829E-8 * T** 3 + 3.26469E-12 * T**4, 'O2U1': lambda T: 8860.0 - 9.285E-1 * (T - 3120), 'O2ZR1': lambda T: 5150 - 0.445 * (T - 2983), 'O1': lambda T: 1.141 # set to meaningless value but ok as, no 'free' oxygen in the considered mixtures } constituentDensityLaws['U'] = constituentDensityLaws['U1'] constituentDensityLaws['ZR'] = constituentDensityLaws['ZR1'] constituentDensityLaws['O'] = constituentDensityLaws['O1'] # phase names phasenames = ['LIQUID', 'LIQUID'] # pressure P = 1E5 # Given initial alloy composition. x0 is the mole fraction of U. x0 = [0.65] # Composition step for searching initial interfacial equilibrium composition. dx = 0.05 # Convergence criterion for loop on interfacial composition epsilonX = 1E-5 # temperature range Tmin = 2800.0 Tmax = 4400.0 Trange = np.linspace(Tmin, Tmax, num=60, endpoint=True) #Tmin = 2800.0 #Tmax = 2900.0 #Trange = np.linspace(Tmin, Tmax, num=3, endpoint=True) results = pd.DataFrame(columns=[ 'temperature', 'n_phase1', 'n_phase2', 'xU_phase1', 'xU_phase2', 'xU_interface', 'sigma', 'VmU', 'VmO' ]) for T in Trange: # calculate global equilibrium and retrieve associated chemical potentials CoherentGibbsEnergy_OC.initOC(tdbFile, comps) model = CoherentGibbsEnergy_OC(T, 1E5, phasenames) mueq = model.chemicalpotential(x0) phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) if (len(phasesAtEquilibriumMolarAmounts) == 1): # it is possible that the miscibility gap has not been detected correctly (can happen when T increases) #print(phasesAtEquilibriumMolarAmounts) # ad hoc strategy: 1) calculate an equilibrium at lower temperature (hopefully finding the two phases) # 2) redo the calculation at the target temperature afterwards without the grid minimizer model = CoherentGibbsEnergy_OC(T - 300.0, 1E5, phasenames) mueq = model.chemicalpotential(x0) phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) #print(phasesAtEquilibriumMolarAmounts) oc.setTemperature(T) oc.calculateEquilibrium(gmStat.Off) mueq = model.getChemicalPotentials() phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) phasesAtEquilibriumElementCompositions = phasesAtEquilibrium.getPhaseElementComposition( ) print(phasesAtEquilibriumMolarAmounts) if (set(phasesAtEquilibriumMolarAmounts) == set( ['LIQUID#1', 'LIQUID_AUTO#2'])): # Composition range for searching initial interfacial equilibrium composition # calculated from the actual phase compositions componentsWithLimits = comps[1:] limit = [[1.0, 0.0] for each in componentsWithLimits] for phase in phasesAtEquilibriumElementCompositions: for element in phasesAtEquilibriumElementCompositions[phase]: elementMolarFraction = phasesAtEquilibriumElementCompositions[ phase][element] if element in componentsWithLimits: limit[componentsWithLimits.index(element)][0] = min( limit[componentsWithLimits.index(element)][0], elementMolarFraction) limit[componentsWithLimits.index(element)][1] = max( limit[componentsWithLimits.index(element)][1], elementMolarFraction) limit = [[ each[0] + dx * (each[1] - each[0]), each[1] - dx * (each[1] - each[0]) ] for each in limit] print('limits: ', limit) notConverged = True x = x0.copy() # Iterate on interfacial molar composition while (notConverged): # Molar volumes of pure components evaluated at x CoherentGibbsEnergy_OC.initOC(tdbFile, comps) model = CoherentGibbsEnergy_OC(T, P, phasenames[0], False) if ('TAF' in tdbFile): functions = model.constantPartialMolarVolumeFunctions( x, constituentDensityLaws, 1E-5, constituentToEndmembersConverter) else: functions = model.constantPartialMolarVolumeFunctions( x, constituentDensityLaws, 1E-5) # calculate interfacial energy sigma = SigmaCoherent_OC(T=T, x0=x0, db=tdbFile, comps=comps, phasenames=phasenames, purevms=functions, limit=limit, dx=dx, enforceGridMinimizerForLocalEq=False, mueq=mueq) print('at T=', T, ' sigma=', sigma.Interfacial_Energy.values, '\n') notConverged = np.abs( x[0] - sigma.Interfacial_Composition.values[1]) > epsilonX print('convergence: ', not notConverged, x[0], sigma.Interfacial_Composition.values[1]) x[0] = sigma.Interfacial_Composition.values[1] # Store result if (np.abs(sigma.Interfacial_Energy.values) > 1E-6): # store results in pandas dataframe results = results.append( { 'temperature': T, 'n_phase1': phasesAtEquilibriumMolarAmounts['LIQUID#1'], 'n_phase2': phasesAtEquilibriumMolarAmounts['LIQUID_AUTO#2'], 'xU_phase1': phasesAtEquilibriumElementCompositions['LIQUID#1'] ['U'], 'xU_phase2': phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] ['U'], 'xU_interface': sigma.Interfacial_Composition.values[1], 'sigma': sigma.Interfacial_Energy.values, 'VmU': functions[1](T), 'VmO': functions[0](T), }, ignore_index=True) else: raise ValueError('wrong value discarded') else: print('at T=', T, ' out of the miscibility gap') print('phases at equilibrium:', phasesAtEquilibriumMolarAmounts) # write csv result file results.to_csv('macro_liquidMG_UO_run.csv')
def run3(tdbFile, RUZr): print( '### test U-O-Zr-Fe coherent interface in the liquid miscibility gap ###\n' ) # components comps = ['O', 'U', 'ZR', 'FE'] # mass density laws (from Barrachin2004) constituentDensityLaws = { 'U1': lambda T: 17270.0 - 1.358 * (T - 1408), 'ZR1': lambda T: 6844.51 - 0.609898 * T + 2.05008E-4 * T**2 - 4.47829E-8 * T** 3 + 3.26469E-12 * T**4, 'O2U1': lambda T: 8860.0 - 9.285E-1 * (T - 3120), 'O2ZR1': lambda T: 5150 - 0.445 * (T - 2983), 'FE1': lambda T: 7030 - 0.88 * (T - 1808), 'NI1': lambda T: 7900 - 1.19 * (T - 1728), 'CR1': lambda T: 6290 - 0.72 * (T - 2178), 'O1': lambda T: 1.141, # set to meaningless value but ok as, no 'free' oxygen in the considered mixtures 'FE1O1': lambda T: 7030 - 0.88 * ( T - 1808 ), # set to Fe value but ok as, almost no such component in the considered mixtures 'FE1O1_5': lambda T: 7030 - 0.88 * ( T - 1808 ), # set to Fe value but ok as, almost no such component in the considered mixtures } # phase names phasenames = ['LIQUID', 'LIQUID'] # pressure P = 1E5 # initial alloy compositions. x0 is the mole fractions of U, Zr, Fe. read = pd.read_csv('tests/{0:2.1f}RUZr.csv'.format(RUZr), delim_whitespace=True) # Composition step for searching initial interfacial equilibrium composition. #dx = 0.5 # Convergence criterion for loop on interfacial composition epsilonX = 1E-4 # temperature range T = 3000 # Trange = np.linspace(Tmin, Tmax, num=10, endpoint=True) results = pd.DataFrame(columns=[ 'temperature', 'n_phase1', 'n_phase2', 'xU_phase1', 'xU_phase2', 'xZr_phase1', 'xZr_phase2', 'xFe_phase1', 'xFe_phase2', 'xU_interface', 'xZr_interface', 'xFe_interface', 'sigma', 'VmU', 'VmZr', 'VmFe' ]) x = None for ii in range(read.shape[0]): x0 = [read['xU'][ii], read['xZr'][ii], read['xFe'][ii]] print("*********({0:d}/{1:d})*********".format(ii + 1, read.shape[0])) print("x0: ", x0) # calculate global equilibrium and retrieve associated chemical potentials CoherentGibbsEnergy_OC.initOC(tdbFile, comps) oc.raw().pytqtgsw(4) # no merging of grid points #oc.raw().pytqtgsw(23) # denser grid model = CoherentGibbsEnergy_OC(T, 1E5, phasenames) mueq = model.chemicalpotential(x0) phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) if (len(phasesAtEquilibriumMolarAmounts) == 1): # it is possible that the miscibility gap has not been detected correctly (can happen when T increases) #print(phasesAtEquilibriumMolarAmounts) # ad hoc strategy: 1) calculate an equilibrium at lower temperature (hopefully finding the two phases) # 2) redo the calculation at the target temperature afterwards without the grid minimizer model = CoherentGibbsEnergy_OC(2900, 1E5, phasenames) mueq = model.chemicalpotential(x0) phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) #print(phasesAtEquilibriumMolarAmounts) oc.setTemperature(T) oc.calculateEquilibrium(gmStat.Off) mueq = model.getChemicalPotentials() phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) phasesAtEquilibriumElementCompositions = phasesAtEquilibrium.getPhaseElementComposition( ) print(phasesAtEquilibriumMolarAmounts) if (set(phasesAtEquilibriumMolarAmounts) == set( ['LIQUID#1', 'LIQUID_AUTO#2'])): # Composition range for searching initial interfacial equilibrium composition # calculated from the actual phase compositions componentsWithLimits = comps[1:] #limit = [ [1.0, 0.0] for each in componentsWithLimits ] #for phase in phasesAtEquilibriumElementCompositions: #for element in phasesAtEquilibriumElementCompositions[phase]: # elementMolarFraction = phasesAtEquilibriumElementCompositions[phase][element] # if element in componentsWithLimits: # limit[componentsWithLimits.index(element)][0] = min(limit[componentsWithLimits.index(element)][0], elementMolarFraction) # limit[componentsWithLimits.index(element)][1] = max(limit[componentsWithLimits.index(element)][1], elementMolarFraction) #limit = [ [each[0]+dx*(each[1]-each[0]), each[1]-dx*(each[1]-each[0])] for each in limit ] bulkX = [[ phasesAtEquilibriumElementCompositions[phase][element] for phase in phasesAtEquilibriumMolarAmounts ] for element in componentsWithLimits] notConverged = True if (x == None): x = [ 0.5 * (phasesAtEquilibriumElementCompositions['LIQUID#1'][comp] + phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] [comp]) for comp in componentsWithLimits ] # Iterate on interfacial molar composition while (notConverged): # Molar volumes of pure components evaluated at x CoherentGibbsEnergy_OC.initOC(tdbFile, comps) model = CoherentGibbsEnergy_OC(T, P, phasenames[0], False) if ('TAF' in tdbFile): functions = model.constantPartialMolarVolumeFunctions( x, constituentDensityLaws, 1E-5, constituentToEndmembersConverter) else: functions = model.constantPartialMolarVolumeFunctions( x, constituentDensityLaws, 1E-5) # calculate interfacial energy sigma = SigmaCoherent_OC2( T=T, x0=x0, db=tdbFile, comps=comps, phasenames=phasenames, purevms=functions, guess=x, computeEquilibriumFunction=partial( ComputeEquilibriumWithConstraints, bulkX=bulkX), enforceGridMinimizerForLocalEq=False, mueq=mueq) print('at T=', T, ' sigma=', sigma.Interfacial_Energy.values, '\n') notConverged = np.linalg.norm( x[:] - sigma.Interfacial_Composition.values[1:], np.inf) > epsilonX print('convergence: ', not notConverged, x[:], sigma.Interfacial_Composition.values[1:]) x[:] = sigma.Interfacial_Composition.values[1:] # store results in pandas dataframe if (np.abs(sigma.Interfacial_Energy.values) > 1E-5): print(sigma, "\n") if (abs( np.max(sigma.Partial_Interfacial_Energy.values) - np.min(sigma.Partial_Interfacial_Energy.values)) > 1E-4): print(np.min(sigma.Partial_Interfacial_Energy.values)) print(np.max(sigma.Partial_Interfacial_Energy.values)) raise ValueError('wrong value discarded') results = results.append( { 'temperature': T, 'n_phase1': phasesAtEquilibriumMolarAmounts['LIQUID#1'], 'n_phase2': phasesAtEquilibriumMolarAmounts['LIQUID_AUTO#2'], 'xU_phase1': phasesAtEquilibriumElementCompositions['LIQUID#1'] ['U'], 'xU_phase2': phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] ['U'], 'xZr_phase1': phasesAtEquilibriumElementCompositions['LIQUID#1'] ['ZR'], 'xZr_phase2': phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] ['ZR'], 'xFe_phase1': phasesAtEquilibriumElementCompositions['LIQUID#1'] ['FE'], 'xFe_phase2': phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] ['FE'], 'xU_interface': sigma.Interfacial_Composition.values[1], 'xZr_interface': sigma.Interfacial_Composition.values[2], 'xFe_interface': sigma.Interfacial_Composition.values[3], 'sigma': sigma.Interfacial_Energy.values, 'VmU': functions[1](T), 'VmZr': functions[2](T), 'VmFe': functions[3](T), 'VmO': functions[0](T), }, ignore_index=True) else: raise ValueError('wrong value discarded') else: print('at T=', T, ' out of the miscibility gap') print('phases at equilibrium:', phasesAtEquilibriumMolarAmounts) # write csv result file if ('TAF' in tdbFile): results.to_csv( 'macro_liquidMG_UOZrFe_run3_TAFID_RUZR={0:2.1f}.csv'.format(RUZr)) else: results.to_csv( 'macro_liquidMG_UOZrFe_run3_RUZR={0:2.1f}.csv'.format(RUZr))
def run2(): print( '### test U-O coherent interface in the liquid miscibility gap ###\n') # tdb filepath #tdbFile=os.environ['TDBDATA_PRIVATE']+'/feouzr.tdb' #tdbFile=os.environ['TDBDATA_PRIVATE']+'/NUCLEA-17_1_mod.TDB' #tdbFile=os.environ['TDBDATA_PRIVATE']+'/NUCLEA-19_1_mod.TDB' tdbFile = 'tests/TAF_uzrofe_V10.TDB' # components comps = ['O', 'U', 'ZR', 'FE'] # mass density laws (from Barrachin2004) constituentDensityLaws = { 'U1': lambda T: 17270.0 - 1.358 * (T - 1408), 'ZR1': lambda T: 6844.51 - 0.609898 * T + 2.05008E-4 * T**2 - 4.47829E-8 * T** 3 + 3.26469E-12 * T**4, 'O2U1': lambda T: 8860.0 - 9.285E-1 * (T - 3120), 'O2ZR1': lambda T: 5150 - 0.445 * (T - 2983), 'FE1': lambda T: 7030 - 0.88 * (T - 1808), 'NI1': lambda T: 7900 - 1.19 * (T - 1728), 'CR1': lambda T: 6290 - 0.72 * (T - 2178), 'O1': lambda T: 1.141, # set to meaningless value but ok as, no 'free' oxygen in the considered mixtures 'FE1O1': lambda T: 7030 - 0.88 * ( T - 1808 ), # set to Fe value but ok as, almost no such component in the considered mixtures 'FE1O1_5': lambda T: 7030 - 0.88 * ( T - 1808 ), # set to Fe value but ok as, almost no such component in the considered mixtures } constituentDensityLaws['U'] = constituentDensityLaws['U1'] constituentDensityLaws['ZR'] = constituentDensityLaws['ZR1'] constituentDensityLaws['O'] = constituentDensityLaws['O1'] constituentDensityLaws['FE'] = constituentDensityLaws['FE1'] # phase names phasenames = ['LIQUID', 'LIQUID'] # pressure P = 1E5 # Given initial alloy composition. x0 is the mole fractions of U, Zr, Fe. # RU/Zr=0.60 CZr=0.3 xSteel=0.1 x0 = [0.1550142, 0.2583569, 0.1215864] # Composition step for searching initial interfacial equilibrium composition. #dx = 0.5 # Convergence criterion for loop on interfacial composition epsilonX = 1E-5 inputs = pd.read_csv('macro_liquidMG_UOZrFe_run.csv') results = pd.DataFrame(columns=[ 'temperature', 'n_phase1', 'n_phase2', 'xU_phase1', 'xU_phase2', 'xZr_phase1', 'xZr_phase2', 'xFe_phase1', 'xFe_phase2', 'xU_interface', 'xZr_interface', 'xFe_interface', 'VmU', 'VmZr', 'VmFe', 'sigma' ]) x = None for i, T in enumerate(inputs['temperature']): # calculate global equilibrium and retrieve associated chemical potentials CoherentGibbsEnergy_OC.initOC(tdbFile, comps) oc.raw().pytqtgsw(4) # no merging of grid points #oc.raw().pytqtgsw(23) # denser grid model = CoherentGibbsEnergy_OC(T, 1E5, phasenames) mueq = model.chemicalpotential(x0) phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) if (len(phasesAtEquilibriumMolarAmounts) == 1): # it is possible that the miscibility gap has not been detected correctly (can happen when T increases) #print(phasesAtEquilibriumMolarAmounts) # ad hoc strategy: 1) calculate an equilibrium at lower temperature (hopefully finding the two phases) # 2) redo the calculation at the target temperature afterwards without the grid minimizer model = CoherentGibbsEnergy_OC(2800.0, 1E5, phasenames) mueq = model.chemicalpotential(x0) phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) #print(phasesAtEquilibriumMolarAmounts) oc.setTemperature(T) oc.calculateEquilibrium(gmStat.Off) mueq = model.getChemicalPotentials() phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) phasesAtEquilibriumElementCompositions = phasesAtEquilibrium.getPhaseElementComposition( ) print(phasesAtEquilibriumMolarAmounts) print(phasesAtEquilibriumElementCompositions) if (set(phasesAtEquilibriumMolarAmounts) == set( ['LIQUID#1', 'LIQUID_AUTO#2'])): # Composition range for searching initial interfacial equilibrium composition # calculated from the actual phase compositions componentsWithLimits = comps[1:] #limit = [ [1.0, 0.0] for each in componentsWithLimits ] #for phase in phasesAtEquilibriumElementCompositions: # for element in phasesAtEquilibriumElementCompositions[phase]: # elementMolarFraction = phasesAtEquilibriumElementCompositions[phase][element] # if element in componentsWithLimits: # limit[componentsWithLimits.index(element)][0] = min(limit[componentsWithLimits.index(element)][0], elementMolarFraction) # limit[componentsWithLimits.index(element)][1] = max(limit[componentsWithLimits.index(element)][1], elementMolarFraction) #limit = [ [each[0]+dx*(each[1]-each[0]), each[1]-dx*(each[1]-each[0])] for each in limit ] bulkX = [[ phasesAtEquilibriumElementCompositions[phase][element] for phase in phasesAtEquilibriumMolarAmounts ] for element in componentsWithLimits] if (x == None): x = [ 0.5 * (phasesAtEquilibriumElementCompositions['LIQUID#1'][comp] + phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] [comp]) for comp in componentsWithLimits ] #x = x0.copy() # Molar volumes of pure components evaluated at x functions = [ lambda _: inputs['VmO'][i], lambda _: inputs['VmU'][i], lambda _: inputs['VmZr'][i], lambda _: inputs['VmFe'][i] ] # calculate interfacial energy sigma = SigmaCoherent_OC2(T=T, x0=x0, db=tdbFile, comps=comps, phasenames=phasenames, purevms=functions, guess=x, computeEquilibriumFunction=partial( ComputeEquilibriumWithConstraints, bulkX=bulkX), enforceGridMinimizerForLocalEq=False, mueq=mueq) print('at T=', T, ' sigma=', sigma.Interfacial_Energy.values, '\n') x[:] = sigma.Interfacial_Composition.values[1:] # Store result if (np.abs(sigma.Interfacial_Energy.values) > 1E-6): # store results in pandas dataframe print(sigma, "\n") results = results.append( { 'temperature': T, 'n_phase1': phasesAtEquilibriumMolarAmounts['LIQUID#1'], 'n_phase2': phasesAtEquilibriumMolarAmounts['LIQUID_AUTO#2'], 'xU_phase1': phasesAtEquilibriumElementCompositions['LIQUID#1'] ['U'], 'xU_phase2': phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] ['U'], 'xZr_phase1': phasesAtEquilibriumElementCompositions['LIQUID#1'] ['ZR'], 'xZr_phase2': phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] ['ZR'], 'xFe_phase1': phasesAtEquilibriumElementCompositions['LIQUID#1'] ['FE'], 'xFe_phase2': phasesAtEquilibriumElementCompositions['LIQUID_AUTO#2'] ['FE'], 'xU_interface': sigma.Interfacial_Composition.values[1], 'xZr_interface': sigma.Interfacial_Composition.values[2], 'xFe_interface': sigma.Interfacial_Composition.values[3], 'sigma': sigma.Interfacial_Energy.values, 'VmU': functions[0](T), 'VmZr': functions[1](T), 'VmFe': functions[2](T), }, ignore_index=True) else: print(sigma, "\n") raise ValueError('wrong value discarded') else: print('at T=', T, ' out of the miscibility gap') print('phases at equilibrium:', phasesAtEquilibriumMolarAmounts) # write csv result file results.to_csv('macro_liquidMG_UOZrFe_run2.csv')
oc.setPressure(1E5) # set temperature oc.setTemperature(2800) ## suspend all phases except the liquid one oc.setPhasesStatus(('* ', ), phStat.Suspended) oc.setPhasesStatus(('LIQUID', ), phStat.Entered) oc.setElementMolarAmounts(x0) # calculate equilibrium oc.changeEquilibriumRecord('eq2') oc.calculateEquilibrium(gmStat.Off) # retrieving mu data phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phaseConstituentComposition = phasesAtEquilibrium.getPhaseConstituentComposition( ) mueq_inf = oc.getChemicalPotentials() # calculate equilibrium with the grid-minimizer (equilibrium record = default equilibrium) oc.changeEquilibriumRecord() oc.calculateEquilibrium() # retrieving mu data phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phaseConstituentComposition = phasesAtEquilibrium.getPhaseConstituentComposition( ) mueq = oc.getChemicalPotentials() # Use of molarVolume class
def run_TAFID(): print( '### test U-O coherent interface in the liquid miscibility gap ###\n') # tdb filepath tdbFile = os.environ['TDBDATA_PRIVATE'] + '/feouzr.tdb' # tdbFile=os.environ['TDBDATA_PRIVATE']+'/NUCLEA-17_1_mod.TDB' # tdbFile='tests/TAF_uzrofe_V10.TDB' # components comps = ['O', 'U'] # mass density laws (from Barrachin2004) constituentDensityLaws = { 'U1': lambda T: 17270.0 - 1.358 * (T - 1408), 'ZR1': lambda T: 6844.51 - 0.609898 * T + 2.05008E-4 * T**2 - 4.47829E-8 * T** 3 + 3.26469E-12 * T**4, 'O2U1': lambda T: 8860.0 - 9.285E-1 * (T - 3120), 'O2ZR1': lambda T: 5150 - 0.445 * (T - 2983), 'O1': lambda T: 1.141 # set to meaningless value but ok as, no 'free' oxygen in the considered mixtures } constituentDensityLaws['U'] = constituentDensityLaws['U1'] constituentDensityLaws['ZR'] = constituentDensityLaws['ZR1'] constituentDensityLaws['O'] = constituentDensityLaws['O1'] # phase names phasenames = ['LIQUID#1', 'LIQUID#2'] # pressure & temp P = 1E5 T = 3200 # Given initial alloy composition. x0 is the mole fraction of U. x0min = [0.5] x0max = [0.7] x0range = np.linspace(x0min[0], x0max[0], num=20, endpoint=True) # Composition step for searching initial interfacial equilibrium composition. dx = 0.05 results_1 = pd.DataFrame( columns=['X_U', 'n_phase1', 'n_phase2', 'mu_U', 'mu_O']) for x0 in x0range: # Molar volumes of pure components evaluated at x0 and kept constant afterwards CoherentGibbsEnergy_OC.initOC(tdbFile, comps) model = CoherentGibbsEnergy_OC(T, P, phasenames[0], False) # # functions=model.constantPartialMolarVolumeFunctions(x0, constituentDensityLaws, 1E-5, constituentToEndmembersConverter) functions = model.constantPartialMolarVolumeFunctions( [x0], constituentDensityLaws, 1E-5) # calculate global equilibrium and retrieve associated chemical potentials model = CoherentGibbsEnergy_OC(T, 1E5, phasenames) mueq = model.chemicalpotential([x0]) print('mu_U= ', mueq[1]) phasesAtEquilibrium = oc.getPhasesAtEquilibrium() phasesAtEquilibriumMolarAmounts = phasesAtEquilibrium.getPhaseMolarAmounts( ) phasesAtEquilibriumElementCompositions = phasesAtEquilibrium.getPhaseElementComposition( ) print(phasesAtEquilibriumMolarAmounts) if (set(phasesAtEquilibriumMolarAmounts) == set( ['LIQUID#1', 'LIQUID_AUTO#2'])): # Composition range for searching initial interfacial equilibrium composition # calculated from the actual phase compositions componentsWithLimits = comps[1:] limit = [[1.0, 0.0] for each in componentsWithLimits] for phase in phasesAtEquilibriumElementCompositions: for element in phasesAtEquilibriumElementCompositions[phase]: elementMolarFraction = phasesAtEquilibriumElementCompositions[ phase][element] if element in componentsWithLimits: limit[componentsWithLimits.index(element)][0] = min( limit[componentsWithLimits.index(element)][0], elementMolarFraction) limit[componentsWithLimits.index(element)][1] = max( limit[componentsWithLimits.index(element)][1], elementMolarFraction) limit = [[each[0] + dx, each[1] - dx] for each in limit] print('limits: ', limit) # store results in pandas dataframe results_1 = results_1.append( { 'X_U': x0, 'n_phase1': phasesAtEquilibriumMolarAmounts['LIQUID#1'], 'n_phase2': phasesAtEquilibriumMolarAmounts['LIQUID_AUTO#2'], 'mu_U': mueq[1], 'mu_O': mueq[0], }, ignore_index=True) results_1.to_csv('UO_muvsx_TAFID.csv')