Beispiel #1
0
    def from_dict(self, reaction_dict):
        """
        A helper function for loading this object from a dictionary in a YAML file for restarting ARC
        """
        self.index = reaction_dict[
            'index'] if 'index' in reaction_dict else None
        self.label = reaction_dict['label'] if 'label' in reaction_dict else ''
        self.multiplicity = reaction_dict[
            'multiplicity'] if 'multiplicity' in reaction_dict else None
        self.charge = reaction_dict[
            'charge'] if 'charge' in reaction_dict else 0
        self.reactants = reaction_dict[
            'reactants'] if 'reactants' in reaction_dict else None
        self.products = reaction_dict[
            'products'] if 'products' in reaction_dict else None
        self.family = reaction_dict[
            'family'] if 'family' in reaction_dict else None
        self.family_own_reverse = reaction_dict[
            'family_own_reverse'] if 'family_own_reverse' in reaction_dict else 0
        if 'rmg_reaction' in reaction_dict:
            self.rmg_reaction_from_str(
                reaction_string=reaction_dict['rmg_reaction'])
        else:
            self.rmg_reaction = None
        self.set_label_reactants_products()
        if self.rmg_reaction is None and (self.reactants is None
                                          or self.products is None):
            raise InputError(
                'Cannot determine reactants and/or products labels for reaction {0}'
                .format(self.label))
        if self.reactants is None or self.products is None:
            if not all([
                    spc.label for spc in self.rmg_reaction.reactants +
                    self.rmg_reaction.products
            ]):
                raise InputError(
                    'All species in a reaction must be labeled (and the labels must correspond'
                    ' to respective Species in ARC). If an RMG Reaction object was passes, make'
                    ' sure that all species in the reactants and products are correctly labeled.'
                    ' Problematic reaction: {0}'.format(self.label))
            self.reactants = [spc.label for spc in self.rmg_reaction.reactants]
            self.products = [spc.label for spc in self.rmg_reaction.products]
        self.set_label_reactants_products()
        if self.ts_label is None:
            self.ts_label = reaction_dict[
                'ts_label'] if 'ts_label' in reaction_dict else None
        self.r_species = [r.from_dict() for r in reaction_dict['r_species']
                          ] if 'r_species' in reaction_dict else list()
        self.p_species = [p.from_dict() for p in reaction_dict['p_species']
                          ] if 'p_species' in reaction_dict else list()
        self.ts_species = reaction_dict['ts_species'].from_dict(
        ) if 'ts_species' in reaction_dict else None

        self.long_kinetic_description = reaction_dict['long_kinetic_description']\
            if 'long_kinetic_description' in reaction_dict else ''
        self.ts_methods = reaction_dict[
            'ts_methods'] if 'ts_methods' in reaction_dict else default_ts_methods
        self.ts_methods = [tsm.lower() for tsm in self.ts_methods]
        self.ts_xyz_guess = reaction_dict[
            'ts_xyz_guess'] if 'ts_xyz_guess' in reaction_dict else list()
Beispiel #2
0
def rdkit_conf_from_mol(mol, coordinates):
    """
     Generate an RDKit Conformer object from an RMG Molecule object

    Args:
        mol (Molecule): The RMG Molecule object.
        coordinates (list, str): The coordinates (in any format) of the conformer,
                                          atoms must be ordered as in the molecule.

    Returns:
        Conformer: An RDKit Conformer object.
    Returns:
        RDMol: An RDKit Molecule object.
    Returns:
        dict: Atom index map. Keys are atom indices in the RMG Molecule, values are atom indices in the RDKit Molecule.
    """
    if coordinates is None or not coordinates:
        raise InputError('Cannot process empty coordinates, got: {0}'.format(coordinates))
    if isinstance(coordinates[0], (str, unicode)) and isinstance(coordinates, list):
        raise InputError('The coordinates argument seem to be of wrong type. Got a list of strings:\n{0}'.format(
            coordinates))
    if isinstance(coordinates, (str, unicode)):
        coordinates = get_xyz_matrix(xyz=coordinates)[0]
    rd_mol, rd_indices = to_rdkit_mol(mol=mol, remove_h=False, return_mapping=True)
    Chem.AllChem.EmbedMolecule(rd_mol)
    index_map = dict()
    for xyz_index, atom in enumerate(mol.atoms):  # generate an atom index mapping dictionary
        index_map[xyz_index] = rd_indices[atom]
    conf = rd_mol.GetConformer(id=0)
    for i in range(rd_mol.GetNumAtoms()):
        conf.SetAtomPosition(index_map[i], coordinates[i])  # reset atom coordinates
    return conf, rd_mol, index_map
