예제 #1
0
def main():
    #Read in XYZ file. Store all the coordinates.
    simulation_atoms = XYZ()
    simulation_atoms.load(
        structure_file)  #simulation_atoms.rows contains the data
    print 'Structure file loaded successfully: ' + structure_file

    #Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)
    print 'Connection table file loaded successfully: ' + connection_table_file

    #The molecule helper class provides methods for working with both the XYZ
    #and connection table classes.
    molecule_helper = Molecule_Helper()
    molecule_helper.simulation_atoms_class = simulation_atoms
    molecule_helper.connection_table_class = connection_table
    molecule_helper.bondorder_cutoff = bondorder_cutoff

    print
    print 'List of molecules for the bond order: ' + str(bondorder_cutoff)
    print

    for each_connection_table in connection_table:
        all_molecules = molecule_helper.get_all_molecules()
        molecules_dict = molecule_helper.molecule_list_to_frequency_dict(
            all_molecules)

        print 'Iteration: ' + str(connection_table.iteration)
        print '------------------------------------------------------'
        for molecule_formula, molecule_freq in molecules_dict.iteritems():
            print str(molecule_freq) + ' x ' + molecule_formula
        print
예제 #2
0
def main():
    #Read in XYZ file. Store all the coordinates.
    simulation_atoms = XYZ()
    simulation_atoms.load(structure_file) #simulation_atoms.rows contains the data
    print 'Structure file loaded successfully: '+structure_file
    
    #Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)
    print 'Connection table file loaded successfully: '+connection_table_file

    #The molecule helper class provides methods for working with both the XYZ
    #and connection table classes.
    molecule_helper = Molecule_Helper()
    molecule_helper.simulation_atoms_class = simulation_atoms
    molecule_helper.connection_table_class = connection_table
    molecule_helper.bondorder_cutoff = bondorder_cutoff
    
    print
    print 'List of molecules for the bond order: '+str(bondorder_cutoff)
    print
    
    for each_connection_table in connection_table:
        all_molecules = molecule_helper.get_all_molecules()
        molecules_dict = molecule_helper.molecule_list_to_frequency_dict(all_molecules)
        
        print 'Iteration: '+str(connection_table.iteration)
        print '------------------------------------------------------'
        for molecule_formula, molecule_freq in molecules_dict.iteritems():
            print str(molecule_freq)+' x '+molecule_formula
        print
def includeall_isolate_grouped_reaction(each_grouped_reaction):
    '''
    Given a grouped reaction, will isolate the atoms based on the 'includeall'
    critera (all molecules in this grouped reaction over the entire iteration
    range will be kept).

    NOTE: This function depends on grouped_reactions and molecule_helper being
          global.
    '''
    molecule_numbers = get_all_unique_molecule_numbers(each_grouped_reaction)
    iterations = get_all_iterations(each_grouped_reaction)
    
    #We will find the range of iterations we need to operate over:
    start_iteration = iterations[0]
    end_iteration = iterations[-1]
    
    #Get all the atom numbers in the molecules involved in this reaction
    #pathway.
    atom_numbers = get_all_unique_atoms_for_molecule_numbers(
                    molecule_helper, molecule_numbers
                   )
    atom_numbers = set(atom_numbers) #Make into set so we can do quick lookups
    
    #Now we go through all the iterations in the appended XYZ file (xmolout)
    #and isolate these molecules. We do this by getting all the atoms in the
    #molecules we want to isolate. Then simply keep those atoms and delete
    #all other ones.
    xmolout = XYZ()
    xmolout.load(appendedxyz_file)
    
    new_xyz = XYZ() #We will use this to write our isolated XYZ structure.

    for each_xyz in xmolout:
        print '.',
        
        #Check if we are in the range of iterations that are grouped reactions
        #are defined for:
        if xmolout.iteration < start_iteration or \
           xmolout.iteration > end_iteration:
            continue #skip this iteration

        new_xyz.rows = []
        #Compare each atom to our atom numbers.
        for i, atom in enumerate(each_xyz):
            each_xyz_atom_number = i+1 #Since atom numbers start at 1 not 0.
            if each_xyz_atom_number in atom_numbers:
                new_xyz.rows.append(atom)
        #Now append this new xyz to our output file:
        output_xyz_filename = appendedxyz_output_file.replace(
                                '[grouped_reaction_number]',
                                str(grouped_reactions.grouped_reaction_number)
                              )
        new_xyz.export(output_xyz_filename, append=True)
예제 #4
0
def includeall_isolate_grouped_reaction(each_grouped_reaction):
    '''
    Given a grouped reaction, will isolate the atoms based on the 'includeall'
    critera (all molecules in this grouped reaction over the entire iteration
    range will be kept).

    NOTE: This function depends on grouped_reactions and molecule_helper being
          global.
    '''
    molecule_numbers = get_all_unique_molecule_numbers(each_grouped_reaction)
    iterations = get_all_iterations(each_grouped_reaction)

    #We will find the range of iterations we need to operate over:
    start_iteration = iterations[0]
    end_iteration = iterations[-1]

    #Get all the atom numbers in the molecules involved in this reaction
    #pathway.
    atom_numbers = get_all_unique_atoms_for_molecule_numbers(
        molecule_helper, molecule_numbers)
    atom_numbers = set(atom_numbers)  #Make into set so we can do quick lookups

    #Now we go through all the iterations in the appended XYZ file (xmolout)
    #and isolate these molecules. We do this by getting all the atoms in the
    #molecules we want to isolate. Then simply keep those atoms and delete
    #all other ones.
    xmolout = XYZ()
    xmolout.load(appendedxyz_file)

    new_xyz = XYZ()  #We will use this to write our isolated XYZ structure.

    for each_xyz in xmolout:
        print '.',

        #Check if we are in the range of iterations that are grouped reactions
        #are defined for:
        if xmolout.iteration < start_iteration or \
           xmolout.iteration > end_iteration:
            continue  #skip this iteration

        new_xyz.rows = []
        #Compare each atom to our atom numbers.
        for i, atom in enumerate(each_xyz):
            each_xyz_atom_number = i + 1  #Since atom numbers start at 1 not 0.
            if each_xyz_atom_number in atom_numbers:
                new_xyz.rows.append(atom)
        #Now append this new xyz to our output file:
        output_xyz_filename = appendedxyz_output_file.replace(
            '[grouped_reaction_number]',
            str(grouped_reactions.grouped_reaction_number))
        new_xyz.export(output_xyz_filename, append=True)
