Exemplo n.º 1
0
    def description_text(self, P=None):
        """Create the text description of what this step will do.
        The dictionary of control values is passed in as P so that
        the code can test values, etc.

        Parameters
        ----------
        P: dict
            An optional dictionary of the current values of the control
            parameters.

        Returns
        -------
        str
            A description of the current step.
        """
        if not P:
            P = self.parameters.values_to_dict()

        script = P['script'].splitlines()

        if len(script) > 10:
            text = '\n'.join(script[0:9])
            text += '\n...'
        else:
            text = '\n'.join(script)

        return (self.header + '\n' +
                __(text, indent=4 * ' ', wrap=False).__str__())
Exemplo n.º 2
0
    def description_text(self, P=None):
        """Return a short description of this step.

        Return a nicely formatted string describing what this step will
        do.

        Keyword arguments:
            P: a dictionary of parameter values, which may be variables
                or final values. If None, then the parameters values will
                be used as is.
        """

        if not P:
            P = self.parameters.values_to_dict()

        text = 'Solvate the system'
        if P['method'][0] == '$':
            text += (
                '. Depending on {method} either make it a periodic box or '
                'spherical droplet of')
        elif P['method'] == 'making periodic':
            text += ' making it a periodic system filled with'
        elif P['method'] == 'within a sphere of solvent':
            text += ' within a spherical droplet of'
        else:
            raise RuntimeError("Don't recognize the method {}".format(
                P['method']))

        if P['solvent'] == 'water':
            text += ' water, using the {water_model} model.'
        else:
            text += ' molecules of {solvent}.'

        if P['submethod'][0] == '$':
            text += ' It will be created according to {submethod}.'
        elif P['submethod'] == (
                "fixing the volume and adding a given number of molecules"):
            text += (' The cell volume is fixed, with {number of molecules} '
                     'solvent molecules added.')
        elif P['submethod'] == ("fixing the volume and filling to a density"):
            text += (
                ' The cell volume is fixed and will be filled to a density '
                'of {density}.')
        elif P['submethod'] == (
                "with the density and number of molecules of solvent"):
            text += (' The density is {density} with {number of molecules} '
                     'solvent molecules.')
        elif P['submethod'] == (
                "with the density and approximate number of atoms of solvent"):
            text += (
                ' The density is {density} and solvent molecules with a total '
                'of approximately {approximate number of atoms} will be used.')
        else:
            raise RuntimeError("Don't recognize the submethod {}".format(
                P['submethod']))

        return self.header + '\n' + __(text, **P, indent=4 * ' ').__str__()
Exemplo n.º 3
0
    def run(self):
        """Run a System step.

        Parameters
        ----------
        None

        Returns
        -------
        seamm.Node
            The next node object in the flowchart.
        """
        next_node = super().run(printer)
        # Get the values of the parameters, dereferencing any variables
        P = self.parameters.current_values_to_dict(
            context=seamm.flowchart_variables._data)

        # Print what we are doing
        printer.important(__(self.description_text(P), indent=self.indent))

        # Temporary code just to print the parameters. You will need to change
        # this!
        for key in P:
            print('{:>15s} = {}'.format(key, P[key]))
            printer.normal(
                __('{key:>15s} = {value}',
                   key=key,
                   value=P[key],
                   indent=4 * ' ',
                   wrap=False,
                   dedent=False))

        # Analyze the results
        self.analyze()

        # Add other citations here or in the appropriate place in the code.
        # Add the bibtex to data/references.bib, and add a self.reference.cite
        # similar to the above to actually add the citation to the references.

        return next_node
Exemplo n.º 4
0
    def analyze(self, indent='', **kwargs):
        """Do any analysis needed for this step, and print important results
        to the local step.out file using 'printer'
        """

        printer.normal(
            __(
                'This is a placeholder for the results form step '
                'Cassandra',
                indent=4 * ' ',
                wrap=True,
                dedent=False
            )
        )