Beispiel #3
0
def check_xyz_species_for_drawing(xyz, species):
    """A helper function to avoid repetitive code"""
    if species is not None and xyz is None:
        xyz = xyz if xyz is not None else species.final_xyz
    if species is not None and not isinstance(species, ARCSpecies):
        raise InputError('Species must be an ARCSpecies instance. Got {0}.'.format(type(species)))
    if species is not None and species.final_xyz is None:
        raise InputError('Species {0} has an empty final_xyz attribute.'.format(species.label))
    return xyz
Beispiel #4
0
 def __init__(self,
              label='',
              reactants=None,
              products=None,
              ts_label=None,
              rmg_reaction=None,
              ts_methods=None,
              ts_xyz_guess=None,
              multiplicity=None,
              charge=0,
              reaction_dict=None):
     self.arrow = ' <=> '
     self.plus = ' + '
     self.r_species = list()
     self.p_species = list()
     self.kinetics = None
     self.rmg_kinetics = None
     self.long_kinetic_description = ''
     self.family = None
     self.family_own_reverse = 0
     self.ts_label = ts_label
     self.dh_rxn298 = None
     self.rmg_reactions = None
     if reaction_dict is not None:
         # Reading from a dictionary
         self.from_dict(reaction_dict=reaction_dict)
     else:
         # Not reading from a dictionary
         self.label = label
         self.index = None
         self.ts_species = None
         self.multiplicity = multiplicity
         self.charge = charge
         if self.multiplicity is not None and not isinstance(
                 self.multiplicity, int):
             raise InputError(
                 'Reaction multiplicity must be an integer, got {0} of type {1}.'
                 .format(self.multiplicity, type(self.multiplicity)))
         self.reactants = reactants
         self.products = products
         self.rmg_reaction = rmg_reaction
         if self.rmg_reaction is None and (
                 self.reactants is None
                 or self.products is None) and not self.label:
             raise InputError(
                 'Cannot determine reactants and/or products labels for reaction {0}'
                 .format(self.label))
         self.set_label_reactants_products()
         self.ts_methods = ts_methods if ts_methods is not None else default_ts_methods
         self.ts_methods = [tsm.lower() for tsm in self.ts_methods]
         self.ts_xyz_guess = ts_xyz_guess if ts_xyz_guess is not None else list(
         )
     if len(self.reactants) > 3 or len(self.products) > 3:
         raise ReactionError(
             'An ARC Reaction can have up to three reactants / products. got {0} reactants'
             ' and {1} products for reaction {2}.'.format(
                 len(self.reactants), len(self.products), self.label))