예제 #5
0
def tests():
    # Currently assume some relative path stuff. This is apt to change once we
    # make this into a module.
    sys.path.insert(0, "..")  # Make this the first thing since we want to override
    from XYZ import XYZ  # XYZ class
    from reax_connection_table import Connection_Table

    # Test files location:
    structure_file = "tests/a10a_ph7.xyz"
    connection_table_file = "tests/a10a_ph7.connect"

    # Read in XYZ file. Store all the coordinates.
    simulation_atoms = XYZ()
    simulation_atoms.load(structure_file)  # simulation_atoms.rows contains the data
    print "Structure file loaded successfully: " + structure_file

    # Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)
    connection_table.next()
    print "Connection table file loaded successfully: " + connection_table_file

    molecule_helper = Molecule_Helper()
    molecule_helper.simulation_atoms_class = simulation_atoms
    molecule_helper.connection_table_class = connection_table
    molecule_helper.bondorder_cutoff = 0.6

    assert molecule_helper.atom_label_list_to_formula(["H", "O", "H"]) == "H2O"
    # print molecule_helper.atom_label_list_to_formula(['H', 'O', 'H'])
    assert molecule_helper.atom_label_list_to_formula(["H"]) == "H"
    assert molecule_helper.atom_label_list_to_formula(["H", "H", "Ti", "O", "Ti", "P"]) == "H2OPTi2"

    all_molecules = molecule_helper.get_all_molecules()
    # We compare this to molfra.out file generated by ReaxFF with bond order
    # cutoff of 0.6
    assert molecule_helper.molecule_list_to_frequency_dict(all_molecules) == {
        "HO20Ti10": 1,
        "H2O81Ti40": 1,
        "H3O41Ti20": 1,
        "H2O": 149,
        "O20Ti10": 6,
        "HO": 1,
        "H2O40Ti20": 1,
        "HO21Ti10": 3,
    }

    print "All tests completed successfully!"
    sys.exit(0)
def exact_isolate_grouped_reaction(each_grouped_reaction):
    '''
    Given a grouped reaction, will isolate the atoms based on the 'exact'
    critera (only specific iterations that are defined will be processed and
    only the molecules defined for that iteration will be processed).

    NOTE: This function depends on grouped_reactions and molecule_helper being
          global.
    '''
    iterations = get_all_iterations(each_grouped_reaction)
    iterations = set(iterations)

    #Now we go through all the iterations in the appended XYZ file (xmolout)
    xmolout = XYZ()
    xmolout.load(appendedxyz_file)
    
    new_xyz = XYZ() #We will use this to write our isolated XYZ structure.

    for each_xyz in xmolout:
        print '.',
        
        #Check if we are at one of the iterations in our grouped reaction list.
        #If not, then we skip:
        if xmolout.iteration not in iterations:
            continue #skip this iteration
       
        #Otherwise, process this iteration, keeping ONLY the molecules defined
        #in this iteration.

        new_xyz.rows = []
        #Compare each atom to our atom numbers.
        for i, atom in enumerate(each_xyz):
            each_xyz_atom_number = i+1 #Since atom numbers start at 1 not 0.
            if each_xyz_atom_number in atom_numbers:
                new_xyz.rows.append(atom)
        #Now append this new xyz to our output file:
        output_xyz_filename = appendedxyz_output_file.replace(
                                '[grouped_reaction_number]',
                                str(grouped_reactions.grouped_reaction_number)
                              )
        new_xyz.export(output_xyz_filename, append=True)
예제 #7
0
def exact_isolate_grouped_reaction(each_grouped_reaction):
    '''
    Given a grouped reaction, will isolate the atoms based on the 'exact'
    critera (only specific iterations that are defined will be processed and
    only the molecules defined for that iteration will be processed).

    NOTE: This function depends on grouped_reactions and molecule_helper being
          global.
    '''
    iterations = get_all_iterations(each_grouped_reaction)
    iterations = set(iterations)

    #Now we go through all the iterations in the appended XYZ file (xmolout)
    xmolout = XYZ()
    xmolout.load(appendedxyz_file)

    new_xyz = XYZ()  #We will use this to write our isolated XYZ structure.

    for each_xyz in xmolout:
        print '.',

        #Check if we are at one of the iterations in our grouped reaction list.
        #If not, then we skip:
        if xmolout.iteration not in iterations:
            continue  #skip this iteration

        #Otherwise, process this iteration, keeping ONLY the molecules defined
        #in this iteration.

        new_xyz.rows = []
        #Compare each atom to our atom numbers.
        for i, atom in enumerate(each_xyz):
            each_xyz_atom_number = i + 1  #Since atom numbers start at 1 not 0.
            if each_xyz_atom_number in atom_numbers:
                new_xyz.rows.append(atom)
        #Now append this new xyz to our output file:
        output_xyz_filename = appendedxyz_output_file.replace(
            '[grouped_reaction_number]',
            str(grouped_reactions.grouped_reaction_number))
        new_xyz.export(output_xyz_filename, append=True)