Exemplo n.º 5
0
    def description_text(self, P=None):
        """Create the text description of what this step will do.
        The dictionary of control values is passed in as P so that
        the code can test values, etc.

        Parameters
        ----------
        P: dict
            An optional dictionary of the current values of the control
            parameters.

        Returns
        -------
        str
            A description of the current step.
        """
        if not P:
            P = self.parameters.values_to_dict()

        method = P['method']

        if method == 'density':
            density = P['density']
            if self.is_expr(density):
                text = ("The cell will be adjusted isotropically to a density "
                        f"given by '{density}'.")
            else:
                text = ("The cell will be adjusted isotropically to a density "
                        f"of {density}.")
        elif method == 'volume':
            volume = P['volume']
            if self.is_expr(volume):
                text = ("The cell will be adjusted isotropically to a volume "
                        f"given by '{volume}'.")
            else:
                text = ("The cell will be adjusted isotropically to a volume "
                        f"of {volume}.")
        elif method == 'cell parameters':
            text = "The cell parameters will be set as follows:"
            for parameter in ('a', 'b', 'c', 'alpha', 'beta', 'gamma'):
                text += f"  {parameter:>5}: {P[parameter]}"
        elif method == 'uniform contraction/expansion':
            text = (
                f"The cell will be adjusted isotropically by {P['expansion']} "
                "in each direction.")
        else:
            raise RuntimeError(f"Don't recognize method '{method}'!")

        return self.header + '\n' + __(text, **P, indent=4 * ' ').__str__()
Exemplo n.º 6
0
    def analyze(self, indent='', **kwargs):
        """Do any analysis of the output from this step.

        Also print important results to the local step.out file using
        'printer'.

        Parameters
        ----------
        indent: str
            An extra indentation for the output
        """
        printer.normal(
            __('This is a placeholder for the results from the '
               'System step',
               indent=4 * ' ',
               wrap=True,
               dedent=False))
Exemplo n.º 7
0
    def describe(self, indent='', json_dict=None):
        """Write out information about what this step will do
        If json_dict is passed in, add information to that dictionary
        so that it can be written out by the controller as appropriate.
        """

        # Call superclasses which will print some information
        next_node = super().describe()

        # Local copies of variables in a dictionary
        P = self.parameters.values_to_dict()

        text = self.description_text(P)

        job.job(__(text, indent=self.indent + '    ', **P))

        return next_node
Exemplo n.º 8
0
    def description_text(self, P=None):
        """Create the text description of what this step will do.
        The dictionary of control values is passed in as P so that
        the code can test values, etc.

        Parameters
        ----------
        P: dict
            An optional dictionary of the current values of the control
            parameters.
        Returns
        -------
        str
            A description of the current step.
        """
        if not P:
            P = self.parameters.values_to_dict()

        text = ('Please replace this with a short summary of the '
                'System step, including key parameters.')

        return self.header + '\n' + __(text, **P, indent=4 * ' ').__str__()
Exemplo n.º 9
0
    def analyze(self, indent='', data={}):
        """Parse the output and generating the text output and store the
        data in variables for other stages to access
        """

        printer.normal(self._long_header)

        # The results
        printer.normal(
            __(('\nThe geometry converged in {NUMBER_SCF_CYCLES} '
                'iterations to a heat of formation of {HEAT_OF_FORMATION} '
                'kcal/mol.'),
               **data,
               indent=7 * ' '))

        # Put any requested results into variables or tables
        self.store_results(
            data=data,
            properties=mopac_step.properties,
            results=self.parameters['results'].value,
            create_tables=self.parameters['create tables'].get())

        printer.normal('\n')
Exemplo n.º 10
0
    def run(self):
        """Run a Cassandra step.
        """
        # Get the values of the parameters, dereferencing any variables
        P = self.parameters.current_values_to_dict(
            context=seamm.flowchart_variables._data
        )

        # Temporary code just to print the parameters. You will need to change
        # this!
        for key in P:
            print('{:>15s} = {}'.format(key, P[key]))
            printer.normal(
                __(
                    '{key:>15s} = {value}',
                    key=key,
                    value=P[key],
                    indent=4 * ' ',
                    wrap=False,
                    dedent=False
                )
            )

        return super().run()