Beispiel #5
0
def get_xyz_matrix(xyz):
    """
    Convert a string xyz form::

        C    0.6616514836    0.4027481525   -0.4847382281
        N   -0.6039793084    0.6637270105    0.0671637135
        H   -1.4226865648   -0.4973210697   -0.2238712255
        H   -0.4993010635    0.6531020442    1.0853092315
        H   -2.2115796924   -0.4529256762    0.4144516252
        H   -1.8113671395   -0.3268900681   -1.1468957003

    into a list of lists xyz form::

        [[0.6616514836, 0.4027481525, -0.4847382281],
        [-0.6039793084, 0.6637270105, 0.0671637135],
        [-1.4226865648, -0.4973210697, -0.2238712255],
        [-0.4993010635, 0.6531020442, 1.0853092315],
        [-2.2115796924, -0.4529256762, 0.4144516252],
        [-1.8113671395, -0.3268900681, -1.1468957003]]

    Args:
        xyz (str): The xyz coordinates to conver.

    Returns:
        list: Array-style coordinates.
    Returns:
        list: Chemical symbols of the elements in xyz, order preserved.
    Returns:
        list: X axis values.
    Returns:
        list: Y axis values.
    Returns:
        list: Z axis values.
    """
    if not isinstance(xyz, (str, unicode)):
        raise InputError('Can only convert sting or unicode to list, got: {0}'.format(xyz))
    xyz = standardize_xyz_string(xyz)
    x, y, z, symbols = list(), list(), list(), list()
    for line in xyz.split('\n'):
        if line:
            line_split = line.split()
            if len(line_split) != 4:
                raise InputError('Expecting each line in an xyz string to have 4 values, e.g.:\n'
                                 'C    0.1000    0.2000   -0.3000\nbut got {0} values:\n{1}'.format(
                                  len(line_split), line))
            atom, xx, yy, zz = line.split()
            x.append(float(xx))
            y.append(float(yy))
            z.append(float(zz))
            symbols.append(atom)
    coords = list()
    for i, _ in enumerate(x):
        coords.append([x[i], y[i], z[i]])
    return coords, symbols, x, y, z
Beispiel #6
0
 def upload_file(self,
                 remote_file_path,
                 local_file_path='',
                 file_string=''):
     """
     Upload `local_file_path` or the contents of `file_string` to `remote_file_path`.
     Either `file_string` or `local_file_path` must be given.
     """
     if local_file_path and not os.path.isfile(local_file_path):
         raise InputError('Cannot upload a non-existing file.'
                          ' Check why file in path {0} is missing.'.format(
                              local_file_path))
     sftp, ssh = self.connect()
     times_tried = 0
     max_times_to_try = 10
     success = False
     while not success and times_tried < max_times_to_try:
         times_tried += 1
         try:
             write_file(sftp, remote_file_path, local_file_path,
                        file_string)
         except IOError:
             pass
         else:
             success = True
     if times_tried == max_times_to_try:
         raise ServerError('Could not write file {0} on {1}'.format(
             remote_file_path, self.server))
     sftp.close()
     ssh.close()
Beispiel #7
0
def get_center_of_mass(xyz=None, coords=None, symbols=None):
    """
    Get the center of mass of xyz coordinates.
    Assumes arc.converter.standardize_xyz_string() was already called for xyz.
    Note that xyz from ESS output is usually already centered at the center of mass (to some precision).
    Either xyz or coords and symbols must be given.

    Args:
        xyz (string, optional): The xyz coordinates in a string format.
        coords (list, optional): The xyz coordinates in an array format.
        symbols (list, optional): The chemical element symbols corresponding to `coords`.

    Returns:
        tuple: The center of mass coordinates.
    """
    if xyz is not None:
        masses, coords = list(), list()
        for line in xyz.splitlines():
            if line.strip():
                splits = line.split()
                masses.append(get_element_mass(str(splits[0]))[0])
                coords.append([float(splits[1]), float(splits[2]), float(splits[3])])
    elif coords is not None and symbols is not None:
        masses = [get_element_mass(str(symbol))[0] for symbol in symbols]
    else:
        raise InputError('Either xyz or coords and symbols must be given')
    cm_x, cm_y, cm_z = 0, 0, 0
    for coord, mass in zip(coords, masses):
        cm_x += coord[0] * mass
        cm_y += coord[1] * mass
        cm_z += coord[2] * mass
    cm_x /= sum(masses)
    cm_y /= sum(masses)
    cm_z /= sum(masses)
    return cm_x, cm_y, cm_z