예제 #8
0
def tests():
    #Currently assume some relative path stuff. This is apt to change once we
    #make this into a module.
    sys.path.insert(0, "..") #Make this the first thing since we want to override
    from XYZ import XYZ #XYZ class
    from reax_connection_table import Connection_Table
   
    #Test files location:
    structure_file = 'tests/a10a_ph7.xyz'
    connection_table_file = 'tests/a10a_ph7.connect'

    #Read in XYZ file. Store all the coordinates.
    simulation_atoms = XYZ()
    simulation_atoms.load(structure_file) #simulation_atoms.rows contains the data
    print 'Structure file loaded successfully: '+structure_file
    
    #Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)
    connection_table.next()
    print 'Connection table file loaded successfully: '+connection_table_file

    molecule_helper = Molecule_Helper()
    molecule_helper.simulation_atoms_class = simulation_atoms
    molecule_helper.connection_table_class = connection_table
    molecule_helper.bondorder_cutoff = 0.6

    assert molecule_helper.atom_label_list_to_formula(['H', 'O', 'H']) == 'H2O'
    #print molecule_helper.atom_label_list_to_formula(['H', 'O', 'H'])
    assert molecule_helper.atom_label_list_to_formula(['H']) == 'H'
    assert molecule_helper.atom_label_list_to_formula(['H', 'H', 'Ti', 'O', 'Ti', 'P']) == 'H2OPTi2'

    all_molecules = molecule_helper.get_all_molecules()
    #We compare this to molfra.out file generated by ReaxFF with bond order
    #cutoff of 0.6
    assert molecule_helper.molecule_list_to_frequency_dict(all_molecules) == \
        {'HO20Ti10': 1, 'H2O81Ti40': 1, 'H3O41Ti20': 1, 'H2O': 149, 'O20Ti10': 6, 'HO': 1, 'H2O40Ti20': 1, 'HO21Ti10': 3}
    
    print 'All tests completed successfully!'
    sys.exit(0)
예제 #9
0
def main():
    #Read in XYZ file. Store all the coordinates.
    simulation_atoms = XYZ()
    simulation_atoms.load(structure_file) #simulation_atoms.rows contains the data
    print 'Structure file loaded successfully: '+structure_file
    
    #Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)
    print 'Connection table file loaded successfully: '+connection_table_file

    #The molecule helper class provides methods for working with both the XYZ
    #and connection table classes.
    molecule_helper = Molecule_Helper()
    molecule_helper.simulation_atoms_class = simulation_atoms
    molecule_helper.connection_table_class = connection_table
    molecule_helper.bondorder_cutoff = bondorder_cutoff

    #Open output file for writing:
    rxns_f = file(rxns_output_file, 'w')
    
    #Pretty/Instructional Output Stuff:
    rxns_f.write('#(NOTE: The molecule numbers are printed in parentheses. '+\
                 'ie. (3). The full table of molecules will be written to '+ 
                 'a binary file.)'+"\n\n")
    
    print 'Finding reactions',

    #Loop through all iterations. We are doing a do...while type loop here:
    connection_table_current = connection_table.next()
    for connection_table_next in connection_table:
        #We say that these are the reactions for the current iteration (and not
        #'from previous iteration to this iteration' just for simplicity.
        rxns_f.write('Reaction(s) for iteration: '+str(connection_table.iteration)+"\n")
        rxns_f.write('-------------------------------------------'+"\n\n")

        #Diff the two sets of molecules based on change in bond order. Figure out
        #which atoms have bond orders that were originally below the BO cutoff and
        #are now equal to or above the BO cutoff. This gives the atoms that have
        #changed connectivity.
        changed_atoms_entries = get_atoms_that_have_connection_changes(connection_table_current,
                            connection_table_next, bondorder_cutoff)
        #Then take these atoms that have changed, and get the molecule that
        #corresponds to it. First, let's transform the output we get into just a
        #list of changed atoms. Our approach is to put all the atoms into a list.
        #Then convert it into a set since that automatically eliminates all duplicate
        #elements.
        just_changed_atoms = []
        for each_changed_atom_entry in changed_atoms_entries:
            just_changed_atoms.append(each_changed_atom_entry[0])
            just_changed_atoms.append(each_changed_atom_entry[1])
        changed_atom_numbers = set(just_changed_atoms)

        #We want to connect reactants to products. See my notebook vol 2, p99 for
        #more explanation.
        #1. Get molecule from changed atoms. First, from reactants:
        reactant_molecules = []
        connection_table.rows = connection_table_current
        for each_changed_atom_number in changed_atom_numbers:
             reactant_molecules.append(
                 molecule_helper.get_molecule_for_atom(each_changed_atom_number)
             )
        #From products:
        product_molecules = []
        connection_table.rows = connection_table_next
        for each_changed_atom_number in changed_atom_numbers:
             product_molecules.append(
                 molecule_helper.get_molecule_for_atom(each_changed_atom_number)
             )
        #Now get rid of exact duplicates:
        reactant_molecules = remove_molecule_duplicates(reactant_molecules)
        product_molecules = remove_molecule_duplicates(product_molecules)
        
        #2. Now we relate the reactants and molecules with each other by finding 
        #   common atoms between them.
        reactants_to_products_mapping = group_reactants_and_products(
                                            reactant_molecules,
                                            product_molecules
                                        )

        #Suppress molecule rearrangement if needed. We define molecule rearragement
        #as when molecules on both sides of a reaction are the same!
        if suppress_molecule_rearrangment == True:
            new_reactants_to_products_mapping = []
            for each_mapping in reactants_to_products_mapping:
                if each_mapping['reactants'] != each_mapping['products']:
                    new_reactants_to_products_mapping.append(each_mapping)
            reactants_to_products_mapping = new_reactants_to_products_mapping

        #Output chemical formulas. This is a helper function for map:
        def molecule_to_chemical_formula_wrapper(molecule):
            '''
            Allows us to pass extra args when using map.
            '''
            return molecule_helper.molecule_to_chemical_formula(
                molecule, True
            )
        for each_reaction in reactants_to_products_mapping:
            #Get chemical formula. We include the molecule number next to each
            #formula:
            reactant_formulas = map(molecule_to_chemical_formula_wrapper,
                                    each_reaction['reactants'])
            product_formulas = map(molecule_to_chemical_formula_wrapper,
                                   each_reaction['products'])

            #List to string:
            reactant_formulas = ' + '.join(reactant_formulas)
            product_formulas = ' + '.join(product_formulas)
            rxns_f.write(reactant_formulas+' -> '+product_formulas+"\n\n")
    
        #Alright, let's move on to the next iteration,
        connection_table_current = connection_table_next
        #Give some indication of progress:
        print '.',
        rxns_f.write("\n")
    
    rxns_f.close()
    print
    print 'Successfully found reactions for every i to i+1 iteration: '+\
            rxns_output_file
    
    #Now that we generated all the reactions for i and i+1, let's output the
    #molecule dictionary as a pickled file.
    molecule_helper.save_molecule_list(molecules_output_file)
    print 'Successfully saved molecule list in binary format: '+molecules_output_file