Exemplo n.º 11
0
    def run(self):
        """Run a Read Structure step."""
        next_node = super().run(printer)

        # Get the values of the parameters, dereferencing any variables
        P = self.parameters.current_values_to_dict(
            context=seamm.flowchart_variables._data)

        # What type of file?
        if isinstance(P["file"], Path):
            filename = str(P["file"])
        else:
            filename = ["file"].strip()
        file_type = P["file type"]

        if file_type != "from extension":
            extension = file_type.split()[0]
        else:
            path = PurePath(filename)
            extension = path.suffix
            if extension == ".gz":
                extension = path.stem.suffix

        if extension == "":
            extension = guess_extension(filename, use_file_name=False)
            P["file type"] = extension

        # Print what we are doing
        printer.important(self.description_text(P))

        # Read the file into the system
        system_db = self.get_variable("_system_db")
        system, configuration = self.get_system_configuration(
            P, structure_handling=True)

        read(
            filename,
            configuration,
            extension=extension,
            add_hydrogens=P["add hydrogens"],
            system_db=system_db,
            system=system,
            indices=P["indices"],
            subsequent_as_configurations=(P["subsequent structure handling"] ==
                                          "Create a new configuration"),
            system_name=P["system name"],
            configuration_name=P["configuration name"],
            printer=printer.important,
            references=self.references,
            bibliography=self._bibliography,
        )

        # Finish the output
        if configuration.periodicity == 3:
            space_group = configuration.symmetry.group
            if space_group == "":
                symmetry_info = ""
            else:
                symmetry_info = f" The space group is {space_group}."
            printer.important(
                __(
                    f"\n    Created a periodic structure with {configuration.n_atoms} "
                    f"atoms.{symmetry_info}"
                    f"\n           System name = {system.name}"
                    f"\n    Configuration name = {configuration.name}",
                    indent=4 * " ",
                ))
        else:
            printer.important(
                __(
                    f"\n    Created a molecular structure with {configuration.n_atoms} "
                    "atoms."
                    f"\n           System name = {system.name}"
                    f"\n    Configuration name = {configuration.name}",
                    indent=4 * " ",
                ))
        printer.important("")

        return next_node
Exemplo n.º 12
0
    def run(self):
        """Run a Write Structure step."""
        next_node = super().run(printer)

        # Get the values of the parameters, dereferencing any variables
        P = self.parameters.current_values_to_dict(
            context=seamm.flowchart_variables._data)

        # What type of file?
        filename = P["file"].strip()
        file_type = P["file type"]

        if file_type != "from extension":
            extension = file_type.split()[0]
        else:
            path = PurePath(filename)
            extension = path.suffix
            if extension == ".gz":
                extension = path.stem.suffix

        if extension == "":
            extension = guess_extension(filename, use_file_name=False)
            P["file type"] = extension

        # Print what we are doing
        printer.important(__(self.description_text(P), indent=4 * " "))

        # Write the file into the system
        system_db = self.get_variable("_system_db")
        system, configuration = self.get_system_configuration(P)

        structures = P["structures"]
        if structures == "current configuration":
            configurations = [configuration]
        elif structures == "all configurations of current system":
            for configuration in system.configurations():
                configurations.append(configuration)
        elif structures == "all systems":
            configurations = []
            for system in system_db.systems():
                for configuration in system.configurations():
                    configurations.append(configuration)

        write(
            filename,
            configurations,
            extension=extension,
            remove_hydrogens=P["remove hydrogens"],
            printer=printer.important,
            references=self.references,
            bibliography=self._bibliography,
        )

        # Finish the output
        printer.important(
            __(
                f"\n    Wrote the structure with {configuration.n_atoms} "
                "atoms."
                f"\n           System name = {system.name}"
                f"\n    Configuration name = {configuration.name}",
                indent=4 * " ",
            ))
        printer.important("")

        return next_node