Beispiel #8
0
def parse_frequencies(path, software):
    if not os.path.isfile(path):
        raise InputError('Could not find file {0}'.format(path))
    freqs = np.array([], np.float64)
    if software.lower() == 'qchem':
        with open(path, 'rb') as f:
            for line in f:
                if ' Frequency:' in line:
                    items = line.split()
                    for i, item in enumerate(items):
                        if i:
                            freqs = np.append(freqs, [(float(item))])
    elif software.lower() == 'gaussian':
        with open(path, 'rb') as f:
            line = f.readline()
            while line != '':
                if 'Frequencies --' in line:
                    freqs = np.append(freqs,
                                      [float(frq) for frq in line.split()[2:]])
                line = f.readline()
    else:
        raise ValueError(
            'parse_frequencies() can curtrently only parse QChem and gaussian files,'
            ' got {0}'.format(software))
    logging.debug(
        'Using parser.parse_frequencies. Determined frequencies are: {0}'.
        format(freqs))
    return freqs
Beispiel #9
0
def translate_to_center_of_mass(xyz=None, coords=None, symbols=None):
    """
    Translate coordinates to their center of mass.
    Must give either xyz or coords along with symbols.

    Args:
        xyz (str, optional): A molecule's coordinates in string-format.
        coords (list): A molecule's coordinates in array-format.
        symbols (list): The matching elemental symbols for the coordinates.

    Returns:
        list or str: The translated coordinates in the input format.
    """
    if xyz is not None:
        coords, symbols, _, _, _ = get_xyz_matrix(xyz)
    if coords is None or symbols is None:
        raise InputError('Could not translate coordinates to center of mass. Got coords = {0} and '
                         'symbols = {1}.'.format(coords, symbols))
    cm_x, cm_y, cm_z = get_center_of_mass(coords=coords, symbols=symbols)
    x = [coord[0] - cm_x for coord in coords]
    y = [coord[1] - cm_y for coord in coords]
    z = [coord[2] - cm_z for coord in coords]
    translated_coords = [[xi, yi, zi] for xi, yi, zi in zip(x, y, z)]
    if xyz is not None:
        return get_xyz_string(coords=translated_coords, symbols=symbols)
    else:
        return translated_coords
Beispiel #10
0
def _get_lines_from_file(path):
    """A helper function for getting a list of lines from the file at `path`"""
    if os.path.isfile(path):
        with open(path, 'r') as f:
            lines = f.readlines()
    else:
        raise InputError('Could not find file {0}'.format(path))
    return lines
Beispiel #11
0
def parse_t1(path):
    """
    Parse the T1 parameter from a Molpro coupled cluster calculation
    """
    if not os.path.isfile(path):
        raise InputError('Could not find file {0}'.format(path))
    t1 = None
    with open(path, 'rb') as f:
        for line in f:
            if 'T1 diagnostic:' in line:
                t1 = float(line.split()[-1])
    return t1
Beispiel #12
0
def parse_e_elect(path, zpe_scale_factor=1.):
    """
    Parse the zero K energy, E0, from an sp job output file.
    """
    if not os.path.isfile(path):
        raise InputError('Could not find file {0}'.format(path))
    log = determine_qm_software(fullpath=path)
    try:
        e_elect = log.loadEnergy(zpe_scale_factor) * 0.001  # convert to kJ/mol
    except Exception:
        logger.warning('Could not read e_elect from {0}'.format(path))
        e_elect = None
    return e_elect
Beispiel #13
0
def parse_e0(path):
    """
    Parse the zero K energy, E0, from an sp job output file
    """
    if not os.path.isfile(path):
        raise InputError('Could not find file {0}'.format(path))
    log = determine_qm_software(fullpath=path)
    try:
        e0 = log.loadEnergy(frequencyScaleFactor=1.) * 0.001  # convert to kJ/mol
    except Exception:
        logging.warning('Could not read E0 from {0}'.format(path))
        e0 = None
    return e0
Beispiel #14
0
def read_yaml_file(path):
    """
    Read a YAML file (usually an input / restart file, but also conformers file)
    and return the parameters as python variables.

    Args:
        path (str): The YAML file path to read.

    Returns:
        dict or list: The content read from the file.
    """
    if not os.path.isfile(path):
        raise InputError('Could not find the YAML file {0}'.format(path))
    with open(path, 'r') as f:
        content = yaml.load(stream=f, Loader=yaml.FullLoader)
    return content