예제 #10
0
def main():

    #Read in XYZ file. Store all the coordinates.
    global simulation_atoms, simulation_atoms_dict
    simulation_atoms = XYZ()
    simulation_atoms.load(
        structure_file)  #simulation_atoms.rows contains the data
    simulation_atoms_dict = simulation_atoms.return_sorted_by_atoms_dict()
    #NOTE: Now we have two tables, simulation_atoms and simulation_atoms_dict

    #Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    global connection_table  #So that other functions can access this
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)

    #Testing
    #tests()

    #Go through each of the target molecules and do isolation stuff:
    for target_molecule in target_molecules:
        #Select an atom to work with. We just select the first one. Used for the 'exclude'
        #case so that we don't have to check every single atom in simulation_atoms:
        an_atom = target_molecule[0]
        #Loop through all the atoms matching the an_atom type:
        for atom_number, each_atom in enumerate(simulation_atoms.rows):
            #Correction to the atom_number since XYZ list starts at 0 but atom numbers
            #start at 1:
            atom_number += 1

            #Somewhat of a hack: Do a quick check here to see if this atom has been
            #nullified in the XYZ object. If so, we can just skip it since this atom
            #has been "deleted".
            if each_atom == None:
                continue  #skip this atom since it has been deleted

            #If the isolate_method is 'include' then we have to check all atoms because
            #there are cases of single atoms and/or atoms not connected to the target
            #molecule atoms that we defined which we won't catch unless we check every
            #atom. If the isolate method is 'exclude' then we just have to check atoms that
            #are connected to an atom in the defined target molecule.
            if isolate_method == 'exclude' and each_atom[0] != an_atom:
                #Skip this atom for now. If it's part of the target molecule, we'll
                #automatically come back to it later.
                continue

            #For the current atom, get the molecule that corresponds to it:
            molecule_parts = get_molecule_for_atom(atom_number)

            #Check this molecule against our isolation specification see if match exists:
            same_molecules_q = are_molecules_the_same( \
             list(target_molecule),
             molecule_parts_to_list(molecule_parts),
             isolate_criteria
             )

            #Get the molecule numbers so that we can feed it into the nullify atoms func.
            molecule_atom_numbers = molecule_parts.keys()
            #Now keep/remove depending on criteria:
            if isolate_method == 'include':
                if same_molecules_q != True:
                    #print molecule_parts
                    #The molecules are not the same so we need to delete it
                    nullify_atoms_in_XYZ(molecule_atom_numbers)
            elif isolate_method == 'exclude':
                if same_molecules_q == True:
                    #This molecule is in the excluded list so we need to delete it
                    nullify_atoms_in_XYZ(molecule_atom_numbers)
            else:
                print 'ERROR: isolate_method option invalid!'
                sys.exit(0)

    #Cool, we now ran through the whole XYZ file. Let's save the changed version:
    simulation_atoms.export(output_xyz_file)
    print 'Processed XYZ file exported: ' + output_xyz_file