Exemplo n.º 13
0
    def run(self):
        """Solvate the system usiing PACKMOL

        To run PACKMOL we need to specify either the size of the cell for
        periodic systems or the radius of the sphere for molecules, along
        with the number of solvent molecules to use.

        Since PACKMOL does not really handle periodic systems, we will
        pack the solvent in a smaller box that fits in the actual periodic
        cell to avoid overlaps at the cell boundaries.
        """

        next_node = super().run(printer)

        # The options from command line, config file ...
        o = self.options

        packmol_exe = os.path.join(o.packmol_path, 'packmol')

        seamm_util.check_executable(packmol_exe,
                                    key='--packmol-path',
                                    parser=self.parser)

        P = self.parameters.current_values_to_dict(
            context=seamm.flowchart_variables._data)

        method = P['method']
        submethod = P['submethod']
        logger.debug('   method = {}'.format(method))
        logger.debug('submethod = {}'.format(submethod))

        # Print what we are doing
        printer.important(__(self.description_text(P), indent=self.indent))

        # Check that there is a system and get its mass, etc.
        if data.structure is None:
            logger.error('Solvate: there is no system!')
            raise RuntimeError('Solvate: there is no system to solvate!')
        else:
            system = data.structure
            atoms = system['atoms']
            # Get the molecular weight
            elements = atoms['elements']
            mass = 0.0
            for element in elements:
                mass += mendeleev.element(element).mass
            system_mass = mass * ureg.g / ureg.mol
            system_mass.ito('kg')  # Mass per 'system', in kg
            # And center of box containing the molecule
            minx, miny, minz = atoms['coordinates'][0]
            maxx, maxy, maxz = atoms['coordinates'][0]
            for x, y, z in atoms['coordinates']:
                minx = x if x < minx else minx
                maxx = x if x > maxx else maxx
                miny = y if y < miny else miny
                maxy = y if y > maxy else maxy
                minz = z if z < minz else minz
                maxz = z if z > maxz else maxz
            cx = (minx + maxx) / 2
            cy = (miny + maxy) / 2
            cz = (minz + maxz) / 2

        # And the solvent molecule(s)
        if P['solvent'] == 'water':
            model = Water.create_model(P['water_model'])
            solvent_mass = model.mass * ureg.g / ureg.mol
            solvent_mass.ito('kg')  # Mass per solvent molecule, in kg
            n_solvent_atoms = 3
            solvent_pdb = model.pdb()
            solvent_system = model.system()
        else:
            raise NotImplementedError(
                'Solvents other than water not available yet.')

        if method == "within a sphere of solvent":
            # Nonperiodic case
            raise NotImplementedError(
                'Solvating with a sphere of solvent not supported yet.')
        else:
            # Periodic case
            if submethod == (
                    "fixing the volume and adding a given number of molecules"
            ):
                if system['periodicity'] != 3:
                    raise RuntimeError('Solvate: the system is not periodic.')
                a, b, c, alpha, beta, gamma = system['cell']
                if alpha != 90 or beta != 90 or gamma != 90:
                    raise NotImplementedError(
                        'Solvate cannot handle non-orthorhombic cells yet')
                lx = a
                ly = b
                lz = c
                n_molecules = P['number of molecules']
            elif submethod == ("fixing the volume and filling to a density"):
                if system['periodicity'] != 3:
                    raise RuntimeError('Solvate: the system is not periodic.')
                a, b, c, alpha, beta, gamma = system['cell']
                if alpha != 90 or beta != 90 or gamma != 90:
                    raise NotImplementedError(
                        'Solvate cannot handle non-orthorhombic cells yet')
                lx = a
                ly = b
                lz = c

                density = P['density']
                volume = a * b * c * math.pow(ureg.m / 1.0e10, 3)
                target_mass = density * volume
                target_mass.ito('kg')
                n_molecules = int(
                    round((target_mass - system_mass) / solvent_mass))
            elif submethod == (
                    "with the density and number of molecules of solvent"):
                density = P['density']
                n_molecules = P['number of molecules']

                mass = system_mass + n_molecules * solvent_mass
                volume = mass / density

                lx = math.pow(volume.to('Å^3').magnitude, 1 / 3)
                ly = lx
                lz = lx

                system['periodicity'] = 3
                system['cell'] = [lx, ly, lz, 90.0, 90.0, 90.0]
            elif submethod == (
                    "with the density and approximate number of atoms of solvent"
            ):
                density = P['density']
                n_molecules = int(
                    round(P['approximate number of atoms'] / n_solvent_atoms))

                mass = system_mass + n_molecules * solvent_mass
                volume = mass / density

                lx = math.pow(volume.to('Å^3').magnitude, 1 / 3)
                ly = lx
                lz = lx

                system['periodicity'] = 3
                system['cell'] = [lx, ly, lz, 90.0, 90.0, 90.0]
            else:
                raise RuntimeError(
                    "Don't recognize the submethod {}".format(submethod))

        # gap = P['gap'].to('Å').magnitude
        gap = 2.0

        lxp = lx - gap / 2
        lyp = ly - gap / 2
        lzp = lz - gap / 2

        dx = (lx / 2) - cx
        dy = (ly / 2) - cy
        dz = (lz / 2) - cz

        lines = []
        lines.append('tolerance {}'.format(gap))
        lines.append('output solvate.pdb')
        lines.append('filetype pdb')
        lines.append('structure system.pdb')
        lines.append('  number 1')
        lines.append('  fixed {} {} {} 0.0 0.0 0.0'.format(dx, dy, dz))
        lines.append('end structure')
        lines.append('structure solvent.pdb')
        lines.append('  number {}'.format(n_molecules))
        lines.append('  inside box {l0} {l0} {l0} {} {} {}'.format(lxp,
                                                                   lyp,
                                                                   lzp,
                                                                   l0=gap / 2))
        lines.append('end structure')
        lines.append('')

        files = {
            'system.pdb': pdbfile.from_molssi(data.structure),
            'solvent.pdb': solvent_pdb,
            'input.inp': '\n'.join(lines)
        }

        for key, value in files.items():
            logger.debug(80 * '*')
            logger.debug('File: ' + key)
            logger.debug(80 * '*')
            logger.debug('\n' + value + '\n\n')

        local = seamm.ExecLocal()
        result = local.run(cmd=(packmol_exe + ' < input.inp'),
                           shell=True,
                           files=files,
                           return_files=['solvate.pdb'])

        tmp_files = []
        for key, value in result.items():
            if key == 'files':
                tmp_files = value
            if key in tmp_files:
                logger.debug(80 * '*')
                logger.debug('File: ' + key)
                if value['exception'] is not None:
                    logger.debug('   Exception = ' + value['exception'])
                logger.debug(80 * '*')
                logger.debug('\n' + value['data'] + '\n\n')
            elif len(str(value)) < 100:
                logger.debug(key + ': ' + str(value))
            else:
                logger.debug(80 * '*')
                logger.debug('Result: ' + key)
                logger.debug(80 * '*')
                logger.debug('\n' + str(value) + '\n\n')

        # Parse the resulting PDB file
        new_structure = pdbfile.to_molssi(result['solvate.pdb']['data'])

        logger.debug('Coordinates for the solvated system:')
        for x, y, z in new_structure['atoms']['coordinates']:
            logger.debug('{:9.4f} {:9.4f} {:9.4f}'.format(x, y, z))

        # Packmol just gives back atoms and coordinates, so we need to
        # build out the system with added solvent molecules and then
        # replace the coordinates with the new coordinates

        solvent_atoms = solvent_system['atoms']
        for molecule_id in range(n_molecules):
            first_atom = len(atoms['elements'])
            for key in atoms:
                if key in solvent_atoms:
                    if key == 'atom_types' or key == 'charges':
                        types = atoms[key]
                        solvent_types = solvent_atoms[key]
                        for type_ in types:
                            if type_ in solvent_types:
                                types[type_].extend(solvent_types[type_])
                            elif '*' in solvent_types:
                                types[type_].extend(solvent_types['*'])
                    else:
                        atoms[key].extend(solvent_atoms[key])
                else:
                    logger.warning(key + 'not in solvent_system.atoms')
                    for i in range(n_solvent_atoms):
                        atoms[key].append('')
            for i, j, order in solvent_system['bonds']:
                system['bonds'].append([first_atom + i, first_atom + j, order])

        # And the new coordinates
        atoms['coordinates'] = new_structure['atoms']['coordinates']

        string = 'Solvated the system with {nmol} solvent molecules.'
        if system['periodicity'] == 3:
            string += ' The periodic cell is {lx:.2f}x{ly:.2f}x{lz:.2f}'
            string += ' with a density of {density:.5~P}.'
        printer.important(
            __(string,
               indent='    ',
               lx=lx,
               ly=ly,
               lz=lz,
               nmol=n_molecules,
               density=density))
        printer.important('')

        logger.log(
            0, 'Structure created by Packmol:\n\n' +
            pprint.pformat(data.structure))

        return next_node