Beispiel #15
0
def parse_xyz_from_file(path):
    """
    Parse xyz coordinated from:
    .xyz - XYZ file
    .gjf - Gaussian input file
    .out or .log - ESS output file (Gaussian, QChem, Molpro)
    other - Molpro or QChem input file
    """
    with open(path, 'r') as f:
        lines = f.readlines()
    _, file_extension = os.path.splitext(path)

    xyz = None
    relevant_lines = list()

    if file_extension == '.xyz':
        relevant_lines = lines[2:]
    elif file_extension == '.gjf':
        for line in lines[5:]:
            if line and line != '\n' and line != '\r\n':
                relevant_lines.append(line)
            else:
                break
    elif 'out' in file_extension or 'log' in file_extension:
        log = Log(path='')
        log.determine_qm_software(fullpath=path)
        coord, number, mass = log.software_log.loadGeometry()
        xyz = get_xyz_string(xyz=coord, number=number)
    else:
        record = False
        for line in lines:
            if '$end' in line or '}' in line:
                break
            if record and len(line.split()) == 4:
                relevant_lines.append(line)
            elif '$molecule' in line:
                record = True
            elif 'geometry={' in line:
                record = True
        if not relevant_lines:
            raise InputError(
                'Could not parse xyz coordinates from file {0}'.format(path))
    if xyz is None and relevant_lines:
        xyz = ''.join([line for line in relevant_lines if line])
    return xyz
Beispiel #16
0
def determine_ess(log_file):
    """
    Determine the ESS to which the log file belongs.

    Args:
        log_file (str): The ESS log file path.

    Returns:
        str: The ESS (either 'gaussian', 'qchem', or 'molpro'.
    """
    log = determine_qm_software(log_file)
    if isinstance(log, GaussianLog):
        return 'gaussian'
    if isinstance(log, QChemLog):
        return 'qchem'
    if isinstance(log, MolproLog):
        return 'molpro'
    raise InputError('Could not identify the log file in {0} as belonging to Gaussian, QChem, or Molpro.')
Beispiel #17
0
def get_atom_radius(symbol):
    """
    Get the atom covalent radius in Angstroms, data in the ATOM_RADII dict.
    (Change to QCElemental after transitioning to Py3)

    Args:
        symbol (str): The atomic symbol.

    Returns:
        float: The atomic covalent radius (None if not found).
    """
    if not isinstance(symbol, (str, unicode)):
        raise InputError('the symbol argument must be string, got {0} which is a {1}'.format(symbol, type(symbol)))

    if symbol in ATOM_RADII:
        return ATOM_RADII[symbol]
    else:
        return None
Beispiel #18
0
def load_families_only(rmgdb, kinetics_families='default'):
    """
    A helper function for loading kinetic families from RMG's database
    """
    if kinetics_families not in ('default', 'all', 'none'):
        if not isinstance(kinetics_families, list):
            raise InputError("kineticsFamilies should be either 'default', 'all', 'none', or a list of names, e.g.,"
                             " ['H_Abstraction','R_Recombination'] or ['!Intra_Disproportionation'].")
    logging.debug('\n\nLoading only kinetic families from the RMG database...')
    rmgdb.load(
        path=db_path,
        thermoLibraries=list(),
        transportLibraries='none',
        reactionLibraries=list(),
        seedMechanisms=list(),
        kineticsFamilies=kinetics_families,
        kineticsDepositories=['training'],
        depository=False,
    )