예제 #11
0
def main():
    #Read in XYZ file. Store all the coordinates.
    simulation_atoms = XYZ()
    simulation_atoms.load(
        structure_file)  #simulation_atoms.rows contains the data

    #Pre-sort the atoms by atom type (into a dictionary). This way, we don't
    #have to loop through the whole file every time we calculate distances for
    #a given atom.
    simulation_atoms_dict = {}
    for atom_number, each_row in enumerate(simulation_atoms.rows):
        #Correction to the atom_number since it starts at 1 instead of 0:
        atom_number += 1

        #We want our new list to be in the format:
        #atom_number x y z
        temp_list = [atom_number]  #Put it in a list first. For some reason,
        temp_list.extend(each_row[1:])  #can't combine these two on same line.

        #Now save it:
        try:
            simulation_atoms_dict[each_row[0]].append(temp_list)
        except KeyError:
            #This means that the dictionary entry for this atom has not been
            #created yet. So we create it:
            simulation_atoms_dict[each_row[0]] = [temp_list]  #New list

    #Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)

    #Loop through each pair of atoms.
    distance_histogram = {
    }  #Dictionary instead of array so we can add entries at any element.
    #TODO: I should add a try to this outer loop too to catch if from_atom
    #      doesn't exist in the dictionary
    for from_atom_row in simulation_atoms_dict[from_atom]:
        #Calculate interatomic distances. Use the minimum image convention here to
        #take care of any periodic conditions. We start by checking through all the
        #other atoms to see if they match our to_atom:
        try:
            for to_atom_row in simulation_atoms_dict[to_atom]:
                #Make sure this isn't the same atom by comparing atom number:
                if from_atom_row[0] == to_atom_row[0]:
                    continue

                #Make sure this to_atom isn't part of the same molecule. We figure
                #this out by using the molecule column of the connection table:
                if exclude_atoms_from_same_molecule:
                    if connection_table.rows[from_atom_row[0]][-1] == \
                       connection_table.rows[to_atom_row[0]][-1]:
                        #We have the same molecule. Don't do anything for this to_atom.
                        continue
                        #pass

                #Otherwise, calculate the interatomic distance:
                from_atom_coord = (from_atom_row[1], from_atom_row[2],
                                   from_atom_row[3])
                to_atom_coord = (to_atom_row[1], to_atom_row[2],
                                 to_atom_row[3])
                interatomic_distance = calc_interatomic_distance(
                    from_atom_coord, to_atom_coord)
                #print interatomic_distance

                #Now figure out which bin this distance goes in. The algorithm in
                #Simulation of Liquids by Allen (p182) differs from the algorithm in
                #Understanding Molecular Simulation by Frenkel (p86) in that Allen rounds
                #up (by adding 1) while Frenkel doesn't round up. Adri's RDF script
                #follows Allen's method. I will take the middle road and round the number:
                #(NOTE: This method is used because FORTRAN only had arrays so the bins
                #       have to be integers. We do it in this case because floats can't
                #       be exactly represented in any prog. language. So this int method
                #       is actually not too bad.
                distance_bin = int(round(interatomic_distance / bin_size))
                try:
                    #We add two for the contribution of both atoms because we just want
                    #to find the number of atoms within a certain distance of each other
                    #(also see p86 of Frenkel).
                    distance_histogram[distance_bin] += 2
                except KeyError:
                    #This is the first entry for this key. So we'll create it:
                    distance_histogram[distance_bin] = 2
        except KeyError:
            #to_atom was not found in the atoms dictionary
            print 'ERROR: ' + to_atom + ' was not found in the structure file: ' + structure_file
            sys.exit(1)

    #print distance_histogram

    #Normalize the histrogram by comparing to ideal gas.
    #We want to compute (p184 Allen):
    # g(r + 1/2 * delta_r) = n(b)/n_id(b) , no clue what b is though
    #where according to p183 Allen and corroborated by p86 Frenkel and Adri's RDF script:
    # n(b) = n_his(b)/(N * t_run)
    #In our case, we are only calculating from one static frame, so t_run = 1. The reason
    #why we divide by N is to get the "average". It sort of doesn't make sense to me since
    #this would make n(b) always fractional. But if n_id(b) is also fractional, then it
    #would work out.
    #Also according to p184 Allen:
    # n_id(b) = (4 pi rho)/3 * [(r + dr)^3 - r^3]
    #which makes sense. NOTE: To convert from the bin index to length (in angstroms) we
    #reverse what we do by multiplying by the bin_size (aka delta_r).
    #Also, rho (the number density) is defined by wikipedia and MatDL wiki as:
    # rho = N/V ; (num of particles)/(volume of system)
    #where N is the number of particles we are considering.
    #But Adri's RDF script doesn't calculate rho this way. He does some sort of ratio
    #of the two atom populations...
    #total_number_atoms = len(simulation_atoms.rows)
    number_from_atoms = len(simulation_atoms_dict[from_atom])
    number_to_atoms = len(simulation_atoms_dict[to_atom])
    if from_atom == to_atom:
        total_number_analyzed_atoms = number_from_atoms
        #No combination correction factor:
        combination_correction_factor = 1
    else:
        total_number_analyzed_atoms = number_from_atoms + number_to_atoms
        #Calculate combination correction factor. This factor allows the ideal
        #gas approx to match the number of combinations that the n_his makes.
        #For more information, see p77 of Vol 2 of my notebook.
        heterogeneous_combinations = number_from_atoms * number_to_atoms
        homogeneous_combinations = \
         (total_number_analyzed_atoms * (total_number_analyzed_atoms-1)) / 2
        combination_correction_factor = \
         heterogeneous_combinations/float(homogeneous_combinations)
    total_number_molecules = connection_table.rows[-1][-1]
    total_volume = float(unit_cell[0]) * unit_cell[1] * unit_cell[2]
    rho = total_number_analyzed_atoms / total_volume
    g = {
    }  #This is our pair correlation function results. However, have to store as bins, not angstroms

    for distance_bin, n_his in distance_histogram.iteritems():
        n = float(n_his)  #/total_number_atoms
        #n = float(n_his)/total_number_molecules
        r = float(distance_bin
                  ) * bin_size  #convert from bin index to length (angstroms)
        n_id = ((4 * math.pi * rho) / 3) * ((r + bin_size)**3 - r**3)
        #Additional correction to n_id.
        n_id *= number_from_atoms + number_to_atoms
        n_id *= combination_correction_factor

        #print n, n_id
        g[distance_bin] = n / n_id

    #Now print it out with angstroms!
    #print "r \t g(r)"
    #for distance_bin, gr in g.iteritems():
    #   #The format syntax is that we have _____._____ (5 spots before the . and 5 spots
    #   #after). This will cover like 99.99% of all numbers we encounter.
    #   print "%10.5f \t %10.5f" % (float(distance_bin) * bin_size, gr)
    #   #pass

    #Output to file:
    try:
        output_f = file(output_file, 'w')
    except (NameError, IOError):
        print 'ERROR: Could not write output to ' + output_file
        sys.exit(1)

    #Alternative method for printing it out. We print only for r <= 1/2 L, where L is
    #the average length of all the sides of the box. According to Deserno's paper on
    #calculating g(r) in 3-D, once we assume minimum image convention, the n_id term
    #no longer increases like how we defined it above. So g(r) is only accurate to 1/2 L.
    average_L = (float(unit_cell[0]) + unit_cell[1] + unit_cell[2]) / 3
    max_bin = int(round((average_L / 2) / bin_size))  #Convert to integer bin
    #print "r \t g(r)"
    output_f.write("r \t g(r)\n")
    for distance_bin in xrange(0, max_bin):
        #The format syntax is that we have _____._____ (5 spots before the . and 5 spots
        #after). This will cover like 99.99% of all numbers we encounter.
        try:
            #print "%10.5f \t %10.5f" % (float(distance_bin) * bin_size, g[distance_bin])
            output_f.write("%10.5f \t %10.5f\n" %
                           (float(distance_bin) * bin_size, g[distance_bin]))
        except KeyError:
            #g doesn't have this distance_bin entry. No worries, since we know it is zero:
            #print "%10.5f \t %10.5f" % (float(distance_bin) * bin_size, 0.0)
            output_f.write("%10.5f \t %10.5f\n" %
                           (float(distance_bin) * bin_size, 0.0))

    output_f.close()
    print 'RDF as tab separated values written to: ' + output_file

    #Add in GNUPlot input file generation. Use below for smoothing:
    '''
    plot "[rdf_tsv_file]" u 1:2 with linespoints, \
         "[rdf_tsv_file]" u 1:2 smooth bezier
    '''
    gnuplot_template = '''
reset
set title "Radial Distribution Function"
set xlabel "r (angstrom)"
set ylabel "g(r)"
set nokey
plot "[rdf_tsv_file]" u 1:2 with linespoints
set term png
set output "[output_image_file]"
replot
'''
    try:
        gnuplot_template = gnuplot_template.replace('[rdf_tsv_file]',
                                                    output_file)
        output_image_file = os.path.splitext(gnuplot_file)[0] + '.png'
        gnuplot_template = gnuplot_template.replace('[output_image_file]',
                                                    output_image_file)
        #Write to file:
        gnuplot_f = open(gnuplot_file, 'w')
        gnuplot_f.write(gnuplot_template)
        gnuplot_f.close()
        print 'GNUPlot file written to: ' + gnuplot_file

        #Now generate the graphs
        os.system('gnuplot ' + gnuplot_file)
        print 'GNUPlot graph generated!'
        time.sleep(2)
        os.system('gqview')
    except IOError:
        print 'ERROR: Could not write gnuplot file to: ' + gnuplot_file
    except NameError:
        #Do nothing
        pass