Exemplo n.º 14
0
    def run(self):
        """Run a Set Cell step.

        Parameters
        ----------
        None

        Returns
        -------
        seamm.Node
            The next node object in the flowchart.
        """
        next_node = super().run(printer)
        # Get the values of the parameters, dereferencing any variables
        P = self.parameters.current_values_to_dict(
            context=seamm.flowchart_variables._data)

        # Print what we are doing -- getting formatted values for printing
        PP = self.parameters.current_values_to_dict(
            context=seamm.flowchart_variables._data,
            formatted=True,
            units=False)
        self.logger.debug(f'Formatted values:\n{pprint.pformat(PP)}')
        printer.normal(__(self.description_text(PP), indent=self.indent))

        method = P['method']
        system_db = self.get_variable('_system_db')
        configuration = system_db.system.configuration

        cell = configuration.cell
        a, b, c, alpha, beta, gamma = cell.parameters
        if method == 'density':
            rho = P['density'].to('g/mL').magnitude
            rho0 = configuration.density
            delta = (rho0 / rho)**(1 / 3)
            a = a * delta
            b = b * delta
            c = c * delta
        elif method == 'volume':
            V = P['volume'].to('Å^3').magnitude
            V0 = configuration.volume
            delta = (V / V0)**(1 / 3)
            a = a * delta
            b = b * delta
            c = c * delta
        elif method == 'cell parameters':
            a = P['a'].to('Å').magnitude
            b = P['b'].to('Å').magnitude
            c = P['c'].to('Å').magnitude
            alpha = P['alpha'].to('degree').magnitude
            beta = P['beta'].to('degree').magnitude
            gamma = P['gamma'].to('degree').magnitude
        elif method == 'uniform contraction/expansion':
            delta = (1 + P['expansion'])**(1 / 3)
            a = a * delta
            b = b * delta
            c = c * delta
        else:
            raise RuntimeError(f"Don't recognize method '{method}'!")

        configuration.cell.parameters = (a, b, c, alpha, beta, gamma)

        text = '\nAdjusted the cell:\n'
        text += f'         a: {a:8.3f}\n'
        text += f'         b: {b:8.3f}\n'
        text += f'         c: {c:8.3f}\n'
        text += f'     alpha: {alpha:7.2f}\n'
        text += f'      beta: {beta:7.2f}\n'
        text += f'     gamma: {gamma:7.2f}\n'
        text += '\n'
        text += f'    volume: {configuration.volume:10.1f} Å^3\n'
        text += f'   density: {configuration.density:11.2f} g/mL\n'

        printer.normal(__(text, indent=self.indent + 4 * ' '))

        printer.normal('')

        # Add other citations here or in the appropriate place in the code.
        # Add the bibtex to data/references.bib, and add a self.reference.cite
        # similar to the above to actually add the citation to the references.

        return next_node
    def assign_parameters(self, configuration=None):

        if configuration is None:
            raise TypeError("A configuration must be provided when assigning forcefield parameters")

        printer.important(
            __(
                "Assigning the atom types and charges for forcefield "
                f"'{self.selected_forcefield}' to the system",
            )
        )

        logger.debug('Atom typing, getting the SMILES for the system')
        smiles = configuration.to_smiles(hydrogens=True)
        logger.debug('Atom typing -- smiles = ' + smiles)


        pat3 = re.compile(r'H3\]')
        pat2 = re.compile(r'H2\]')
        pat1 = re.compile(r'(?P<c1>[^[])H\]')

        smiles = pat3.sub(']([H])([H])([H])', smiles)
        smiles = pat2.sub(']([H])([H])', smiles)
        smiles = pat1.sub(r'\g<c1>]([H])', smiles)

        h_subst = None
        for el in ('Rb', 'Cs', 'Fr', 'At'):
            if el not in smiles:
                h_subst = el
                pat4 = re.compile(r'\[H\]')
                smiles = pat4.sub('[{}]'.format(el), smiles)
                logger.debug("Subst SMILES = '{}'".format(smiles))
                break

        molecule = rdkit.Chem.MolFromSmiles(smiles)
        if molecule is None:
            print("There was problem with the SMILES '{}'".format(smiles))
            return

        if h_subst is not None:
            for atom in molecule.GetAtoms():
                if atom.GetSymbol() == h_subst:
                    atom.SetAtomicNum(1)

        # if add_hydrogens:
        #     molecule = rdkit.Chem.AddHs(molecule)
        #     n_atoms = molecule.GetNumAtoms()
        #     logger.debug(
        #         "'{}' has {} atoms with hydrogens added".format(
        #             smiles, n_atoms
        #         )
        #     )
        # else:
        #     n_atoms = molecule.GetNumAtoms()
        #     logger.debug("'{}' has {} atoms".format(smiles, n_atoms))
        n_atoms = molecule.GetNumAtoms()

        atomtypes = ['?'] * n_atoms
        templates = self.forcefield.get_templates()
        for atom_type in templates:
            template = templates[atom_type]
            for smarts in template['smarts']:
                pattern = rdkit.Chem.MolFromSmarts(smarts)

                ind_map = {}
                for atom in pattern.GetAtoms():
                    map_num = atom.GetAtomMapNum()
                    if map_num:
                        ind_map[map_num - 1] = atom.GetIdx()
                map_list = [ind_map[x] for x in sorted(ind_map)]

                matches = molecule.GetSubstructMatches(pattern)
                logger.debug(atom_type + ': ')
                if len(matches) > 0:
                    for match in matches:
                        atom_ids = [match[x] for x in map_list]
                        for x in atom_ids:
                            atomtypes[x] = atom_type
                        tmp = [str(x) for x in atom_ids]
                        logger.debug('\t' + ', '.join(tmp))

        i = 0
        untyped = []
        for atom, atom_type in zip(molecule.GetAtoms(), atomtypes):
            if atom_type == '?':
                untyped.append(i)
            logger.debug("{}: {}".format(atom.GetSymbol(), atom_type))
            i += 1

        if len(untyped) > 0:
            logger.warning(
                'The forcefield does not have atom types for'
                ' the molecule!. See missing_atomtypes.png'
                ' for more detail.'
            )
            #rdkit.Chem.AllChem.Compute2DCoords(molecule)
            #img = rdkit.Chem.Draw.MolToImage(
            #    molecule,
            #    size=(1000, 1000),
            #    highlightAtoms=untyped,
            #    highlightColor=(0, 1, 0)
            #)
            #img.save('missing_atomtypes.png')

            #if self.have_tk:
            #    root = tk.Tk()
            #    root.title('Atom types')
            #    tkPI = ImageTk.PhotoImage(img)
            #    tkLabel = tk.Label(root, image=tkPI)
            #    tkLabel.place(x=0, y=0, width=img.size[0], height=img.size[1])
            #    root.geometry('%dx%d' % (img.size))
            #    root.mainloop()
        else:
            logger.info('The molecule was successfully atom-typed')


        logger.info('Atom types: ' + ', '.join(atomtypes))
        key = f'atomtypes_{self.selected_forcefield}'
        if key not in configuration.atoms:
            configuration.atoms.add_attribute(key, coltype='str')
        configuration.atoms[key] = atomtypes

        # Now get the charges if forcefield has them.
        terms = self.forcefield.data['forcefield'][self.selected_forcefield]['parameters']

        if 'bond_increments' in terms:
            logger.debug('Getting the charges for the system')
            neighbors = configuration.bonded_neighbors(as_indices=True)

            charges = []
            total_q = 0.0
            for i in range(configuration.n_atoms):
                itype = atomtypes[i]
                parameters = self.forcefield.charges(itype)[3]
                q = float(parameters['Q'])
                for j in neighbors[i]:
                    jtype = atomtypes[j]
                    parameters = self.forcefield.bond_increments(itype, jtype)[3]
                    q += float(parameters['deltaij'])
                charges.append(q)
                total_q += q
            if abs(total_q) > 0.0001:
                logger.warning('Total charge is not zero: {}'.format(total_q))
                logger.info(
                    'Charges from increments and charges:\n' +
                    pprint.pformat(charges)
                )
            else:
                logger.debug(
                    'Charges from increments:\n' + pprint.pformat(charges)
                )

            key = f'charges_{self.selected_forcefield}'
            if key not in configuration.atoms:
                configuration.atoms.add_attribute(key, coltype='float')
            charge_column = configuration.atoms.get_column(key)
            charge_column[0:] = charges
            logger.debug(f"Set column '{key}' to the charges")

            printer.important(
                __(
                    "Assigned atom types and charges to "
                    f"{configuration.n_atoms} atoms.",
                )
            )
        else:
            printer.important(
                __(
                    f"Assigned atom types to {configuration.n_atoms} "
                    "atoms.",
                )
            )