Beispiel #19
0
def get_xyz_matrix(xyz):
    """
    Convert a string xyz form:
    C    0.6616514836    0.4027481525   -0.4847382281
    N   -0.6039793084    0.6637270105    0.0671637135
    H   -1.4226865648   -0.4973210697   -0.2238712255
    H   -0.4993010635    0.6531020442    1.0853092315
    H   -2.2115796924   -0.4529256762    0.4144516252
    H   -1.8113671395   -0.3268900681   -1.1468957003
    into a list of lists xyz form:
    [[0.6616514836, 0.4027481525, -0.4847382281],
    [-0.6039793084, 0.6637270105, 0.0671637135],
    [-1.4226865648, -0.4973210697, -0.2238712255],
    [-0.4993010635, 0.6531020442, 1.0853092315],
    [-2.2115796924, -0.4529256762, 0.4144516252],
    [-1.8113671395, -0.3268900681, -1.1468957003]]

    Returns xyz as well as atom symbols, x, y, and z separately
    """
    xyz = standardize_xyz_string(xyz)
    x, y, z, symbols = [], [], [], []
    for line in xyz.split('\n'):
        if line:
            line_split = line.split()
            if len(line_split) != 4:
                raise InputError(
                    'Expecting each line in an xyz string to have 4 elements, e.g.:\n'
                    'C    0.1000    0.2000   -0.3000\nbut got {0} elements:\n{1}'
                    .format(len(line_split), line))
            atom, xx, yy, zz = line.split()
            x.append(float(xx))
            y.append(float(yy))
            z.append(float(zz))
            symbols.append(atom)
    xyz = []
    for i, _ in enumerate(x):
        xyz.append([x[i], y[i], z[i]])
    return xyz, symbols, x, y, z
Beispiel #20
0
def initialize_job_types(job_types):
    """
    A helper function for initializing job_types.
    Returns the comprehensive (default values for missing job types) job types for ARC.

    Args:
        job_types (dict): Keys are job types, values are booleans of whether or not to consider this job type.

    Returns:
        job_types (dict): An updated (comprehensive) job type dictionary.
    """
    defaults_to_true = ['conformers', 'opt', 'fine', 'freq', 'sp', '1d_rotors']
    defaults_to_false = ['onedmin', 'orbitals', 'bde']
    if job_types is None:
        job_types = default_job_types
    if 'lennard_jones' in job_types:
        # rename lennard_jones to OneDMin
        job_types['onedmin'] = job_types['lennard_jones']
        del job_types['lennard_jones']
    if 'fine_grid' in job_types:
        # rename fine_grid to fine
        job_types['fine'] = job_types['fine_grid']
        del job_types['fine_grid']
    for job_type in defaults_to_true:
        if job_type not in job_types:
            # set default value to True if this job type key is missing
            job_types[job_type] = True
    for job_type in defaults_to_false:
        if job_type not in job_types:
            # set default value to False if this job type key is missing
            job_types[job_type] = False
    for job_type in job_types.keys():
        if job_type not in defaults_to_true and job_type not in defaults_to_false:
            raise InputError("Job type '{0}' not supported. Check the job types dictionary "
                             "(either in ARC's input or in default_job_types under settings)".format(job_type))
    job_types_report = [job_type for job_type, val in job_types.items() if val]
    logger.info('\nConsidering the following job types: {0}\n'.format(job_types_report))
    return job_types
Beispiel #21
0
 def upload_file(self,
                 remote_file_path,
                 local_file_path='',
                 file_string=''):
     """
     Upload `local_file_path` or the contents of `file_string` to `remote_file_path`.
     Either `file_string` or `local_file_path` must be given.
     """
     if local_file_path and not os.path.isfile(local_file_path):
         raise InputError('Cannot upload a non-existing file.'
                          ' Check why file in path {0} is missing.'.format(
                              local_file_path))
     sftp, ssh = self.connect()
     i, max_times_to_try = 1, 30
     success = False
     sleep_time = 10  # seconds
     while i < 30:
         try:
             write_file(sftp, remote_file_path, local_file_path,
                        file_string)
         except IOError:
             logger.error('Could not upload file {0} to {1}!'.format(
                 local_file_path, self.server))
             logger.error(
                 'ARC is sleeping for {0} seconds before re-trying,'
                 ' please check your connectivity.'.format(sleep_time * i))
             logger.info('ZZZZZ..... ZZZZZ.....')
             time.sleep(sleep_time * i)  # in seconds
         else:
             success = True
             i = 1000
         i += 1
     if not success:
         raise ServerError(
             'Could not write file {0} on {1}. Tried {2} times.'.format(
                 remote_file_path, self.server, max_times_to_try))
     sftp.close()
     ssh.close()
