Пример #1
0
        # 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)
Пример #2
0
def lambdacorr(it_num, verb, input, info, save_info=None):
    '''
    This module applies lambda correction method (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.
    verb: Integer
       Verbosity level (0=mute, 1=quiet, 2=verbose).
    input: List
       A list containing the results/data from the previous
       calculation in lagrange.py or lambdacorr.py:
       the 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.
    info: list
       pressure: atmospheric layer's pressure (bar)
       i: Number of species
       j: Number of elements
       a: Stoichiometric coefficients
       b: Elemental mixing fractions
       g_RT: Species chemical potential
    save_info: List of stuff
       If not None, save info to files.  The list contains:
         [location_out, desc, speclist, temp]

    Returns
    -------
    y: array of floats
       The initial guess of molecular species.
    x_corr:  array of floats
       Final mole numbers of molecular species.
    delta_corr: array of floats
       Array containing change of initial and final mole numbers of
       molecular species for current iteration.
    y_bar: float
       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.
    verb: Integer
       Verbosity level (0=mute, 1=quiet, 2=verbose).

    Notes
    -----
    The code works without adjustments for the temperatures above ~500 K. 
    For temperatures below 500 K the code produces results with low
    precision, thus it is not recommended to use TEA below 500 K. 
    Setting xtol to 1e-8 and maxinter to 200 is most optimizing. 
    If higher tolerance level is desired (xtol>1e-8), maxium number
    of iterations must be increased. The result can be further improved
    with fine adjustments to 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')

    pressure = info[0]
    i        = info[1]
    g_RT     = info[5]

    # Take final values from last iteration, lagrange.py
    y         = input[0]
    x         = input[1]
    delta     = input[2]
    y_bar     = input[3]
    x_bar     = input[4]
    delta_bar = input[5]

    # Create 'c' value, equation (17) TEA theory document
    # c_i = (g/RT)i + ln(P)
    c = g_RT + np.log(pressure)

    # 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):
            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

    if save_info is not None:
        location_out, desc, speclist, temp = save_info
        hfolder = location_out + desc + "/headers/"
        headerfile = "{:s}/header_{:s}_{:.0f}K_{:.2e}bar.txt".format(
                        hfolder, desc, temp, pressure)
        # Create and name outputs and results directories if they do not exist
        datadir   = location_out + desc + '/outputs/'
        datadir = "{:s}/{:s}_{:.0f}K_{:.2e}bar/".format(
                    datadir, desc, temp, pressure)

        # Export all values into machine and human readable output files
        file = '{:s}/lagrange_iteration-{:03d}_machine-read.txt'.format(
                datadir, it_num)
        form.output(headerfile, it_num, speclist, y, x_corr, delta_corr,
                    y_bar, x_corr_bar, delta_corr_bar, file, verb)
        file = '{:s}/lagrange_iteration-{:03d}_visual.txt'.format(
              datadir, it_num)
        form.fancyout(it_num, speclist, y, x_corr, delta_corr, y_bar,
                     x_corr_bar, delta_corr_bar, file, verb)

    return y, x_corr, delta_corr, y_bar, x_corr_bar, delta_corr_bar
Пример #3
0
    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)
Пример #4
0
        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 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]
Пример #6
0
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]
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]
        
Пример #8
0
def lagrange(it_num, verb, input, info, save_info=None):
    '''
    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.
    verb: Integer
       Verbosity level (0=mute, 1=quiet, 2=verbose).
    input:   List
       The input values/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.
    info: List
       pressure: atmospheric layer's pressure (bar)
       i: Number of species
       j: Number of elements
       a: Stoichiometric coefficients
       b: Elemental mixing fractions
       g_RT: Species chemical potential
    save_info: string
       Current directory where TEA is run.

    Returns
    -------
    y: float array
       Input guess of molecular species.
    x: float array
       Final mole numbers of molecular species.
    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 the header file
    pressure = info[0]
    j = info[2]
    a = info[3]
    b = info[4]
    g_RT = info[5]

    # Use final values from last iteration (x values) as new initial
    y = input[1]
    y_bar = input[4]

    # ============== 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)

    # Fill in fi(Y) values equation (19) TEA theory document
    # fi = x_i * [ci + ln(x_i/x_bar)]
    fi_y = y * (c + np.log(y / y_bar))

    # Allocate value of u, equation (28) TEA theory document
    # List all unknowns in the above set of equations
    unknowns = [None] * (j + 1)

    # Allocate pi_j, where j is element index
    for m in np.arange(j):
        unknowns[m] = Symbol('pi_' + str(m + 1))
    unknowns[j] = Symbol('u')

    # ================= SET SYSTEM OF EQUATIONS ======================= #
    # Total number of equations is j + 1
    # Create first j'th equations equation (27) TEA theory document
    # r_1j*pi_1 + r_2j*pi_2 + ... + r_jj*pi_j + b_j*u = sum_i[a_ij * fi(Y)]
    system = np.zeros((j + 1, j + 2))

    # Fill out values of r_ij, equation (26) TEA theory document
    # rjk = rkj = sum_i(a_ij * a_ik) * y_i
    for l in np.arange(j):
        for m in np.arange(j):
            system[m, l] = np.sum(a[:, m] * a[:, l] * y)
    # Last column, b_j*u:
    system[:j, j] = b

    # Set up a_ij * fi(Y) summations equation (27) TEA theory document
    # sum_i[a_ij * fi(Y)]
    system[:j, j + 1] = np.sum(a.T * fi_y, axis=1)

    # Last (j+1)th equation (27) TEA theory document
    # b_1*pi_1 + b_2*pi_2 + ... + b_m*pi_m  = sum_i[fi(Y)]
    system[j, :j] = b
    system[j, j + 1] = np.sum(fi_y)

    # Solve final system of j+1 equations
    fsol = solve_linear_system(Matrix(system), *unknowns)

    # Make array of pi values
    pi_f = np.zeros(j, np.double)
    for m in np.arange(j):
        pi_f[m] = fsol[unknowns[m]]
    fsolu = fsol[unknowns[j]]

    # ============ CALCULATE xi VALUES FOR CURRENT ITERATION ============ #
    # Calculate x_bar from solution to get 'u', eq (28) TEA theory document
    # u = -1. + (x_bar/y_bar), where fsolu is u
    x_bar = (fsolu + 1.0) * y_bar

    # 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
    sum_pi_aij = np.sum(pi_f * a, axis=1)
    x = np.array(-fi_y + (y / y_bar) * x_bar + sum_pi_aij * y, np.double)

    # 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
    if save_info:
        location_out, desc, speclist, temp = save_info
        hfolder = location_out + desc + "/headers/"
        headerfile = "{:s}/header_{:s}_{:.0f}K_{:.2e}bar.txt".format(
            hfolder, desc, temp, pressure)
        # Create and name outputs and results directories if they do not exist
        datadir = location_out + desc + '/outputs/'
        datadir = "{:s}/{:s}_{:.0f}K_{:.2e}bar/".format(
            datadir, desc, temp, pressure)
        if not os.path.exists(datadir):
            os.makedirs(datadir)

        # Export all values into machine and human readable output files
        file = '{:s}/lagrange_iteration-{:03d}_machine-read-nocorr.txt'.format(
            datadir, it_num)
        form.output(headerfile, it_num, speclist, y, x, delta, y_bar, x_bar,
                    delta_bar, file, verb)

        file = '{:s}/lagrange_iteration-{:03d}_visual-nocorr.txt'.format(
            datadir, it_num)
        form.fancyout(it_num, speclist, y, x, delta, y_bar, x_bar, delta_bar,
                      file, verb)

    return y, x, delta, y_bar, x_bar, delta_bar