예제 #12
0
def main():
    #Read in XYZ file. Store all the coordinates.
    simulation_atoms = XYZ()
    simulation_atoms.load(structure_file) #simulation_atoms.rows contains the data
    
    #Pre-sort the atoms by atom type (into a dictionary). This way, we don't
    #have to loop through the whole file every time we calculate distances for
    #a given atom.
    simulation_atoms_dict = {}
    for atom_number, each_row in enumerate(simulation_atoms.rows):
        #Correction to the atom_number since it starts at 1 instead of 0:
        atom_number += 1
        
        #We want our new list to be in the format:
        #atom_number x y z
        temp_list = [atom_number] #Put it in a list first. For some reason, 
        temp_list.extend(each_row[1:]) #can't combine these two on same line.
        
        #Now save it:
        try:
            simulation_atoms_dict[each_row[0]].append(temp_list)
        except KeyError:
            #This means that the dictionary entry for this atom has not been
            #created yet. So we create it:
            simulation_atoms_dict[each_row[0]] = [temp_list] #New list
    
    #Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
    connection_table = Connection_Table()
    connection_table.load(connection_table_file)
    
    #Loop through each pair of atoms.
    distance_histogram = {} #Dictionary instead of array so we can add entries at any element.
    #TODO: I should add a try to this outer loop too to catch if from_atom
    #      doesn't exist in the dictionary
    for from_atom_row in simulation_atoms_dict[from_atom]:
        #Calculate interatomic distances. Use the minimum image convention here to
        #take care of any periodic conditions. We start by checking through all the
        #other atoms to see if they match our to_atom:
        try:
            for to_atom_row in simulation_atoms_dict[to_atom]:
                #Make sure this isn't the same atom by comparing atom number:
                if from_atom_row[0] == to_atom_row[0]:
                    continue
                
                #Make sure this to_atom isn't part of the same molecule. We figure
                #this out by using the molecule column of the connection table:
                if exclude_atoms_from_same_molecule:
                    if connection_table.rows[from_atom_row[0]][-1] == \
                       connection_table.rows[to_atom_row[0]][-1]:
                        #We have the same molecule. Don't do anything for this to_atom.
                        continue
                        #pass
                
                #Otherwise, calculate the interatomic distance:
                from_atom_coord = (from_atom_row[1], from_atom_row[2], from_atom_row[3])
                to_atom_coord = (to_atom_row[1], to_atom_row[2], to_atom_row[3])
                interatomic_distance = calc_interatomic_distance(from_atom_coord, to_atom_coord)
                #print interatomic_distance
                
                #Now figure out which bin this distance goes in. The algorithm in 
                #Simulation of Liquids by Allen (p182) differs from the algorithm in
                #Understanding Molecular Simulation by Frenkel (p86) in that Allen rounds
                #up (by adding 1) while Frenkel doesn't round up. Adri's RDF script
                #follows Allen's method. I will take the middle road and round the number:
                #(NOTE: This method is used because FORTRAN only had arrays so the bins
                #       have to be integers. We do it in this case because floats can't
                #       be exactly represented in any prog. language. So this int method
                #       is actually not too bad.
                distance_bin = int(round(interatomic_distance / bin_size))
                try:
                    #We add two for the contribution of both atoms because we just want
                    #to find the number of atoms within a certain distance of each other
                    #(also see p86 of Frenkel).
                    distance_histogram[distance_bin] += 2
                except KeyError:
                    #This is the first entry for this key. So we'll create it:
                    distance_histogram[distance_bin] = 2
        except KeyError:
            #to_atom was not found in the atoms dictionary
            print 'ERROR: '+to_atom+' was not found in the structure file: '+structure_file
            sys.exit(1)
    
    #print distance_histogram

    #Normalize the histrogram by comparing to ideal gas.
    #We want to compute (p184 Allen): 
    # g(r + 1/2 * delta_r) = n(b)/n_id(b) , no clue what b is though
    #where according to p183 Allen and corroborated by p86 Frenkel and Adri's RDF script:
    # n(b) = n_his(b)/(N * t_run)
    #In our case, we are only calculating from one static frame, so t_run = 1. The reason
    #why we divide by N is to get the "average". It sort of doesn't make sense to me since
    #this would make n(b) always fractional. But if n_id(b) is also fractional, then it
    #would work out.
    #Also according to p184 Allen:
    # n_id(b) = (4 pi rho)/3 * [(r + dr)^3 - r^3]
    #which makes sense. NOTE: To convert from the bin index to length (in angstroms) we 
    #reverse what we do by multiplying by the bin_size (aka delta_r).
    #Also, rho (the number density) is defined by wikipedia and MatDL wiki as:
    # rho = N/V ; (num of particles)/(volume of system)
    #where N is the number of particles we are considering.
    #But Adri's RDF script doesn't calculate rho this way. He does some sort of ratio
    #of the two atom populations...
    #total_number_atoms = len(simulation_atoms.rows)
    number_from_atoms = len(simulation_atoms_dict[from_atom])
    number_to_atoms = len(simulation_atoms_dict[to_atom])
    if from_atom == to_atom:
        total_number_analyzed_atoms = number_from_atoms
        #No combination correction factor:
        combination_correction_factor = 1
    else:
        total_number_analyzed_atoms = number_from_atoms + number_to_atoms
        #Calculate combination correction factor. This factor allows the ideal
        #gas approx to match the number of combinations that the n_his makes.
        #For more information, see p77 of Vol 2 of my notebook.
        heterogeneous_combinations = number_from_atoms * number_to_atoms
        homogeneous_combinations = \
         (total_number_analyzed_atoms * (total_number_analyzed_atoms-1)) / 2
        combination_correction_factor = \
         heterogeneous_combinations/float(homogeneous_combinations)
    total_number_molecules = connection_table.rows[-1][-1]
    total_volume = float(unit_cell[0]) * unit_cell[1] * unit_cell[2]
    rho = total_number_analyzed_atoms/total_volume
    g = {} #This is our pair correlation function results. However, have to store as bins, not angstroms
    
    for distance_bin, n_his in distance_histogram.iteritems():
        n = float(n_his)#/total_number_atoms
        #n = float(n_his)/total_number_molecules
        r = float(distance_bin) * bin_size #convert from bin index to length (angstroms)
        n_id = ((4 * math.pi * rho)/3) * ( (r + bin_size)**3 - r**3 )
        #Additional correction to n_id.
        n_id *= number_from_atoms + number_to_atoms
        n_id *= combination_correction_factor
        
        #print n, n_id
        g[distance_bin] = n/n_id
    
    #Now print it out with angstroms!
    #print "r \t g(r)"
    #for distance_bin, gr in g.iteritems():
    #   #The format syntax is that we have _____._____ (5 spots before the . and 5 spots
    #   #after). This will cover like 99.99% of all numbers we encounter.
    #   print "%10.5f \t %10.5f" % (float(distance_bin) * bin_size, gr)
    #   #pass

    #Output to file:
    try:
        output_f = file(output_file, 'w')
    except (NameError, IOError):
        print 'ERROR: Could not write output to '+output_file
        sys.exit(1)
    
    #Alternative method for printing it out. We print only for r <= 1/2 L, where L is
    #the average length of all the sides of the box. According to Deserno's paper on
    #calculating g(r) in 3-D, once we assume minimum image convention, the n_id term 
    #no longer increases like how we defined it above. So g(r) is only accurate to 1/2 L.
    average_L = (float(unit_cell[0]) + unit_cell[1] + unit_cell[2])/3
    max_bin = int(round((average_L/2) / bin_size)) #Convert to integer bin
    #print "r \t g(r)"
    output_f.write("r \t g(r)\n")
    for distance_bin in xrange(0, max_bin):
        #The format syntax is that we have _____._____ (5 spots before the . and 5 spots
        #after). This will cover like 99.99% of all numbers we encounter.
        try:
            #print "%10.5f \t %10.5f" % (float(distance_bin) * bin_size, g[distance_bin])
            output_f.write("%10.5f \t %10.5f\n" % (float(distance_bin) * bin_size, g[distance_bin]))
        except KeyError:
            #g doesn't have this distance_bin entry. No worries, since we know it is zero:
            #print "%10.5f \t %10.5f" % (float(distance_bin) * bin_size, 0.0)
            output_f.write("%10.5f \t %10.5f\n" % (float(distance_bin) * bin_size, 0.0))

    output_f.close()
    print 'RDF as tab separated values written to: '+output_file

    #Add in GNUPlot input file generation. Use below for smoothing:
    '''
    plot "[rdf_tsv_file]" u 1:2 with linespoints, \
         "[rdf_tsv_file]" u 1:2 smooth bezier
    '''
    gnuplot_template = '''
reset
set title "Radial Distribution Function"
set xlabel "r (angstrom)"
set ylabel "g(r)"
set nokey
plot "[rdf_tsv_file]" u 1:2 with linespoints
set term png
set output "[output_image_file]"
replot
'''
    try:
        gnuplot_template = gnuplot_template.replace('[rdf_tsv_file]', output_file)
        output_image_file = os.path.splitext(gnuplot_file)[0]+'.png'
        gnuplot_template = gnuplot_template.replace('[output_image_file]', output_image_file)
        #Write to file:
        gnuplot_f = open(gnuplot_file, 'w')
        gnuplot_f.write(gnuplot_template)
        gnuplot_f.close()
        print 'GNUPlot file written to: '+gnuplot_file

        #Now generate the graphs
        os.system('gnuplot '+gnuplot_file)
        print 'GNUPlot graph generated!'
        time.sleep(2)
        os.system('gqview')
    except IOError:
        print 'ERROR: Could not write gnuplot file to: '+gnuplot_file
    except NameError:
        #Do nothing
        pass