Beispiel #22
0
def load_rmg_database(rmgdb,
                      thermo_libraries=None,
                      reaction_libraries=None,
                      kinetics_families='default',
                      load_thermo_libs=True,
                      load_kinetic_libs=True):
    """
    A helper function for loading the RMG database
    """
    thermo_libraries = thermo_libraries if thermo_libraries is not None else list(
    )
    reaction_libraries = reaction_libraries if reaction_libraries is not None else list(
    )
    if isinstance(thermo_libraries, str):
        thermo_libraries.replace(' ', '')
        thermo_libraries = [lib for lib in thermo_libraries.split(',')]
    if isinstance(reaction_libraries, (str, unicode)):
        reaction_libraries.replace(' ', '')
        reaction_libraries = [lib for lib in reaction_libraries.split(',')]
        reaction_libraries = [reaction_libraries]
    if kinetics_families not in ('default', 'all', 'none'):
        if not isinstance(kinetics_families, list):
            raise InputError(
                "kineticsFamilies should be either 'default', 'all', 'none', or a list of names, e.g.,"
                " ['H_Abstraction','R_Recombination'] or ['!Intra_Disproportionation']."
            )
    if not thermo_libraries:
        thermo_path = os.path.join(db_path, 'thermo', 'libraries')
        for thermo_library_path in os.listdir(thermo_path):
            thermo_library, _ = os.path.splitext(
                os.path.basename(thermo_library_path))
            thermo_libraries.append(thermo_library)
        # prioritize libraries
        thermo_priority = [
            'BurkeH2O2', 'thermo_DFT_CCSDTF12_BAC', 'DFT_QCI_thermo',
            'Klippenstein_Glarborg2016', 'primaryThermoLibrary', 'primaryNS',
            'NitrogenCurran', 'NOx2018', 'FFCM1(-)', 'SulfurLibrary',
            'SulfurGlarborgH2S'
        ]
        indices_to_pop = []
        for i, lib in enumerate(thermo_libraries):
            if lib in thermo_priority:
                indices_to_pop.append(i)
        for i in reversed(
                range(len(thermo_libraries))
        ):  # pop starting from the end, so other indices won't change
            if i in indices_to_pop:
                thermo_libraries.pop(i)
        thermo_libraries = thermo_priority + thermo_libraries

    if not reaction_libraries:
        kinetics_path = os.path.join(db_path, 'kinetics', 'libraries')
        reaction_libraries = os.listdir(kinetics_path)
        indices_to_pop = list()
        second_level_libraries = list()
        for i, library in enumerate(reaction_libraries):
            if not os.path.isfile(
                    os.path.join(kinetics_path, library, 'reactions.py')):
                indices_to_pop.append(i)
                second_level_libraries.extend([library + '/' + second_level\
                                               for second_level in os.listdir(os.path.join(kinetics_path, library))])
        for i in reversed(
                range(len(reaction_libraries))
        ):  # pop starting from the end, so other indices won't change
            if i in indices_to_pop:
                reaction_libraries.pop(i)
        reaction_libraries.extend(second_level_libraries)

    # set library to be represented by a string rather than a unicode,
    # this might not be needed after a full migration to Py3
    thermo_libraries = [str(lib) for lib in thermo_libraries]
    reaction_libraries = [str(lib) for lib in reaction_libraries]
    if not load_kinetic_libs:
        reaction_libraries = list()
    if not load_thermo_libs:
        thermo_libraries = list()
    # reaction_libraries = list()  # empty library list for debugging
    logging.info('\n\nLoading the RMG database...')

    rmgdb.load(
        path=db_path,
        thermoLibraries=thermo_libraries,
        transportLibraries=['PrimaryTransportLibrary', 'NOx2018', 'GRI-Mech'],
        reactionLibraries=reaction_libraries,
        seedMechanisms=list(),
        kineticsFamilies=kinetics_families,
        kineticsDepositories=['training'],
        depository=False,
    )
    for family in rmgdb.kinetics.families.values():
        try:
            family.addKineticsRulesFromTrainingSet(thermoDatabase=rmgdb.thermo)
        except KineticsError:
            logging.info('Could not train family {0}'.format(family))
        else:
            family.fillKineticsRulesByAveragingUp(verbose=False)
    logging.info('\n\n')
