elapsed = end - start print("iterate.py imports: " + str(elapsed)) # Read run-time arguments header = argv[1:][0] # Name of header file desc = argv[1:][1] # Directory name # Create and name outputs and results directories if they do not exist datadir = location_out + desc + '/outputs/' + 'transient/' datadirr = location_out + desc + '/results' if not os.path.exists(datadir): os.makedirs(datadir) if not os.path.exists(datadirr): os.makedirs(datadirr) # Retrieve header info inhead = form.readheader(header) pressure = inhead[0] temp = inhead[1] # Locate and read initial iteration output from balance.py infile = datadir + '/lagrange-iteration-0-machine-read.txt' input = form.readoutput(infile) # Retrieve and set initial values speclist = input[2] x = input[3] x_bar = input[6] # Set up first iteration it_num = 1 repeat = True
def lambdacorr(it_num, datadir, doprint, direct): ''' This module applies lambda correction method (see Section 1.3 in TEA Document). When input mole numbers are negative, the code corrects them to positive values and pass them to the next iteration cycle. The code reads the values from the last lagrange output, the information from the header file, performs checks, and starts setting basic equations. It sets a smart range so it can efficiently explore the lambda values from [0,1]. Half of the range is sampled exponentially, and the other half linearly, totalling 150 points. The code retrieves the last lambda value before first derivative becomes positive (equation (33) in TEA Document), and corrects negative mole numbers to positive. Parameters ---------- it_num: integer Iteration number. datadir: string Current directory where TEA is run. doprint: string Parameter in configuration file that allows printing for debugging purposes. direct: object Object containing all of the results/data from the previous calculation in lagrange.py or lambdacorr.py. It is a list containing current header directory, current iteration number, array of species names, array of initial guess, array of non-corrected Lagrange values, and array of lambdacorr corrected values. Returns ------- header: string Name of the header file used. it_num: integer Iteration number. speclist: array of strings Array containing names of molecular species. y: array of floats Array containing initial guess of molecular species for current iteration. x_corr: array of floats Array containing final mole numbers of molecular species for current iteration. delta_corr: array of floats Array containing change of initial and final mole numbers of molecular species for current iteration. y_bar: float Array containing total initial guess of all molecular species for current iteration. x_corr_bar: float Total sum of the final mole numbers of all molecular species. detla_corr_bar: float Change in total number of all species. doprint: string Parameter in configuration file that allows printing for debugging purposes. Notes ----- The code works without adjustments and with high precision for the the fractional abundances (mixing fractions) up to 10e-14 and the temperature range of 1000 - 4000 K. For temperatures below 1000 K and mixing fractions below 10e-14, the code produces results with low precision. To improve the precision, adjust the lambda exploration variables 'lower' and 'steps' to larger magnitudes (i.e., lower = -100, steps = 1000). This will lengthen the time of execution. ''' # Suppress nan warnings, as they are used for finding valid minima np.seterr(invalid='ignore') np.seterr(divide='ignore') # Read values from last lagrange output input = direct # Take the current header file header = input[0] # Read values from the header file pressure, temp, i, j, speclist, a, b, g_RT = form.readheader(header) # Take final values from last iteration, lagrange.py y = input[3] x = input[4] delta = input[5] y_bar = input[6] x_bar = input[7] delta_bar = input[8] # Perform checks to be safe it_num_check = input[1] speclist_check = input[2] # Make array of checks check = np.array([it_num_check != it_num, False in (speclist_check == speclist) ]) # If iteration number given by iterate.py is not one larger as in 'direct', # give error if check[0]: print("\n\nMAJOR ERROR! Read in file's it_num is not the \ current iteration!\n\n") # If species names in the header are not the same as in 'direct', # give error if check[1]: print("\n\nMAJOR ERROR! Read in file uses different species \ order/list!\n\n") # Create 'c' value, equation (16) TEA Document # c_i = (g/RT)i + ln(P) c = g_RT + np.log(pressure) # Set equation (33) TEA Document # dF(lam)/dlam = sum_i delta_i[(g(T)/RT)_i + lnP + # ln (yi+lam*delta_i)/(y_bar+lam*delta_bar)] def dF_dlam(s, i, x, y, delta, c, x_bar, y_bar, delta_bar): dF_dlam = 0 for n in np.arange(i): dF_dlam += delta[n] * (c[n] + np.log(y[n] + s*delta[n]) - \ np.log(y_bar + s*delta_bar)) return dF_dlam # Create the range of lambda values to explore. To speed up finding # the correct lambda value to use, the range is split into two # parts at range_split: the lower exponential range and the # higher linear range. This helps the system to converge faster. range_split = 0.5 # Create exponential range, low_range # Exponent parameter that gives a value close to zero for the start of # lambda exploration lower = -50 # Define number of steps to explore exponential range steps = 100 # Create lower exponential range low_range = np.exp(np.linspace(lower, 0, steps+1)) # Create linear, evenly spaced range, high_range high_step = 0.01 high_range = np.arange(0.5, 1 + high_step, high_step) # Combine the two ranges to create one overall range for lambda exploration smart_range = np.append(low_range[low_range <= range_split], high_range) # Set that lambda is not found lam_not_found = True # Retrieve last lambda value explored before the minimum energy is passed for h in smart_range: val = dF_dlam(h, i, x, y, delta, c, x_bar, y_bar, delta_bar) if val > 0 or np.isnan(val) == True: break # If lambda found, take lambda and set lambda not found to false lam = h lam_not_found = False # If lambda is not found (break), function F (equation (35) in TEA # Document) set the final x mole numbers to the values calculated # in the last iteration output if lam_not_found: x_corr = y else: x_corr = y + lam * delta # Correct x values given this value of lambda x_corr_bar = np.sum(x_corr) delta_corr = y - x_corr delta_corr_bar = x_corr_bar - y_bar # Name output files with corresponding iteration number name file = datadir + '/lagrange-iteration-' + np.str(it_num) + \ '-machine-read.txt' file_fancy = datadir + '/lagrange-iteration-' + np.str(it_num) + \ '-visual.txt' # Export all values into machine and human readable output files form.output(datadir, header, it_num, speclist, y, x_corr, delta_corr, \ y_bar, x_corr_bar, delta_corr_bar, file, doprint) form.fancyout(datadir, it_num, speclist, y, x_corr, delta_corr, y_bar, \ x_corr_bar, delta_corr_bar, file_fancy, doprint) return [header, it_num, speclist, y, x_corr, delta_corr, y_bar, \ x_corr_bar, delta_corr_bar, doprint]
# Correct location_TEA name if location_out[-1] != '/': location_out += '/' # Read run-time arguments header = argv[1:][0] # Name of header file desc = argv[1:][1] # Directory name # Create and name outputs and results directories if they do not exist datadir = location_out + desc + '/outputs/' + 'transient/' # If output directory does not exist already, make it if not os.path.exists(datadir): os.makedirs(datadir) # Read in values from header file pressure, temp, i, j, speclist, a, b, c = form.readheader(header) # Print b values for debugging purposes if doprint: print("b values: " + str(b)) # Find chunk of ai_j array that will allow the corresponding yi values # to be free variables such that all elements are considered for n in np.arange(i - j + 1): # Get lower and upper indices for chunk of ai_j array to check lower = n upper = n + j # Retrieve chunk of ai_j that would contain free variables a_chunk = a[lower:upper]
def lagrange(it_num, datadir, doprint, direct): ''' This code applies Lagrange's method and calculates minimum based on the methodology elaborated in the TEA theory document in Section (3). Equations in this code contain both references and an explicitly written definitions. The program reads the last iteration's output and data from the last header file, creates variables for the Lagrange equations, sets up the Lagrange equations, and calculates final x_i mole numbers for the current iteration cycle. Note that the mole numbers that result from this function are allowed to be negative. If negatives are returned, lambda correction (lambdacorr.py) is necessary. The final x_i values, as well as x_bar, y_bar, delta, and delta_bar are written into machine- and human-readable output files. This function is executed by iterate.py. Parameters ---------- it_num: integer Iteration number. datadir: string Current directory where TEA is run. doprint: string Parameter in configuration file that allows printing for debugging purposes. direct: object Object containing all of the results/data from the previous calculation in lagrange.py or lambdacorr.py. It is a list containing current header directory, current iteration number, array of species names, array of initial guess, array of non-corrected Lagrange values, and array of lambdacorr corrected values. Returns ------- header: string Name of the header file used. it_num: integer Iteration number. speclist: string array Array containing names of molecular species. y: float array Array containing initial guess of molecular species for current iteration. x: float array Array containing final mole numbers of molecular species for current iteration. delta: float array Array containing change in initial and final mole numbers of molecular species for current iteration. y_bar: float Array containing total sum of initial guesses of all molecular species for current iteration. x_bar: float Total sum of the final mole numbers of all molecular species. delta_bar: float Change in total of initial and final mole numbers of molecular species. ''' # Read values from last iteration input = direct # Take the current header file header = input[0] # Read values from the header file pressure, temp, i, j, speclist, a, b, g_RT = form.readheader(header) # Use final values from last iteration (x values) as new initial y = input[4] y_bar = input[7] # Perform checks to be safe it_num_check = input[1] speclist_check = input[2] # Make array of checks check = np.array([it_num_check != it_num - 1, False in (speclist_check == speclist) ]) # If iteration number given by iterate.py is not one larger as in 'direct', # give error if check[0]: print("\n\nMAJOR ERROR! Read in file's it_num is not the most \ recent iteration!\n\n") # If species names in the header are not the same as in 'direct', # give error if check[1]: print("\n\nMAJOR ERROR! Read in file uses different species \ order/list!\n\n") # ============== CREATE VARIABLES FOR LAGRANGE EQUATION ============== # # Create 'c' value, equation (18) TEA theory document # ci = (g/RT)_i + ln(P) c = g_RT + np.log(pressure) # Allocates array of fi(Y) over different values of i (species) fi_y = np.zeros(i) # Fill in fi(Y) values equation (19) TEA theory document # fi = x_i * [ci + ln(x_i/x_bar)] for n in np.arange(i): y_frac = np.float(y[n] / y_bar) fi_y[n] = y[n] * ( c[n] + np.log(y_frac) ) # Allocate values of rjk. Both j and k goes from 1 to m. k = j rjk = np.zeros((j,k)) # Fill out values of rjk, equation (26) TEA theory document # rjk = rkj = sum_i(a_ij * a_ik) * y_i for l in np.arange(k): for m in np.arange(j): r_sum = 0.0 for n in np.arange(i): r_sum += a[n, m] * a[n, l] * y[n] rjk[m, l] = r_sum # Allocate value of u, equation (28) TEA theory document u = Symbol('u') # Allocate pi_j variables, where j is element index # Example: pi_2 is Lagrange multiplier of N (j = 2) pi = [] for m in np.arange(j): name = 'pi_' + np.str(m+1) pi = np.append(pi, Symbol(name)) # Allocate rjk * pi_j summations, equation (27) TEA theory document # There will be j * k terms of rjk * pi_j sq_pi = [pi] for m in np.arange(j-1): # Make square array of pi values with shape j * k sq_pi = np.append(sq_pi, [pi], axis = 0) # Multiply rjk * sq_pi to get array of rjk * pi_j # equation (27) TEA theory document rpi = rjk * sq_pi # ======================= SET FINAL EQUATIONS ======================= # # Total number of equations is j + 1 # Set up a_ij * fi(Y) summations equation (27) TEA theory document # sum_i[a_ij * fi(Y)] aij_fiy = np.zeros((j)) for m in np.arange(j): rhs = 0.0 for n in np.arange(i): rhs += a[n,m] * fi_y[n] aij_fiy[m] = rhs # Create first j'th equations equation (27) TEA theory document # r_1m*pi_1 + r_2m*pi_2 + ... + r_mm*pi_m + b_m*u = sum_i[a_im * fi(Y)] for m in np.arange(j): if m == 0: equations = np.array([np.sum(rpi[m]) + b[m]*u - aij_fiy[m]]) else: lagrange_eq = np.array([np.sum(rpi[m]) + b[m]*u - aij_fiy[m]]) equations = np.append(equations, lagrange_eq) # Last (j+1)th equation (27) TEA theory document # b_1*pi_1 + b_2*pi_2 + ... + b_m*pi_m + 0*u = sum_i[fi(Y)] bpi = b * pi lagrange_eq_last = np.array([np.sum(bpi) - np.sum(fi_y)]) equations = np.append(equations, lagrange_eq_last) # List all unknowns in the above set of equations unknowns = list(pi) unknowns.append(u) # Solve final system of j+1 equations fsol = solve(list(equations), unknowns, rational=False) # ============ CALCULATE xi VALUES FOR CURRENT ITERATION ============ # # Make array of pi values pi_f = [] for m in np.arange(j): pi_f = np.append(pi_f, [fsol[pi[m]]]) # Calculate x_bar from solution to get 'u', equation (28) TEA theory document # u = -1. + (x_bar/y_bar) u_f = fsol[u] x_bar = (u_f + 1.) * y_bar # Initiate array for x values of size i x = np.zeros(i) # Apply Lagrange solution for final set of x_i values for this iteration # equation (23) TEA theory document # x_i = -fi(Y) + (y_i/y_bar) * x_bar + [sum_j(pi_j * a_ij)] * y_i for n in np.arange(i): sum_pi_aij = 0.0 for m in np.arange(j): sum_pi_aij += pi_f[m] * a[n, m] x[n] = - fi_y[n] + (y[n]/y_bar) * x_bar + sum_pi_aij * y[n] # Calculate other variables of interest x_bar = np.sum(x) # sum of all x_i delta = x - y # difference between initial and final values delta_bar = x_bar - y_bar # difference between sum of initial and # final values # Name output files with corresponding iteration number name file = datadir + '/lagrange-iteration-' + np.str(it_num) + \ 'machine-read-nocorr.txt' file_fancy = datadir + '/lagrange-iteration-' + np.str(it_num) + \ '-visual-nocorr.txt' # Export all values into machine and human readable output files form.output(datadir, header, it_num, speclist, y, x, \ delta, y_bar, x_bar, delta_bar, file, doprint) form.fancyout(datadir, it_num, speclist, y, x, delta,\ y_bar, x_bar, delta_bar, file_fancy, doprint) return [header, it_num, speclist, y, x, delta, y_bar, x_bar, delta_bar]
def lambdacorr(it_num, datadir, doprint, direct): ''' This module applies lambda correction method (see Section 4 in the TEA theory document). When input mole numbers are negative, the code corrects them to positive values and pass them to the next iteration cycle. The code reads the values from the last lagrange output, the information from the header file, performs checks, and starts setting basic equations. It defines a 'smart' range so it can efficiently explore the lambda values from [0,1]. Half of the range is sampled exponentially, and the other half linearly, totalling 150 points. The code retrieves the last lambda value before first derivative becomes positive (equation (34) in TEA theory document), and corrects negative mole numbers to positive. Parameters ---------- it_num: integer Iteration number. datadir: string Current directory where TEA is run. doprint: string Parameter in configuration file that allows printing for debugging purposes. direct: object Object containing all of the results/data from the previous calculation in lagrange.py or lambdacorr.py. It is a list containing current header directory, current iteration number, array of species names, array of initial guess, array of non-corrected Lagrange values, and array of lambdacorr corrected values. Returns ------- header: string Name of the header file used. it_num: integer Iteration number. speclist: array of strings Array containing names of molecular species. y: array of floats Array containing initial guess of molecular species for current iteration. x_corr: array of floats Array containing final mole numbers of molecular species for current iteration. delta_corr: array of floats Array containing change of initial and final mole numbers of molecular species for current iteration. y_bar: float Array containing total initial guess of all molecular species for current iteration. x_corr_bar: float Total sum of the final mole numbers of all molecular species. detla_corr_bar: float Change in total number of all species. doprint: string Parameter in configuration file that allows printing for debugging purposes. Notes ----- The code works without adjustments and with high precision for the temperatures above ~600 K. For temperatures below 600 K and mixing fractions below 10e-14, the code produces results with low precision. To improve the precision, adjust the lambda exploration variables 'lower' and 'steps' to larger magnitudes (i.e., lower = -100, steps = 1000). This will lengthen the time of execution. ''' # Suppress nan warnings, as they are used for finding valid minima np.seterr(invalid='ignore') np.seterr(divide='ignore') # Read values from last lagrange output input = direct # Take the current header file header = input[0] # Read values from the header file pressure, temp, i, j, speclist, a, b, g_RT = form.readheader(header) # Take final values from last iteration, lagrange.py y = input[3] x = input[4] delta = input[5] y_bar = input[6] x_bar = input[7] delta_bar = input[8] # Perform checks to be safe it_num_check = input[1] speclist_check = input[2] # Make array of checks check = np.array( [it_num_check != it_num, False in (speclist_check == speclist)]) # If iteration number given by iterate.py is not one larger as in 'direct', # give error if check[0]: print("\n\nMAJOR ERROR! Read in file's it_num is not the \ current iteration!\n\n") # If species names in the header are not the same as in 'direct', # give error if check[1]: print("\n\nMAJOR ERROR! Read in file uses different species \ order/list!\n\n") # Create 'c' value, equation (17) TEA theory document # c_i = (g/RT)i + ln(P) c = g_RT + np.log(pressure) # Set equation (34) TEA theory document # dF(lam)/dlam = sum_i delta_i[(g(T)/RT)_i + lnP + # ln (yi+lam*delta_i)/(y_bar+lam*delta_bar)] def dF_dlam(s, i, x, y, delta, c, x_bar, y_bar, delta_bar): dF_dlam = 0 for n in np.arange(i): dF_dlam += delta[n] * (c[n] + np.log(y[n] + s*delta[n]) - \ np.log(y_bar + s*delta_bar)) return dF_dlam # Create the range of lambda values to explore. To speed up finding # the correct lambda value to use, the range is split into two # parts at range_split: the lower exponential range and the # higher linear range. This helps the system to converge faster. range_split = 0.5 # Create exponential range, low_range # Exponent parameter that gives a value close to zero for the start of # lambda exploration lower = -50 # Define number of steps to explore exponential range steps = 100 # Create lower exponential range low_range = np.exp(np.linspace(lower, 0, steps + 1)) # Create linear, evenly spaced range, high_range high_step = 0.01 high_range = np.arange(0.5, 1 + high_step, high_step) # Combine the two ranges to create one overall range for lambda exploration smart_range = np.append(low_range[low_range <= range_split], high_range) # Set that lambda is not found lam_not_found = True # Retrieve last lambda value explored before the minimum energy is passed for h in smart_range: val = dF_dlam(h, i, x, y, delta, c, x_bar, y_bar, delta_bar) if val > 0 or np.isnan(val) == True: break # If lambda found, take lambda and set lambda not found to false lam = h lam_not_found = False # If lambda is not found (break), function F (equation (33) in the TEA # theory document) set the final x mole numbers to the values calculated # in the last iteration output if lam_not_found: x_corr = y else: x_corr = y + lam * delta # Correct x values given this value of lambda x_corr_bar = np.sum(x_corr) delta_corr = y - x_corr delta_corr_bar = x_corr_bar - y_bar # Name output files with corresponding iteration number name file = datadir + '/lagrange-iteration-' + np.str(it_num) + \ '-machine-read.txt' file_fancy = datadir + '/lagrange-iteration-' + np.str(it_num) + \ '-visual.txt' # Export all values into machine and human readable output files form.output(datadir, header, it_num, speclist, y, x_corr, delta_corr, \ y_bar, x_corr_bar, delta_corr_bar, file, doprint) form.fancyout(datadir, it_num, speclist, y, x_corr, delta_corr, y_bar, \ x_corr_bar, delta_corr_bar, file_fancy, doprint) return [header, it_num, speclist, y, x_corr, delta_corr, y_bar, \ x_corr_bar, delta_corr_bar, doprint]
def iterator(head, destination, location_out): # Correct location_TEA name if location_out[-1] != '/': location_out += '/' # Time / speed testing if times: end = time.time() elapsed = end - start print("iterate.py imports: " + str(elapsed)) # Read run-time arguments header = head # Name of header file desc = destination # Directory name # Create and name outputs and results directories if they do not exist datadir = location_out + desc + '/outputs/' + 'transient/' datadirr = location_out + desc + '/results' if not os.path.exists(datadir): os.makedirs(datadir) if not os.path.exists(datadirr): os.makedirs(datadirr) # Retrieve header info inhead = form.readheader(header) pressure = inhead[0] temp = inhead[1] # Locate and read initial iteration output from balance.py infile = datadir + '/lagrange-iteration-0-machine-read.txt' input = form.readoutput(infile) # Retrieve and set initial values speclist = input[2] x = input[3] x_bar = input[6] # Set up first iteration it_num = 1 repeat = True # Prepare data object for iterative process # (see description of the 'direct' object in lagrange.py) lambdacorr_data = [header, 0, speclist, x, x, 0, x_bar, x_bar, 0] # Time / speed testing if times: new = time.time() elapsed = new - end print("pre-loop setup: " + str(elapsed)) # ====================== PERFORM MAIN TEA LOOP ====================== # while repeat: # Output iteration number if ((not doprint) & (not times)): stdout.write(' ' + str(it_num) + '\r') stdout.flush() # Time / speed testing for lagrange.py if times: ini = time.time() # Execute Lagrange minimization lagrange_data = lg.lagrange(it_num, datadir, doprint, lambdacorr_data) # Time / speed testing for lagrange.py if times: fin = time.time() elapsed = fin - ini print("lagrange" + str(it_num).rjust(4) + " : " + str(elapsed)) # Print for debugging purposes if doprint: printout('Iteration %d Lagrange complete. Starting lambda correction...', it_num) # Take final x_i mole numbers from last Lagrange calculation lagrange_x = lagrange_data[4] # Check if x_i have negative mole numbers, and if yes perform lambda correction if where((lagrange_x < 0) == True)[0].size != 0: # Print for debugging purposes if doprint: printout('Correction required. Initializing...') # Time / speed testing for lambdacorr.py if times: ini = time.time() # Execute lambda correction lambdacorr_data = lc.lambdacorr(it_num, datadir, doprint, \ lagrange_data) # Print for debugging purposes if times: fin = time.time() elapsed = fin - ini print("lambcorr" + str(it_num).rjust(4) + " : " + \ str(elapsed)) # Print for debugging purposes if doprint: printout('Iteration %d lambda correction complete. Checking precision...', it_num) # Lambda correction is not needed else: # Pass previous Lagrange results as inputs to next iteration lambdacorr_data = lagrange_data # Print for debugging purposes if doprint: printout('Iteration %d did not need lambda correction.', it_num) # Retrieve most recent iteration values input_new = lambdacorr_data # Take most recent x_i and x_bar values x_new = input_new[4] x_bar_new = input_new[7] # If max iteration not met, continue with next iteration cycle if it_num < maxiter: # Add 1 to iteration number it_num += 1 # Print for debugging purposes if doprint: printout('Max interation not met. Starting next iteration...\n') # ============== Stop the loop, max iteration reached ============== # # Stop if max iteration is reached else: # Print to screen printout('Maximum iteration reached, ending minimization.\n') # Set repeat to False to stop the loop repeat = False # Calculate delta values delta = x_new - x # Calculate delta_bar values delta_bar = x_bar_new - x_bar # Name output files with corresponding iteration number name file_results = datadirr + '/results-machine-read.txt' file_fancyResults = datadirr + '/results-visual.txt' # Export all values into machine and human readable output files form.output(datadirr, header, it_num, speclist, x, x_new, delta, \ x_bar, x_bar_new, delta_bar, file_results, doprint) form.fancyout_results(datadirr, header, it_num, speclist, x, x_new, \ delta, x_bar, x_bar_new, delta_bar, pressure, \ temp, file_fancyResults, doprint)
def balanceFunction(head, destination, location_out): if location_out[-1] != '/': location_out += '/' header = head # Name of header file desc = destination # Directory name # Create and name outputs and results directories if they do not exist datadir = location_out + desc + '/outputs/' + 'transient/' # If output directory does not exist already, make it if not os.path.exists(datadir): os.makedirs(datadir) # Read in values from header file pressure, temp, i, j, speclist, a, b, c = form.readheader(header) # Print b values for debugging purposes if doprint: print("b values: " + str(b)) # Find chunk of ai_j array that will allow the corresponding yi values # to be free variables such that all elements are considered for n in np.arange(i - j + 1): # Get lower and upper indices for chunk of ai_j array to check lower = n upper = n + j # Retrieve chunk of ai_j that would contain free variables a_chunk = a[lower:upper] # Sum columns to get total of ai_j in chunk for each species 'j' check = map(sum,zip(*a_chunk)) # Look for zeros in check. If a zero is found, this chunk of data can't # be used for free variables, as this signifies an element is ignored has_zero = 0 in check # If zero not found, create list of free variables' indices if has_zero == False: free_id = [] for m in np.arange(j): if doprint == True: print('Using y_' + np.str(n + m + 1) + ' as a free variable') free_id = np.append(free_id, n + m) break # Set initial guess of non-free y_i scale = 0.1 # Assume that all or some y_i are negative or zeros nofit = True # Loop until all y_i are non-zero positive while nofit: # Set up list of 'known' initial mole numbers before and after free chunk pre_free = np.zeros(free_id[0]) + scale post_free = np.zeros(i - free_id[-1] - 1) + scale # Set up list of free variables free = [] for m in np.arange(j): name = 'y_unknown_' + np.str(m) free = np.append(free, Symbol(name)) # Combine free and 'known' to make array of y_initial mole numbers y_init = np.append(pre_free, free) y_init = np.append( y_init, post_free) # Make 'j' equations satisfying mass balance equation (17) in TEA theory doc: # sum_i(ai_j * y_i) = b_j eq = [[]] for m in np.arange(j): rhs = 0 for n in np.arange(i): rhs += a[n, m] * y_init[n] rhs -= b[m] eq = np.append(eq, rhs) # Solve system of linear equations to get free y_i variables result = solve(list(eq), list(free), rational=False) # Correct for no-solution-found results. # If no solution found, decrease scale size. if result == []: scale /= 10 if doprint: print("Correcting initial guesses for realistic mass. \ Trying " + str(scale) + "...") # Correct for negative-mass results. If found, decrease scale size. else: # Assume no negatives and check hasneg = False for m in np.arange(j): if result[free[m]] < 0: hasneg = True # If negatives found, decrease scale size if hasneg: scale /= 10 if doprint: print("Negative numbers found in fit.") print("Correcting initial guesses for realistic mass. \ Trying " + str(scale) + "...") # If no negatives found, exit the loop (good fit is found) else: nofit = False if doprint: print(str(scale) + " provided a viable initial guess.") # Gather the results fit = [] for m in np.arange(j): fit = np.append(fit, result[free[m]]) # Put the result into the final y_init array y_init[free_id[0]:free_id[j-1]+1] = fit # This part of the code is only for debugging purposes # It rounds the values and checks whether the balance equation is satisfied # No values are changed and this serves solely as a check if doprint == True: print('\nCHECKS:') for m in np.arange(j): bool = round((sum(a[:,m] * y_init[:])), 2) == round(b[m], 2) if bool == True: if doprint == True: print('Equation ' + np.str(m+1) + ' is satisfied.') if bool == False: print('Equation ' + np.str(m+1) + \ ' is NOT satisfied. Check for errors!') # Set iteration number to zero it_num = 0 # Put all initial mole numbers in y array y = y_init # Make y_bar (sum of all y values) y_bar = np.sum(y) # Initialize delta variables to 0. (this signifies the first iteration) delta = np.zeros(i) delta_bar = np.sum(delta) # Name output files with corresponding iteration number name file = datadir + '/lagrange-iteration-' + np.str(it_num) + \ '-machine-read.txt' file_fancy = datadir + '/lagrange-iteration-' + np.str(it_num) + \ '-visual.txt' # Put results into machine readable file form.output(datadir, header, it_num, speclist, y, y, delta, \ y_bar, y_bar, delta_bar, file, doprint) # Put results into human readable file form.fancyout(datadir, it_num, speclist, y, y, delta, y_bar, \ y_bar, delta_bar, file_fancy, doprint)
def lagrange(it_num, datadir, doprint, direct): ''' DOCSTRING ''' infile = datadir + '/lagrange-iteration-' + np.str(it_num - 1) + '.txt' # Read in values from header.txt and previous output #if direct: input = direct #else: # input = form.readoutput(infile) header = input[0] pressure, temp, i, j, speclist, a, b, g_RT = form.readheader(header) y = input[4] # 'x' in output file, using final values as new initials y_bar = input[7] # see above # Perform checks to be safe it_num_check = input[1] speclist_check = input[2] check = np.array([ it_num_check != it_num - 1, False in (speclist_check == speclist) ]) if check[0]: print("\n\nMAJOR ERROR! Read in file's it_num is not the most previous iteration!\n\n") if check[1]: print("\n\nMAJOR ERROR! Read in file uses different species order/list!\n\n") # FINDME: DEGRADED, CHECK IS SALVAGABLE save_JANAF = False if save_JANAF: dir_save = os.getcwd() # CHANGE THIS TO MATCH DESC IN runatm.py or runinput.py desc_save = 'checkJANAF' JANAF_file_save = '/outputs/' + desc_save + '/JANAF_check_' + '%.0f'%temp + 'K.txt' f = open(dir_save + JANAF_file_save, 'w+') for r in np.arange(np.size(speclist)): f.write(speclist[r] + ' ' + '%4.3f'%c[r] + '\n') #print(speclist[r], c[r]) f.close() # Create 'c' value from 'g_RT' value with pressure correction c = g_RT + np.log(pressure) # Set up values of fi(Y) over different values of i fi_y = np.zeros(i) for n in np.arange(i): y_frac = np.float(y[n] / y_bar) fi_y[n] = y[n] * ( c[n] + np.log(y_frac) ) # ### Set up values of rjk over different values of i # NOTE: j , k = 1, 2, ... , total elements # so rj_k = rk_j, making a matrix symmetrical across the diagonal k = j # Both the same size rjk = np.zeros((j,k)) for l in np.arange(k): for m in np.arange(j): foo = 0.0 for n in np.arange(i): # summate (ai_j * ai_k * yi) over i foo += a[n, m] * a[n, l] * y[n] rjk[m, l] = foo # ### Set up value of u # Depends on if you want output of u or output of xi # For now, use u for simplicity in checking #u = -1. + (x_bar/y_bar) u = Symbol('u') # ### Set up pi_j variables, where j is species index # Example: pi_2 is lagrangian multipier of N (j = 2) pi = [] for m in np.arange(j): name = 'pi_' + np.str(m+1) pi = np.append(pi, Symbol(name)) # ### Set up r_jk * pi_j summations # There will be j x k terms of r_jk * pi_j sq_pi = [pi] for m in np.arange(j-1): # Make square array of pi values with shape j x k sq_pi = np.append(sq_pi, [pi], axis = 0) # Multiply rjk * sq_pi to get array of rjk * pi_j rpi = rjk * sq_pi # ################################################################### # # ####################### SET FINAL EQUATIONS! ###################### # # ################################################################### # # Total number of equations is j + 1 # Set up ai_j * fi(Y) summations aij_fiy = np.zeros((j)) for m in np.arange(j): foo = 0.0 for n in np.arange(i): # summate (ai_j * fi(Y)) over i foo += a[n,m] * fi_y[n] aij_fiy[m] = foo # Create first j-1 equations for m in np.arange(j): if m == 0: equations = np.array([np.sum(rpi[m]) + b[m]*u - aij_fiy[m]]) else: lagrange_eq = np.array([np.sum(rpi[m]) + b[m]*u - aij_fiy[m]]) equations = np.append(equations, lagrange_eq) # Last equation, only one here bpi = b * pi lagrange_eq = np.array([np.sum(bpi) - np.sum(fi_y)]) equations = np.append(equations, lagrange_eq) # Solve final system of equations unknowns = list(pi) unknowns.append(u) fsol = solve(list(equations), unknowns) # ################################################################### # # ########################## GET xi VALUES ########################## # # ################################################################### # # Make array of pi values pi_f = [] for m in np.arange(j): pi_f = np.append(pi_f, [fsol[pi[m]]]) # Calculate x_bar from solution to 'u' u_f = fsol[u] x_bar = (u_f + 1.)*y_bar # Start array for x values size of i x = np.zeros(i) # Apply lagrange solution for final values (eq 14) for n in np.arange(i): sum_pi_aij = 0.0 for m in np.arange(j): sum_pi_aij += pi_f[m] * a[n, m] x[n] = - fi_y[n] + (y[n]/y_bar) * x_bar \ + sum_pi_aij * y[n] # ### Now have final mole values for this iteration! # Also note the distance between initial and final # Correct x_bar x_bar = np.sum(x) delta = x - y delta_bar = x_bar - y_bar # Export all values into output files or via memory #TESTING: ALWAYS WRITE FILES form.output_nocorr(datadir, header, it_num, speclist, y, x, delta, y_bar, x_bar, delta_bar, doprint) form.fancyout_nocorr(datadir, it_num, speclist, y, x, delta, y_bar, x_bar, delta_bar, doprint) #if direct: return [header, it_num, speclist, y, x, delta, y_bar, x_bar, delta_bar]