Пример #9
0
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]
Пример #10
0
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)
Пример #11
0
        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)

Пример #12
0
def iterate(pressure,
            a,
            b,
            g_RT,
            maxiter,
            verb,
            times,
            guess,
            xtol=1e-8,
            save_info=None):
    """
    Run iterative Lagrangian minimization and lambda correction.

    Parameters
    ----------
    pressure: Float
          Atmospheric pressure (bar).
    a: 2D float ndarray
          Species stoichiometric coefficients.
    b: 1D float ndarray
          Elemental mixing fractions.
    g_RT: 1D float ndarray
          Species chemical potentials.
    maxiter: Integer
          Maximum number of iterations.
    verb: Integer
          Verbosity level (0=mute, 1=quiet, 2=verbose).
    times: Bool
          If True, track excecution times.
    guess: 1D list
          A two-element list with input guess values for x and x_bar.
    xtol: Float
          Error between iterations that is acceptable for convergence.
          The routine has converged when the sum of the relative improvement
          per species becomes less than xtol, i.e.:
         sum(abs((y-x)/y)) / len(x) <= xtol.
    save_info: List of stuff
          If not None, save info to files.  The list contains:
           [location_out, desc, speclist, temp]

    Returns
    -------
    y: float array
          Input guess of molecular species.
    x: float array
          Final mole numbers of molecular species.
    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.
    """

    # Retrieve header info
    i, j = np.shape(a)

    # Retrieve and set initial values
    x, x_bar = guess

    # Prepare data object for iterative process
    #         (see description of the 'input' object in lagrange.py)
    lc_data = x, x, 0, x_bar, x_bar, 0
    info = pressure, i, j, a, b, g_RT

    # ====================== PERFORM MAIN TEA LOOP ====================== #
    it_num = 1
    while it_num <= maxiter:
        # Output iteration number
        if verb >= 1 and (it_num % 10) == 0:
            stdout.write(' {:d}\r'.format(it_num))
            stdout.flush()

        # Time / speed testing for lagrange.py
        if times:
            ini = time.time()

        # Execute Lagrange minimization:
        lc_data = lg.lagrange(it_num, verb, lc_data, info, save_info)

        # Time / speed testing for lagrange.py
        if times:
            fin = time.time()
            elapsed = fin - ini
            print("lagrange {:>4d}:  {:f} s.".format(it_num, elapsed))

        # Print for debugging purposes
        if verb > 1:
            printout("Iteration {:d} Lagrange complete.  Starting lambda "
                     "correction...".format(it_num))

        # Check if x_i have negative mole numbers, if so, do lambda correction
        if np.any(lc_data[1] < 0):
            # Print for debugging purposes
            if verb > 1:
                printout('Correction required. Initializing...')

            # Time / speed testing for lambdacorr.py
            if times:
                ini = time.time()

            # Execute lambda correction
            lc_data = lc.lambdacorr(it_num, verb, lc_data, info, save_info)

            # Print for debugging purposes
            if times:
                fin = time.time()
                elapsed = fin - ini
                print("lambcorr {:>4d}:  {:f} s.".format(it_num, elapsed))

            # Print for debugging purposes
            if verb > 1:
                printout("Iteration {:d} lambda correction complete.  "
                         "Checking precision...".format(it_num))

        # Lambda correction is not needed
        else:
            # Print for debugging purposes
            if verb > 1:
                printout(
                    'Iteration {:d} did not need lambda correction.'.format(
                        it_num))

        # Check the tolerance
        xdiff = (lc_data[1] / lc_data[4]) / (lc_data[0] / lc_data[3]) - 1
        if np.sum(np.abs(xdiff)) / len(xdiff) <= xtol:
            if verb >= 1:
                stdout.write(' {:d}\r'.format(it_num))
                printout(
                    "The solution has converged to the given tolerance error.\n"
                )
            break

        it_num += 1
        # If max iteration not met, continue with next iteration cycle
        if verb > 1:
            printout('Max interation not met. Starting next iteration...\n')

    # ============== Stop the loop, max iteration reached ============== #

    # Stop if max iteration is reached
    if verb >= 1 and it_num == maxiter + 1:
        printout('Maximum iteration reached, ending minimization.\n')

    # Retrieve most recent iteration values
    input_new = lc_data

    # Take most recent x_i and x_bar values
    x_new = input_new[1]
    x_bar_new = input_new[4]

    # Calculate delta values
    delta = x_new - x

    # Calculate delta_bar values
    delta_bar = x_bar_new - x_bar

    if save_info is not None:
        location_out, desc, speclist, temp = save_info
        hfolder = location_out + desc + "/headers/"
        headerfile = "{:s}/header_{:s}_{:.0f}K_{:.2e}bar.txt".format(
            hfolder, desc, temp, pressure)

        # Create and name outputs and results directories if they do not exist
        datadirr = '{:s}{:s}/results/results_{:.0f}K_{:.2e}bar'.format(
            location_out, desc, temp, pressure)
        if not os.path.exists(datadirr):
            os.makedirs(datadirr)

        # Export all values into machine and human readable output files
        file = "{:s}/results-machine-read.txt".format(datadirr)
        form.output(headerfile, it_num, speclist, x, x_new, delta, x_bar,
                    x_bar_new, delta_bar, file, verb)
        file = "{:s}/results-visual.txt".format(datadirr)
        form.fancyout_results(headerfile, it_num, speclist, x, x_new, delta,
                              x_bar, x_bar_new, delta_bar, pressure, temp,
                              file, verb)

    return input_new
Пример #13
0
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)