예제 #13
0
def main():
	
	#Read in XYZ file. Store all the coordinates.
	global simulation_atoms, simulation_atoms_dict
	simulation_atoms = XYZ()
	simulation_atoms.load(structure_file) #simulation_atoms.rows contains the data
	simulation_atoms_dict = simulation_atoms.return_sorted_by_atoms_dict()
	#NOTE: Now we have two tables, simulation_atoms and simulation_atoms_dict

	#Read in connection table (ReaxFF fort.7 style, see ReaxFF manual for more info)
	global connection_table #So that other functions can access this
	connection_table = Connection_Table()
	connection_table.load(connection_table_file)

	#Testing
	#tests()

	#Go through each of the target molecules and do isolation stuff:
	for target_molecule in target_molecules:
		#Select an atom to work with. We just select the first one. Used for the 'exclude'
		#case so that we don't have to check every single atom in simulation_atoms:
		an_atom = target_molecule[0] 
		#Loop through all the atoms matching the an_atom type:
		for atom_number, each_atom in enumerate(simulation_atoms.rows):
			#Correction to the atom_number since XYZ list starts at 0 but atom numbers
			#start at 1:
			atom_number += 1

			#Somewhat of a hack: Do a quick check here to see if this atom has been
			#nullified in the XYZ object. If so, we can just skip it since this atom
			#has been "deleted".
			if each_atom == None:
				continue #skip this atom since it has been deleted
			
			#If the isolate_method is 'include' then we have to check all atoms because
			#there are cases of single atoms and/or atoms not connected to the target
			#molecule atoms that we defined which we won't catch unless we check every 
			#atom. If the isolate method is 'exclude' then we just have to check atoms that
			#are connected to an atom in the defined target molecule.
			if isolate_method == 'exclude' and each_atom[0] != an_atom:
				#Skip this atom for now. If it's part of the target molecule, we'll
				#automatically come back to it later.
				continue
			
			#For the current atom, get the molecule that corresponds to it:
			molecule_parts = get_molecule_for_atom(atom_number)
			
			#Check this molecule against our isolation specification see if match exists:
			same_molecules_q = are_molecules_the_same( \
				list(target_molecule),
				molecule_parts_to_list(molecule_parts),
				isolate_criteria
				)

			#Get the molecule numbers so that we can feed it into the nullify atoms func.
			molecule_atom_numbers = molecule_parts.keys()
			#Now keep/remove depending on criteria:
			if isolate_method == 'include':
				if same_molecules_q != True:
					#print molecule_parts
					#The molecules are not the same so we need to delete it	
					nullify_atoms_in_XYZ(molecule_atom_numbers)
			elif isolate_method == 'exclude':
				if same_molecules_q == True:
					#This molecule is in the excluded list so we need to delete it
					nullify_atoms_in_XYZ(molecule_atom_numbers)
			else:
				print 'ERROR: isolate_method option invalid!'
				sys.exit(0)

	#Cool, we now ran through the whole XYZ file. Let's save the changed version:
	simulation_atoms.export(output_xyz_file)
	print 'Processed XYZ file exported: '+output_xyz_file