예제 #1
0
def read_yaml_file(
    path: str,
    project_directory: Optional[str] = None,
) -> Union[dict, list]:
    """
    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.
        project_directory (str, optional): The current project directory to rebase upon.

    Returns: Union[dict, list]
        The content read from the file.
    """
    if project_directory is not None:
        path = globalize_paths(path, project_directory)
    if not isinstance(path, str):
        raise InputError(
            f'path must be a string, got {path} which is a {type(path)}')
    if not os.path.isfile(path):
        raise InputError(f'Could not find the YAML file {path}')
    with open(path, 'r') as f:
        content = yaml.load(stream=f, Loader=yaml.FullLoader)
    return content
예제 #2
0
 def __init__(self,
              label: str = '',
              reactants: Optional[List[str]] = None,
              products: Optional[List[str]] = None,
              ts_label: Optional[str] = None,
              rmg_reaction: Optional[Reaction] = None,
              ts_methods: Optional[List[str]] = None,
              ts_xyz_guess: Optional[list] = None,
              multiplicity: Optional[int] = None,
              charge: int = 0,
              reaction_dict: Optional[dict] = None,
              preserve_param_in_scan: Optional[list] = 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
     self.ts_xyz_guess = ts_xyz_guess or list()
     self.preserve_param_in_scan = preserve_param_in_scan
     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()
         self._atom_map = None
     if len(self.reactants) > 3 or len(self.products) > 3:
         raise ReactionError(f'An ARC Reaction can have up to three reactants / products. got {len(self.reactants)} '
                             f'reactants and {len(self.products)} products for reaction {self.label}.')
     if self.ts_xyz_guess is not None and not isinstance(self.ts_xyz_guess, list):
         self.ts_xyz_guess = [self.ts_xyz_guess]
     self.check_atom_balance()
예제 #3
0
def sort_two_lists_by_the_first(
    list1: List[Union[float, int, None]],
    list2: List[Union[float, int, None]],
) -> Tuple[List[Union[float, int]], List[Union[float, int]]]:
    """
    Sort two lists in increasing order by the values of the first list.
    Ignoring None entries from list1 and their respective entries in list2.
    The function was written in this format rather the more pytonic ``zip(*sorted(zip(list1, list2)))`` style
    to accommodate for dictionaries as entries of list2, otherwise a
    ``TypeError: '<' not supported between instances of 'dict' and 'dict'`` error is raised.

    Args:
        list1 (list, tuple): Entries are floats or ints (could also be None).
        list2 (list, tuple): Entries could be anything.

    Raises:
        InputError: If types are wrong, or lists are not the same length.

    Returns: Tuple[list, list]
        - Sorted values from list1, ignoring None entries.
        - Respective entries from list2.
    """
    if not isinstance(list1,
                      (list, tuple)) or not isinstance(list2, (list, tuple)):
        raise InputError(
            f'Arguments must be lists, got: {type(list1)} and {type(list2)}')
    for entry in list1:
        if not isinstance(entry, (float, int)) and entry is not None:
            raise InputError(
                f'Entries of list1 must be either floats or integers, got: {type(entry)}.'
            )
    if len(list1) != len(list2):
        raise InputError(
            f'Both lists must be the same length, got {len(list1)} and {len(list2)}'
        )

    # remove None entries from list1 and their respective entries from list2:
    new_list1, new_list2 = list(), list()
    for entry1, entry2 in zip(list1, list2):
        if entry1 is not None:
            new_list1.append(entry1)
            new_list2.append(entry2)
    indices = list(range(len(new_list1)))

    zipped_lists = zip(new_list1, indices)
    sorted_lists = sorted(zipped_lists)
    sorted_list1 = [x for x, _ in sorted_lists]
    sorted_indices = [x for _, x in sorted_lists]
    sorted_list2 = [0] * len(new_list2)
    for counter, index in enumerate(sorted_indices):
        sorted_list2[counter] = new_list2[index]
    return sorted_list1, sorted_list2
예제 #4
0
파일: arkane.py 프로젝트: rvkmr1989/ARC
    def __init__(self,
                 output_directory: str,
                 output_dict: dict,
                 bac_type: Optional[str],
                 sp_level: Optional[Level] = None,
                 freq_scale_factor: float = 1.0,
                 species: Type[ARCSpecies] = None,
                 reaction: Type[ARCReaction] = None,
                 species_dict: dict = None,
                 T_min: tuple = None,
                 T_max: tuple = None,
                 T_count: int = 50,
                 three_params: bool = True,
                 ):
        self.output_directory = output_directory
        self.output_dict = output_dict
        self.bac_type = bac_type
        self.sp_level = sp_level
        self.freq_scale_factor = freq_scale_factor
        self.species = species
        self.reaction = reaction
        self.species_dict = species_dict
        self.T_min = T_min
        self.T_max = T_max
        self.T_count = T_count
        self.three_params = three_params

        if not self.output_directory:
            raise InputError('A project directory was not provided.')
예제 #5
0
파일: parser.py 프로젝트: rvkmr1989/ARC
def parse_1d_scan_energies(
        path: str) -> Tuple[Optional[List[float]], Optional[List[float]]]:
    """
    Parse the 1D torsion scan energies from an ESS log file.

    Args:
        path (str): The ESS log file to parse from.

    Raises:
        InputError: If ``path`` is invalid.

    Returns: Tuple[Optional[List[float]], Optional[List[float]]]
        The electronic energy in kJ/mol and the dihedral scan angle in degrees.
    """
    if not os.path.isfile(path):
        raise InputError(f'Could not find file {path}')
    log = ess_factory(fullpath=path)
    try:
        energies, angles = log.load_scan_energies()
        energies *= 0.001  # convert to kJ/mol
        angles *= 180 / np.pi  # convert to degrees
    except (LogError, NotImplementedError, ZeroDivisionError):
        logger.warning(f'Could not read energies from {path}')
        energies, angles = None, None
    return energies, angles
예제 #6
0
파일: rmgdb.py 프로젝트: rvkmr1989/ARC
def load_families_only(rmgdb, kinetics_families='default'):
    """
    A helper function for loading kinetic families from RMG's database.

    Args:
        rmgdb (RMGDatabase): The RMG database instance.
        kinetics_families (str, optional): Specific kinetics families to load.
    """
    if kinetics_families not in ('default', 'all', 'none'):
        if not isinstance(kinetics_families, list):
            raise InputError(
                "kinetics families should be either 'default', 'all', 'none', or a list of names, e.g.,"
                " ['H_Abstraction','R_Recombination'] or ['!Intra_Disproportionation']."
            )
    logger.debug('\n\nLoading only kinetic families from the RMG database...')
    rmgdb.load(
        path=db_path,
        thermo_libraries=list(),
        transport_libraries='none',
        reaction_libraries=list(),
        seed_mechanisms=list(),
        kinetics_families=kinetics_families,
        kinetics_depositories=['training'],
        depository=False,
    )
예제 #7
0
    def upload_file(
        self,
        remote_file_path: str,
        local_file_path: str = '',
        file_string: str = '',
    ) -> None:
        """
        Upload a local file or contents from a string to the remote server.

        Args:
            remote_file_path (str): The path to write into on the remote server.
            local_file_path (Optional[str]): The local file path to be copied to the remote location.
            file_string (Optional[str]): The file content to be copied and saved as the remote file.

        Raises:
            InputError: If both `local_file_path` or `file_string` are invalid,
                        or `local_file_path` does not exists.
            ServerError: If the file cannot be uploaded with maximum times to try
        """
        if not local_file_path and not file_string:
            raise InputError(
                'Cannot not upload file to server. Either `file_string` or `local_file_path`'
                ' must be specified')
        if local_file_path and not os.path.isfile(local_file_path):
            raise InputError(
                f'Cannot upload a non-existing file. '
                f'Check why file in path {local_file_path} is missing.')
        # If the directory does not exist, _upload_file cannot create a file based on the given path
        remote_dir_path = os.path.dirname(remote_file_path)
        if not self._check_dir_exists(remote_dir_path):
            self._create_dir(remote_dir_path)

        try:
            if file_string:
                with self._sftp.open(remote_file_path, 'w') as f_remote:
                    f_remote.write(file_string)
            else:
                self._sftp.put(localpath=local_file_path,
                               remotepath=remote_file_path)
        except IOError:
            logger.debug(
                f'Could not upload file {local_file_path} to {self.server}!')
            raise ServerError(
                f'Could not write file {remote_file_path} on {self.server}. ')
예제 #8
0
파일: arkane.py 프로젝트: rvkmr1989/ARC
    def compute_thermo(self,
                       kinetics_flag: bool = False,
                       e0_only: bool = False,
                       ) -> None:
        """
        Generate thermodynamic data for a species.
        Populates the species.thermo attribute.

        Args:
            kinetics_flag (bool, optional): Whether this call is used for generating species statmech
                                            for a rate coefficient calculation.
            e0_only (bool, optional): Whether to only run statmech (w/o thermo) to compute E0.
        """
        if not kinetics_flag:
            # initialize the Arkane species_dict so that species for which thermo is calculated won't interfere
            # with species used for a rate coefficient calculation.
            arkane.input.species_dict = dict()
            if self.sp_level.to_arkane_level_of_theory(variant='AEC', raise_error=False, warn=False) is None:
                raise ValueError(f'Cannot compute thermo without a valid Arkane Level for AEC.')

        if self.species is None:
            raise InputError('Cannot not compute thermo without a species object.')

        arkane_output_path = self.generate_arkane_species_file(species=self.species,
                                                               bac_type=self.bac_type)

        if arkane_output_path is not None:
            try:
                arkane_species = arkane_input_species(self.species.label, self.species.arkane_file)
            except ValueError:
                arkane_species = arkane.input.species_dict[self.species.label]
            self.species.rmg_species = Species(molecule=[self.species.mol])
            self.species.rmg_species.reactive = True
            if self.species.mol_list:
                # add resonance structures for thermo determination
                arkane_species.molecule = self.species.mol_list
                self.species.rmg_species.molecule = self.species.mol_list
            statmech_success = self.run_statmech(arkane_species=arkane_species,
                                                 arkane_file_path=self.species.arkane_file,
                                                 arkane_output_path=arkane_output_path,
                                                 bac_type=self.bac_type,
                                                 sp_level=self.sp_level,
                                                 plot=False)
            if statmech_success:
                self.species.e0 = arkane_species.conformer.E0.value_si * 0.001  # convert to kJ/mol
                logger.debug(f'Assigned E0 to {self.species.label}: {self.species.e0:.2f} kJ/mol')
                if not e0_only:
                    thermo_job = ThermoJob(arkane_species, 'NASA')
                    thermo_job.execute(output_directory=arkane_output_path, plot=True)
                    self.species.thermo = arkane_species.get_thermo_data()
                    if not kinetics_flag:
                        plotter.log_thermo(self.species.label, path=arkane_output_path)
            else:
                logger.error(f'Could not run statmech job for species {self.species.label}')
        clean_output_directory(species_path=os.path.join(self.output_directory, 'Species', self.species.label))
예제 #9
0
    def from_dict(self, reaction_dict: 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(f'Cannot determine reactants and/or products labels for reaction {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(f'All species in a reaction must be labeled (and the labels must correspond '
                                 f'to respective Species in ARC). If an RMG Reaction object was passes, make '
                                 f'sure that all species in the reactants and products are correctly labeled. '
                                 f'Problematic reaction: {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()
        self.preserve_param_in_scan = reaction_dict['preserve_param_in_scan'] \
            if 'preserve_param_in_scan' in reaction_dict else None
        self._atom_map = reaction_dict['atom_map'] if 'atom_map' in reaction_dict else None
예제 #10
0
    def _send_command_to_server(
        self,
        command: Union[str, list],
        remote_path: str = '',
    ) -> Tuple[list, list]:
        """
        A wrapper for exec_command in paramiko.SSHClient. Send commands to the server. 

        Args:
            command (Union[str, list]): A string or an array of string commands to send.
            remote_path (Optional[str]): The directory path at which the command will be executed.

        Returns: Tuple[list, list]:
            - A list of lines of standard output stream.
            - A list of lines of the standard error stream.
        """
        if isinstance(command, list):
            command = '; '.join(command)
        if remote_path != '':
            # execute command in remote_path directory.
            # Check remote path existence, otherwise the cmd will be invalid
            # and even yield different behaviors.
            # Make sure to change directory back after the command is executed
            if self._check_dir_exists(remote_path):
                command = f'cd "{remote_path}"; {command}; cd '
            else:
                raise InputError(
                    f'Cannot execute command at given remote_path({remote_path})'
                )
        try:
            _, stdout, stderr = self._ssh.exec_command(command)
        except Exception as e:  # SSHException: Timeout opening channel.
            logger.debug(f'ssh timed-out in the first trial. Got: {e}')
            try:  # try again
                _, stdout, stderr = self._ssh.exec_command(command)
            except Exception as e:
                logger.debug(f'ssh timed-out after two trials. Got: {e}')
                return [
                    '',
                ], [
                    'ssh timed-out after two trials',
                ]
        stdout = stdout.readlines()
        stderr = stderr.readlines()
        return stdout, stderr
예제 #11
0
def main():
    """
    Delete ARC jobs according to the command line arguments specifications.
    """

    args = parse_command_line_arguments()

    if not args.all and not args.project and not args.job and not args.server:
        raise InputError(
            "Either a project (e,g,, '-p project_name'), a job (e.g., '-j a4563'), "
            "or a server (e,g,, '-s server_name'), or ALL (i.e., '-a')")

    server_list = args.server if args.server else [
        server for server in servers.keys()
    ]

    local_arc_path_ = local_arc_path if os.path.isdir(
        local_arc_path) else ARC_PATH
    csv_path = os.path.join(local_arc_path_, 'initiated_jobs.csv')

    project, jobs = None, list()
    if args.project:
        project = args.project
    elif args.job:
        with open(csv_path, 'r') as f:
            reader = csv.reader(f, dialect='excel')
            for row in reader:
                if args.job == row[0]:
                    project = row[1]

    if project is not None:
        with open(csv_path, 'r') as f:
            reader = csv.reader(f, dialect='excel')
            for row in reader:
                if row[1] == project:
                    jobs.append(row[8])

    if args.all:
        jobs = None

    for server in server_list:
        if server != 'local':
            delete_all_arc_jobs(server_list=server_list, jobs=jobs)
        else:
            delete_all_local_arc_jobs(jobs=jobs)
예제 #12
0
파일: parser.py 프로젝트: rvkmr1989/ARC
def process_conformers_file(
        conformers_path: str) -> Tuple[List[Dict[str, tuple]], List[float]]:
    """
    Parse coordinates and energies from an ARC conformers file of either species or TSs.

    Args:
        conformers_path (str): The path to an ARC conformers file
                               (either a "conformers_before_optimization" or
                               a "conformers_after_optimization" file).

    Raises:
        InputError: If the file could not be found.

    Returns: Tuple[List[Dict[str, tuple]], List[float]]
        Conformer coordinates in a dict format, the respective energies in kJ/mol.
    """
    if not os.path.isfile(conformers_path):
        raise InputError(
            'Conformers file {0} could not be found'.format(conformers_path))
    with open(conformers_path, 'r') as f:
        lines = f.readlines()
    xyzs, energies = list(), list()
    line_index = 0
    while line_index < len(lines):
        if 'conformer' in lines[line_index] and ':' in lines[
                line_index] and lines[line_index].strip()[-2].isdigit():
            xyz, energy = '', None
            line_index += 1
            while len(lines) and line_index < len(lines) and lines[line_index].strip() \
                    and 'SMILES' not in lines[line_index] \
                    and 'energy' not in lines[line_index].lower() \
                    and 'guess method' not in lines[line_index].lower():
                xyz += lines[line_index]
                line_index += 1
            while len(lines) and line_index < len(
                    lines) and 'conformer' not in lines[line_index]:
                if 'relative energy:' in lines[line_index].lower():
                    energy = float(lines[line_index].split()[2])
                line_index += 1
            xyzs.append(str_to_xyz(xyz))
            energies.append(energy)
        else:
            line_index += 1
    return xyzs, energies
예제 #13
0
파일: parser.py 프로젝트: rvkmr1989/ARC
def _get_lines_from_file(path: str) -> List[str]:
    """
    A helper function for getting a list of lines from a file.

    Args:
        path (str): The file path.

    Raises:
        InputError: If the file could not be read.

    Returns: List[str]
        Entries are lines from the file.
    """
    if os.path.isfile(path):
        with open(path, 'r') as f:
            lines = f.readlines()
    else:
        raise InputError(f'Could not find file {path}')
    return lines
예제 #14
0
파일: parser.py 프로젝트: rvkmr1989/ARC
def parse_zpe(path: str) -> Optional[float]:
    """
    Determine the calculated ZPE from a frequency output file

    Args:
        path (str): The path to a frequency calculation output file.

    Returns: Optional[float]
        The calculated zero point energy in kJ/mol.
    """
    if not os.path.isfile(path):
        raise InputError('Could not find file {0}'.format(path))
    log = ess_factory(fullpath=path)
    try:
        zpe = log.load_zero_point_energy() * 0.001  # convert to kJ/mol
    except (LogError, NotImplementedError):
        logger.warning('Could not read zpe from {0}'.format(path))
        zpe = None
    return zpe
예제 #15
0
파일: parser.py 프로젝트: rvkmr1989/ARC
def parse_t1(path: str) -> Optional[float]:
    """
    Parse the T1 parameter from a Molpro or Orca coupled cluster calculation.

    Args:
        path (str): The ess log file path.

    Returns: Optional[float]
        The T1 parameter.
    """
    if not os.path.isfile(path):
        raise InputError('Could not find file {0}'.format(path))
    log = ess_factory(fullpath=path)
    try:
        t1 = log.get_T1_diagnostic()
    except (LogError, NotImplementedError):
        logger.warning('Could not read t1 from {0}'.format(path))
        t1 = None
    return t1
예제 #16
0
def save_yaml_file(
    path: str,
    content: list or dict,
) -> None:
    """
    Save a YAML file (usually an input / restart file, but also conformers file)

    Args:
        path (str): The YAML file path to save.
        content (list, dict): The content to save.
    """
    if not isinstance(path, str):
        raise InputError(
            f'path must be a string, got {path} which is a {type(path)}')
    yaml.add_representer(str, string_representer)
    logger.debug('Creating a restart file...')
    content = yaml.dump(data=content)
    if '/' in path and os.path.dirname(path) and not os.path.exists(
            os.path.dirname(path)):
        os.makedirs(os.path.dirname(path))
    with open(path, 'w') as f:
        f.write(content)
예제 #17
0
파일: parser.py 프로젝트: rvkmr1989/ARC
def parse_e_elect(
    path: str,
    zpe_scale_factor: float = 1.,
) -> Optional[float]:
    """
    Parse the electronic energy from an sp job output file.

    Args:
        path (str): The ESS log file to parse from.
        zpe_scale_factor (float): The ZPE scaling factor, used only for composite methods in Gaussian via Arkane.

    Returns: Optional[float]
        The electronic energy in kJ/mol.
    """
    if not os.path.isfile(path):
        raise InputError(f'Could not find file {path}')
    log = ess_factory(fullpath=path)
    try:
        e_elect = log.load_energy(
            zpe_scale_factor) * 0.001  # convert to kJ/mol
    except (LogError, NotImplementedError):
        logger.warning(f'Could not read e_elect from {path}')
        e_elect = None
    return e_elect
예제 #18
0
def determine_ess(log_file: str) -> str:
    """
    Determine the ESS to which the log file belongs.

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

    Returns: str
        The ESS log class from Arkane.
    """
    log = ess_factory(log_file)
    if isinstance(log, GaussianLog):
        return 'gaussian'
    if isinstance(log, MolproLog):
        return 'molpro'
    if isinstance(log, OrcaLog):
        return 'orca'
    if isinstance(log, QChemLog):
        return 'qchem'
    if isinstance(log, TeraChemLog):
        return 'terachem'
    raise InputError(
        f'Could not identify the log file in {log_file} as belonging to '
        f'Gaussian, Molpro, Orca, QChem, or TeraChem.')
예제 #19
0
파일: parser.py 프로젝트: rvkmr1989/ARC
def parse_str_blocks(
    file_path: str,
    head_pat: Union[Match, str],
    tail_pat: Union[Match, str],
    regex: bool = True,
    tail_count: int = 1,
    block_count: int = 1,
) -> List[str]:
    """
    Return a list of blocks defined by the head pattern and the tail pattern.

    Args:
        file_path (str): The path to the readable file.
        head_pat (str/regex): Str pattern or regular expression of the head of the block.
        tail_pat (str/regex): Str pattern or regular expresion of the tail of the block.
        regex (bool, optional): Use regex (True) or str pattern (False) to search.
        tail_count (int, optional): The number of times that the tail repeats.
        block_count (int, optional): The max number of blocks to search. -1 for any number.

    Raises:
        InputError: If the file could not be found.

    Returns: List[str]
        List of str blocks.
    """
    if not os.path.isfile(file_path):
        raise InputError('Could not find file {0}'.format(file_path))
    with open(file_path, 'r') as f:
        blks = []
        # Different search mode
        if regex:

            def search(x, y):
                return re.search(x, y)
        else:

            def search(x, y):
                return x in y

        # 'search' for the head or 'read' until the tail
        mode = 'search'
        line = f.readline()
        while line != '':
            if mode == 'search':
                # Stop searching if found enough blocks
                if (len(blks)) == block_count:
                    break
                # Check if matching the head pattern
                else:
                    match = search(head_pat, line)
                    # Switch to 'read' mode
                    if match:
                        tail_repeat = 0
                        mode = 'read'
                        blks.append([])
                        blks[-1].append(line)
            elif mode == 'read':
                blks[-1].append(line)
                match = search(tail_pat, line)
                if match:
                    tail_repeat += 1
                    # If see enough tail patterns, switch to 'search' mode
                    if tail_repeat == tail_count:
                        mode = 'search'
            line = f.readline()
        # Remove the last incomplete search
        if len(blks) > 0 and (tail_repeat != tail_count):
            blks.pop()
        return blks
예제 #20
0
파일: rmgdb.py 프로젝트: rvkmr1989/ARC
def load_rmg_database(rmgdb,
                      thermo_libraries=None,
                      reaction_libraries=None,
                      kinetics_families='default',
                      load_thermo_libs=True,
                      load_kinetic_libs=True,
                      include_nist=False):
    """
    A helper function for loading the RMG database.

    Args:
        rmgdb (RMGDatabase): The RMG database instance.
        thermo_libraries (list, optional): Specific thermodynamic libraries to load (``None`` will load all).
        reaction_libraries (list, optional): Specific kinetics libraries to load (``None`` will load all).
        kinetics_families (list, str, optional): Specific kinetics families to load
                                                 (either a list or 'default', 'all', 'none')
        load_thermo_libs (bool, optional): Whether to load thermodynamic libraries, ``True`` to load.
        load_kinetic_libs (bool, optional): Whether to load kinetics libraries, ``True`` to load.
        include_nist (bool, optional): Whether to include the NIST kinetics libraries,
                                      ``True`` to include, default is ``False``
    """
    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):
        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(
                "kinetics_families 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):
            # Avoid reading .DS_store files for compatibility with Mac OS
            if not thermo_library_path.startswith('.'):
                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')
        # Avoid reading .DS_store files for compatibility with Mac OS
        reaction_libraries = [
            library for library in os.listdir(kinetics_path)
            if not library.startswith('.')
        ]
        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')) and library != 'Surface':
                indices_to_pop.append(i)
                # Avoid reading .DS_store files for compatibility with Mac OS (`if not second_level.startswith('.')`)
                second_level_libraries.extend([
                    library + '/' + second_level
                    for second_level in os.listdir(
                        os.path.join(kinetics_path, library))
                    if not second_level.startswith('.')
                ])
        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 or reaction_libraries[i] == 'Surface':
                reaction_libraries.pop(i)
        reaction_libraries.extend(second_level_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
    logger.info('\n\nLoading the RMG database...')

    kinetics_depositories = ['training', 'NIST'
                             ] if include_nist else ['training']
    rmgdb.load(
        path=db_path,
        thermo_libraries=thermo_libraries,
        transport_libraries=['PrimaryTransportLibrary', 'NOx2018', 'GRI-Mech'],
        reaction_libraries=reaction_libraries,
        seed_mechanisms=list(),
        kinetics_families=kinetics_families,
        kinetics_depositories=kinetics_depositories,
        depository=False,
    )
    for family in rmgdb.kinetics.families.values():
        try:
            family.add_rules_from_training(thermo_database=rmgdb.thermo)
        except KineticsError:
            logger.info('Could not train family {0}'.format(family))
        else:
            family.fill_rules_by_averaging_up(verbose=False)
    logger.info('\n\n')
예제 #21
0
def initialize_job_types(
    job_types: dict,
    specific_job_type: str = '',
) -> dict:
    """
    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.
        specific_job_type (str): Specific job type to execute. Legal strings are job types (keys of job_types dict).

    Returns: dict
        An updated (comprehensive) job type dictionary.
    """
    if specific_job_type:
        logger.info(
            f'Specific_job_type {specific_job_type} was requested by the user.'
        )
        if job_types:
            logger.warning(
                'Both job_types and specific_job_type were given, use only specific_job_type to '
                'populate the job_types dictionary.')
        job_types = {job_type: False for job_type in default_job_types.keys()}
        try:
            job_types[specific_job_type] = True
        except KeyError:
            raise InputError(
                f'Specified job type {specific_job_type} is not supported.')

    if specific_job_type == 'bde':
        bde_default = {
            'opt': True,
            'fine_grid': True,
            'freq': True,
            'sp': True
        }
        job_types.update(bde_default)

    defaults_to_true = [
        'conformers', 'fine', 'freq', 'irc', 'opt', 'rotors', 'sp'
    ]
    defaults_to_false = ['bde', 'onedmin', 'orbitals']
    if job_types is None:
        job_types = default_job_types
        logger.info("Job types were not specified, using ARC's defaults")
    else:
        logger.debug(f'the following job types were specified: {job_types}.')
    if 'lennard_jones' in job_types:
        job_types['onedmin'] = job_types['lennard_jones']
        del job_types['lennard_jones']
    if 'fine_grid' in job_types:
        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:
            if job_type == '1d_rotors':
                logging.error(
                    "Note: The `1d_rotors` job type was renamed to simply `rotors`. "
                    "Please modify your input accordingly (see ARC's documentation for examples)."
                )
            raise InputError(
                f"Job type '{job_type}' is not supported. Check the job types dictionary "
                "(either in ARC's input or in default_job_types under settings)."
            )
    job_types_report = [job_type for job_type, val in job_types.items() if val]
    logger.info(f'\nConsidering the following job types: {job_types_report}\n')
    return job_types