Beispiel #23
0
def determine_scaling_factors(levels_of_theory,
                              ess_settings=None,
                              init_log=True):
    """
    Determine the zero-point energy, harmonic frequencies, and fundamental frequencies scaling factors
    for a given frequencies level of theory.

    Args:
        levels_of_theory (list, str): A list of frequencies levels of theory
                                               for which scaling factors are determined.
                                               A string can also be passed for just one level of theory.
        ess_settings (dict, optional): A dictionary of available ESS (keys) and a corresponding server list (values).
        init_log (bool, optional): Whether to initialize the logger. True to initialize.
                                   Should be True when called as a stand alone, and False when called within ARC.

    Returns:
        str: The modified level of theory
    """
    if init_log:
        initialize_log(log_file='scaling_factor.log',
                       project='Scaling Factors')

    if isinstance(levels_of_theory, (str, unicode)):
        levels_of_theory = [levels_of_theory]
    if not isinstance(levels_of_theory, list):
        raise InputError(
            'levels_of_theory must be a list (or a string if only one level is desired). Got: {0}'
            .format(type(levels_of_theory)))
    t0 = time.time()

    logger.info('\n\n\n')
    logger.info(HEADER)
    logger.info('\n\nstarting ARC...\n')

    # only run opt (fine) and freq
    job_types = initialize_job_types(
        dict())  # get the defaults, so no job type is missing
    job_types = {job_type: False for job_type in job_types.keys()}
    job_types['opt'], job_types['fine'], job_types['freq'] = True, True, True

    lambda_zpes, zpe_dicts, times = list(), list(), list()
    for level_of_theory in levels_of_theory:
        t1 = time.time()
        logger.info(
            '\nComputing scaling factors at the {0} level of theory...\n\n'.
            format(level_of_theory))
        renamed_level = rename_level(level_of_theory)
        project = 'scaling_' + renamed_level
        project_directory = os.path.join(arc_path, 'Projects',
                                         'scaling_factors', project)

        species_list = get_species_list()

        if '//' in level_of_theory:
            raise InputError(
                'Level of theory should either be a composite method or in a method/basis-set format. '
                'Got {0}'.format(level_of_theory))
        if '/' not in level_of_theory:  # assume this is a composite method
            freq_level = ''
            composite_method = level_of_theory.lower()
            job_types['freq'] = False
        else:
            freq_level = level_of_theory.lower()
            composite_method = ''
            job_types['freq'] = True

        ess_settings = check_ess_settings(ess_settings or global_ess_settings)

        Scheduler(project=project,
                  project_directory=project_directory,
                  species_list=species_list,
                  composite_method=composite_method,
                  opt_level=freq_level,
                  freq_level=freq_level,
                  ess_settings=ess_settings,
                  job_types=job_types,
                  allow_nonisomorphic_2d=True)

        zpe_dict = dict()
        for spc in species_list:
            try:
                zpe = get_zpe(
                    os.path.join(project_directory, 'output', 'Species',
                                 spc.label, 'geometry', 'freq.out'))
            except Exception:
                zpe = None
            zpe_dict[spc.label] = zpe
        zpe_dicts.append(zpe_dict)

        lambda_zpes.append(
            calculate_truhlar_scaling_factors(zpe_dict, level_of_theory))
        times.append(time_lapse(t1))

    summarize_results(lambda_zpes, levels_of_theory, zpe_dicts, times,
                      time_lapse(t0))
    logger.info('\n\n\n')
    logger.info(HEADER)

    harmonic_freq_scaling_factors = [
        lambda_zpe * 1.014 for lambda_zpe in lambda_zpes
    ]
    return harmonic_freq_scaling_factors