Esempio n. 1
0
    print(e)
    assert False, 'Castep calculator module could not be loaded'

try:
    __import__(ase_castep_dir + ".io.castep")
except Exception, e:
    assert False, 'Castep io module could not be loaded'


tmp_dir = tempfile.mkdtemp()
cwd = os.getcwd()

from ase.calculators.castep import Castep

try:
    c = Castep(directory=tmp_dir, label='test_label')
except Exception, e:
    traceback.print_exc()
    print(e)
    assert False, 'Could not instantiate castep calculator'


try:
    c.xc_functional = 'PBE'
except Exception, e:
    traceback.print_exc()
    print(e)
    assert False, 'Setting xc_functional  failed'

import ase.lattice.cubic
lattice = ase.lattice.cubic.BodyCenteredCubic('Li' )
Esempio n. 2
0
"""Simple shallow test of the CASTEP interface"""
import os
import shutil
import tempfile

import ase.lattice.cubic
from ase.calculators.castep import (Castep, CastepParam,
                                    create_castep_keywords,
                                    import_castep_keywords)

tmp_dir = tempfile.mkdtemp()
cwd = os.getcwd()

c = Castep(directory=tmp_dir, label='test_label')
c.xc_functional = 'PBE'

lattice = ase.lattice.cubic.BodyCenteredCubic('Li')

print('For the sake of evaluating this test, warnings')
print('about auto-generating pseudo-potentials are')
print('normal behavior and can be safely ignored')

lattice.set_calculator(c)

create_castep_keywords(
    castep_command=os.environ['CASTEP_COMMAND'],
    path=tmp_dir,
    fetch_only=20)

param_fn = os.path.join(tmp_dir, 'myParam.param')
param = open(param_fn, 'w')
Esempio n. 3
0
def read_castep_cell(filename, _=None):
    """Read a .cell file and return an atoms object.
    Any value found that does not fit the atoms API
    will be stored in the atoms.calc attribute.
    """

    from ase.calculators.castep import Castep
    calc = Castep()

    fileobj = open(filename)
    lines = fileobj.readlines()
    fileobj.close()

    def get_tokens(lines, l):
        """Tokenizes one line of a *cell file."""
        comment_chars = "#!"
        while l < len(lines):
            line = lines[l].strip()
            if len(line) == 0:
                l += 1
                continue
            elif any([line.startswith(comment_char)
                      for comment_char in comment_chars]):
                l += 1
                continue
            else:
                for c in comment_chars:
                    if c in line:
                        icomment = min(line.index(c))
                    else:
                        icomment = len(line)
                tokens = line[:icomment].split()
                return tokens, l + 1
        tokens = ""
        print("read_cell: Warning - get_tokens has not found any more tokens")
        return tokens, l

    lat = []
    have_lat = False

    pos = []
    spec = []
    constraints = []
    raw_constraints = {}
    have_pos = False
    pos_frac = False

    l = 0
    while l < len(lines):
        tokens, l = get_tokens(lines, l)
        if not tokens:
            continue
        elif tokens[0].upper() == "%BLOCK":
            if tokens[1].upper() == "LATTICE_CART" and not have_lat:
                tokens, l = get_tokens(lines, l)
                if len(tokens) == 1:
                    print('read_cell: Warning - ignoring unit specifier in')
                    print('%BLOCK LATTICE_CART (assuming Angstrom instead)')
                    tokens, l = get_tokens(lines, l)
                for _ in range(3):
                    lat_vec = [float(a) for a in tokens[0:3]]
                    lat.append(lat_vec)
                    tokens, l = get_tokens(lines, l)
                if tokens[0].upper() != "%ENDBLOCK":
                    print('read_cell: Warning - ignoring more than three')
                    print('lattice vectors in invalid %BLOCK LATTICE_CART')
                    print('%s ...' % tokens[0].upper())
                have_lat = True

            elif tokens[1].upper() == "LATTICE_ABC" and not have_lat:
                tokens, l = get_tokens(lines, l)
                if len(tokens) == 1:
                    print('read_cell: Warning - ignoring unit specifier in')
                    print('%BLOCK LATTICE_ABC (assuming Angstrom instead)')
                    tokens, l = get_tokens(lines, l)
                a, b, c = map(float, tokens[0:3])
                tokens, l = get_tokens(lines, l)
                alpha, beta, gamma = [radians(float(phi)) for phi in tokens[0:3]]
                tokens, l = get_tokens(lines, l)
                if tokens[0].upper() != "%ENDBLOCK":
                    print('read_cell: Warning - ignoring additional lines in')
                    print('invalid %BLOCK LATTICE_ABC')
                lat_a = [a, 0, 0]
                lat_b = [b * cos(gamma), b * sin(gamma), 0]
                lat_c1 = c * cos(beta)
                lat_c2 = c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma)
                lat_c3 = sqrt(c * c - lat_c1 * lat_c1 - lat_c2 * lat_c2)
                lat_c = [lat_c1, lat_c2, lat_c3]
                lat = [lat_a, lat_b, lat_c]
                have_lat = True

            elif tokens[1].upper() == "POSITIONS_ABS" and not have_pos:
                tokens, l = get_tokens(lines, l)
                if len(tokens) == 1:
                    print('read_cell: Warning - ignoring unit specifier in')
                    print('%BLOCK POSITIONS_ABS(assuming Angstrom instead)')
                    tokens, l = get_tokens(lines, l)
                while len(tokens) == 4:
                    spec.append(tokens[0])
                    pos.append([float(p) for p in tokens[1:4]])
                    tokens, l = get_tokens(lines, l)
                if tokens[0].upper() != "%ENDBLOCK":
                    print('read_cell: Warning - ignoring invalid lines in')
                    print('%%BLOCK POSITIONS_ABS:\n\t %s' % tokens)
                have_pos = True

            elif tokens[1].upper() == "POSITIONS_FRAC" and not have_pos:
                pos_frac = True
                tokens, l = get_tokens(lines, l)
                while len(tokens) == 4:
                    spec.append(tokens[0])
                    pos.append([float(p) for p in tokens[1:4]])
                    tokens, l = get_tokens(lines, l)
                if tokens[0].upper() != "%ENDBLOCK":
                    print('read_cell: Warning - ignoring invalid lines')
                    print('%%BLOCK POSITIONS_FRAC:\n\t %s' % tokens)
                have_pos = True
            elif tokens[1].upper() == 'SPECIES_POT':
                tokens, l = get_tokens(lines, l)
                while tokens and not tokens[0].upper() == '%ENDBLOCK':
                    if len(tokens) == 2:
                        calc.cell.species_pot = tuple(tokens)
                    tokens, l = get_tokens(lines, l)
            elif tokens[1].upper() == 'IONIC_CONSTRAINTS':

                while True:
                    if tokens and tokens[0].upper() == '%ENDBLOCK':
                        break
                    tokens, l = get_tokens(lines, l)
                    if not len(tokens) == 6:
                        continue
                    _, species, nic, x, y, z = tokens
                    nic = int(nic)
                    if (species, nic) not in raw_constraints:
                        raw_constraints[(species, nic)] = []
                    raw_constraints[(species, nic)].append(array(
                                                           [x, y, z]))
            else:
                print('Warning: the keyword %s is not' % tokens[1].upper())
                print('         interpreted in cell files')
                while not tokens[0].upper() == '%ENDBLOCK':
                    tokens, l = get_tokens(lines, l)
                #raise UserWarning
        else:
            key = tokens[0]
            value = ' '.join(tokens[1:])
            try:
                calc.__setattr__(key, value)
            except:
                print("Problem setting calc.cell.%s = %s" % (key, value))
                raise

    if pos_frac:
        atoms = ase.Atoms(
            calculator=calc,
            cell=lat,
            pbc=True,
            scaled_positions=pos,
            symbols=spec,
            )
    else:
        atoms = ase.Atoms(
            calculator=calc,
            cell=lat,
            pbc=True,
            positions=pos,
            symbols=spec,
            )

    fixed_atoms = []
    for (species, nic), value in raw_constraints.items():
        absolute_nr = atoms.calc._get_absolute_number(species, nic)
        if len(value) == 3:
            fixed_atoms.append(absolute_nr)
        elif len(value) == 2:
            constraint = ase.constraints.FixedLine(a=absolute_nr,
                direction=cross(value[0], value[1]))
            constraints.append(constraint)
        elif len(value) == 1:
            constraint = ase.constraints.FixedPlane(a=absolute_nr,
                direction=array(value[0], dtype=float32))
            constraints.append(constraint)
        else:
            print('Error: Found %s statements attached to atoms %s'
                  % (len(value), absolute_nr))
    constraints.append(ase.constraints.FixAtoms(fixed_atoms))
    atoms.set_constraint(constraints)

    # needs to go here again to have the constraints in
    # atoms.calc.atoms.constraints as well
    atoms.calc.atoms = atoms
    atoms.calc.push_oldstate()
    return atoms
Esempio n. 4
0
def write_castep_cell(fd,
                      atoms,
                      positions_frac=False,
                      force_write=False,
                      precision=6,
                      magnetic_moments=None,
                      castep_cell=None):
    """
    This CASTEP export function write minimal information to
    a .cell file. If the atoms object is a trajectory, it will
    take the last image.

    Note that function has been altered in order to require a filedescriptor
    rather than a filename. This allows to use the more generic write()
    function from formats.py

    Note that the "force_write" keywords has no effect currently.

    Arguments:

        positions_frac: boolean. If true, positions are printed as fractional
                        rather than absolute. Default is false.
        castep_cell: if provided, overrides the existing CastepCell object in
                     the Atoms calculator
        precision: number of digits to which lattice and positions are printed
        magnetic_moments: if None, no SPIN values are initialised.
                          If 'initial', the values from
                          get_initial_magnetic_moments() are used.
                          If 'calculated', the values from
                          get_magnetic_moments() are used.
                          If an array of the same length as the atoms object,
                          its contents will be used as magnetic moments.
    """

    if atoms is None:
        print('Atoms object not initialized')
        return False
    if isinstance(atoms, list):
        if len(atoms) > 1:
            atoms = atoms[-1]

    # Header
    fd.write('#######################################################\n')
    fd.write('#CASTEP cell file: %s\n' % fd.name)
    fd.write('#Created using the Atomic Simulation Environment (ASE)#\n')
    fd.write('#######################################################\n\n')

    # To write this we simply use the existing Castep calculator, or create
    # one
    from ase.calculators.castep import Castep, CastepCell

    try:
        has_cell = isinstance(atoms.calc.cell, CastepCell)
    except AttributeError:
        has_cell = False

    if has_cell:
        cell = deepcopy(atoms.calc.cell)
    else:
        cell = Castep(keyword_tolerance=2).cell

    # Write lattice
    fformat = '%{0}.{1}f'.format(precision + 3, precision)
    cell_block_format = ' '.join([fformat] * 3)
    cell.lattice_cart = [
        cell_block_format % tuple(line) for line in atoms.get_cell()
    ]

    if positions_frac:
        pos_keyword = 'positions_frac'
        positions = atoms.get_scaled_positions()
    else:
        pos_keyword = 'positions_abs'
        positions = atoms.get_positions()

    if atoms.has('castep_custom_species'):
        elems = atoms.get_array('castep_custom_species')
    else:
        elems = atoms.get_chemical_symbols()

    if atoms.has('castep_labels'):
        labels = atoms.get_array('castep_labels')
    else:
        labels = ['NULL'] * len(elems)

    if str(magnetic_moments).lower() == 'initial':
        magmoms = atoms.get_initial_magnetic_moments()
    elif str(magnetic_moments).lower() == 'calculated':
        magmoms = atoms.get_magnetic_moments()
    elif np.array(magnetic_moments).shape == (len(elems), ):
        magmoms = np.array(magnetic_moments)
    else:
        magmoms = [0] * len(elems)

    pos_block = []
    pos_block_format = '%s ' + cell_block_format

    for i, el in enumerate(elems):
        xyz = positions[i]
        line = pos_block_format % tuple([el] + list(xyz))
        # ADD other keywords if necessary
        if magmoms[i] != 0:
            line += ' SPIN={0} '.format(magmoms[i])
        if labels[i].strip() not in ('NULL', ''):
            line += ' LABEL={0} '.format(labels[i])
        pos_block.append(line)

    setattr(cell, pos_keyword, pos_block)

    constraints = atoms.constraints
    if len(constraints):
        _supported_constraints = (FixAtoms, FixedPlane, FixedLine,
                                  FixCartesian)

        constr_block = []

        for constr in constraints:
            if not isinstance(constr, _supported_constraints):
                print('Warning: you have constraints in your atoms, that are')
                print('         not supported by the CASTEP ase interface')
                break
            if isinstance(constr, FixAtoms):
                for i in constr.index:

                    try:
                        symbol = atoms.get_chemical_symbols()[i]
                        nis = atoms.calc._get_number_in_species(i)
                    except KeyError:
                        raise UserWarning('Unrecognized index in' +
                                          ' constraint %s' % constr)
                    for j in range(3):
                        L = '%6d %3s %3d   ' % (len(constr_block) + 1, symbol,
                                                nis)
                        L += ['1 0 0', '0 1 0', '0 0 1'][j]
                        constr_block += [L]

            elif isinstance(constr, FixCartesian):
                n = constr.a
                symbol = atoms.get_chemical_symbols()[n]
                nis = atoms.calc._get_number_in_species(n)

                for i, m in enumerate(constr.mask):
                    if m == 1:
                        continue
                    L = '%6d %3s %3d   ' % (len(constr_block) + 1, symbol, nis)
                    L += ' '.join(['1' if j == i else '0' for j in range(3)])
                    constr_block += [L]

            elif isinstance(constr, FixedPlane):
                n = constr.a
                symbol = atoms.get_chemical_symbols()[n]
                nis = atoms.calc._get_number_in_species(n)

                L = '%6d %3s %3d   ' % (len(constr_block) + 1, symbol, nis)
                L += ' '.join([str(d) for d in constr.dir])
                constr_block += [L]

            elif isinstance(constr, FixedLine):
                n = constr.a
                symbol = atoms.get_chemical_symbols()[n]
                nis = atoms.calc._get_number_in_species(n)

                direction = constr.dir
                ((i1, v1), (i2, v2)) = sorted(enumerate(direction),
                                              key=lambda x: abs(x[1]),
                                              reverse=True)[:2]
                n1 = np.zeros(3)
                n1[i2] = v1
                n1[i1] = -v2
                n1 = n1 / np.linalg.norm(n1)

                n2 = np.cross(direction, n1)

                l1 = '%6d %3s %3d   %f %f %f' % (len(constr_block) + 1, symbol,
                                                 nis, n1[0], n1[1], n1[2])
                l2 = '%6d %3s %3d   %f %f %f' % (len(constr_block) + 2, symbol,
                                                 nis, n2[0], n2[1], n2[2])

                constr_block += [l1, l2]

        cell.ionic_constraints = constr_block

    write_freeform(fd, cell)

    return True
Esempio n. 5
0
def read_castep_cell(fd,
                     index=None,
                     calculator_args={},
                     find_spg=False,
                     units=units_CODATA2002):
    """Read a .cell file and return an atoms object.
    Any value found that does not fit the atoms API
    will be stored in the atoms.calc attribute.

    By default, the Castep calculator will be tolerant and in the absence of a
    castep_keywords.json file it will just accept all keywords that aren't
    automatically parsed.
    """

    from ase.calculators.castep import Castep

    cell_units = {  # Units specifiers for CASTEP
        'bohr': units_CODATA2002['a0'],
        'ang': 1.0,
        'm': 1e10,
        'cm': 1e8,
        'nm': 10,
        'pm': 1e-2
    }

    calc = Castep(**calculator_args)

    if calc.cell.castep_version == 0 and calc._kw_tol < 3:
        # No valid castep_keywords.json was found
        print('read_cell: Warning - Was not able to validate CASTEP input.')
        print('           This may be due to a non-existing '
              '"castep_keywords.json"')
        print('           file or a non-existing CASTEP installation.')
        print('           Parsing will go on but keywords will not be '
              'validated and may cause problems if incorrect during a CASTEP '
              'run.')

    celldict = read_freeform(fd)

    def parse_blockunit(line_tokens, blockname):
        u = 1.0
        if len(line_tokens[0]) == 1:
            usymb = line_tokens[0][0].lower()
            u = cell_units.get(usymb, 1)
            if usymb not in cell_units:
                warnings.warn(
                    ('read_cell: Warning - ignoring invalid '
                     'unit specifier in %BLOCK {0} '
                     '(assuming Angstrom instead)').format(blockname))
            line_tokens = line_tokens[1:]
        return u, line_tokens

    # Arguments to pass to the Atoms object at the end
    aargs = {'pbc': True}

    # Start by looking for the lattice
    lat_keywords = [w in celldict for w in ('lattice_cart', 'lattice_abc')]
    if all(lat_keywords):
        warnings.warn('read_cell: Warning - two lattice blocks present in the'
                      ' same file. LATTICE_ABC will be ignored')
    elif not any(lat_keywords):
        raise ValueError('Cell file must contain at least one between '
                         'LATTICE_ABC and LATTICE_CART')

    if 'lattice_abc' in celldict:

        lines = celldict.pop('lattice_abc')[0].split('\n')
        line_tokens = [l.split() for l in lines]

        u, line_tokens = parse_blockunit(line_tokens, 'lattice_abc')

        if len(line_tokens) != 2:
            warnings.warn('read_cell: Warning - ignoring additional '
                          'lines in invalid %BLOCK LATTICE_ABC')

        abc = [float(p) * u for p in line_tokens[0][:3]]
        angles = [float(phi) for phi in line_tokens[1][:3]]

        aargs['cell'] = cellpar_to_cell(abc + angles)

    if 'lattice_cart' in celldict:

        lines = celldict.pop('lattice_cart')[0].split('\n')
        line_tokens = [l.split() for l in lines]

        u, line_tokens = parse_blockunit(line_tokens, 'lattice_cart')

        if len(line_tokens) != 3:
            warnings.warn('read_cell: Warning - ignoring more than '
                          'three lattice vectors in invalid %BLOCK '
                          'LATTICE_CART')

        aargs['cell'] = [[float(x) * u for x in lt[:3]] for lt in line_tokens]

    # Now move on to the positions
    pos_keywords = [w in celldict for w in ('positions_abs', 'positions_frac')]

    if all(pos_keywords):
        warnings.warn('read_cell: Warning - two lattice blocks present in the'
                      ' same file. POSITIONS_FRAC will be ignored')
        del celldict['positions_frac']
    elif not any(pos_keywords):
        raise ValueError('Cell file must contain at least one between '
                         'POSITIONS_FRAC and POSITIONS_ABS')

    aargs['symbols'] = []
    pos_type = 'positions'
    pos_block = celldict.pop('positions_abs', [None])[0]
    if pos_block is None:
        pos_type = 'scaled_positions'
        pos_block = celldict.pop('positions_frac', [None])[0]
    aargs[pos_type] = []

    lines = pos_block.split('\n')
    line_tokens = [l.split() for l in lines]

    if 'scaled' not in pos_type:
        u, line_tokens = parse_blockunit(line_tokens, 'positions_abs')
    else:
        u = 1.0

    # Here we extract all the possible additional info
    # These are marked by their type

    add_info = {
        'SPIN': (float, 0.0),  # (type, default)
        'MAGMOM': (float, 0.0),
        'LABEL': (str, 'NULL')
    }
    add_info_arrays = dict((k, []) for k in add_info)

    def parse_info(raw_info):

        re_keys = (r'({0})\s*[=:\s]{{1}}\s'
                   r'*([^\s]*)').format('|'.join(add_info.keys()))
        # Capture all info groups
        info = re.findall(re_keys, raw_info)
        info = {g[0]: add_info[g[0]][0](g[1]) for g in info}
        return info

    # Array for custom species (a CASTEP special thing)
    # Usually left unused
    custom_species = None

    for tokens in line_tokens:
        # Now, process the whole 'species' thing
        spec_custom = tokens[0].split(':', 1)
        elem = spec_custom[0]
        if len(spec_custom) > 1 and custom_species is None:
            # Add it to the custom info!
            custom_species = list(aargs['symbols'])
        if custom_species is not None:
            custom_species.append(tokens[0])
        aargs['symbols'].append(elem)
        aargs[pos_type].append([float(p) * u for p in tokens[1:4]])
        # Now for the additional information
        info = ' '.join(tokens[4:])
        info = parse_info(info)
        for k in add_info:
            add_info_arrays[k] += [info.get(k, add_info[k][1])]

    # Now on to the species potentials...
    if 'species_pot' in celldict:
        lines = celldict.pop('species_pot')[0].split('\n')
        line_tokens = [l.split() for l in lines]

        for tokens in line_tokens:
            if len(tokens) == 1:
                # It's a library
                all_spec = (set(custom_species) if custom_species is not None
                            else set(aargs['symbols']))
                for s in all_spec:
                    calc.cell.species_pot = (s, tokens[0])
            else:
                calc.cell.species_pot = tuple(tokens[:2])

    # Ionic constraints
    raw_constraints = {}

    if 'ionic_constraints' in celldict:
        lines = celldict.pop('ionic_constraints')[0].split('\n')
        line_tokens = [l.split() for l in lines]

        for tokens in line_tokens:
            if not len(tokens) == 6:
                continue
            _, species, nic, x, y, z = tokens
            # convert xyz to floats
            x = float(x)
            y = float(y)
            z = float(z)

            nic = int(nic)
            if (species, nic) not in raw_constraints:
                raw_constraints[(species, nic)] = []
            raw_constraints[(species, nic)].append(np.array([x, y, z]))

    # Symmetry operations
    if 'symmetry_ops' in celldict:
        lines = celldict.pop('symmetry_ops')[0].split('\n')
        line_tokens = [l.split() for l in lines]

        # Read them in blocks of four
        blocks = np.array(line_tokens).astype(float)
        if (len(blocks.shape) != 2 or blocks.shape[1] != 3
                or blocks.shape[0] % 4 != 0):
            warnings.warn('Warning: could not parse SYMMETRY_OPS'
                          ' block properly, skipping')
        else:
            blocks = blocks.reshape((-1, 4, 3))
            rotations = blocks[:, :3]
            translations = blocks[:, 3]

            # Regardless of whether we recognize them, store these
            calc.cell.symmetry_ops = (rotations, translations)

    # Anything else that remains, just add it to the cell object:
    for k, (val, otype) in celldict.items():
        try:
            if otype == 'block':
                val = val.split('\n')  # Avoids a bug for one-line blocks
            calc.cell.__setattr__(k, val)
        except Exception:
            raise RuntimeError('Problem setting calc.cell.%s = %s' % (k, val))

    # Get the relevant additional info
    aargs['magmoms'] = np.array(add_info_arrays['SPIN'])
    # SPIN or MAGMOM are alternative keywords
    aargs['magmoms'] = np.where(aargs['magmoms'] != 0, aargs['magmoms'],
                                add_info_arrays['MAGMOM'])
    labels = np.array(add_info_arrays['LABEL'])

    aargs['calculator'] = calc

    atoms = ase.Atoms(**aargs)

    # Spacegroup...
    if find_spg:
        # Try importing spglib
        try:
            import spglib
        except ImportError:
            try:
                from pyspglib import spglib
            except ImportError:
                # spglib is not present
                warnings.warn('spglib not found installed on this system - '
                              'automatic spacegroup detection is not possible')
                spglib = None

        if spglib is not None:
            symmd = spglib.get_symmetry_dataset(atoms)
            atoms_spg = Spacegroup(int(symmd['number']))
            atoms.info['spacegroup'] = atoms_spg

    atoms.new_array('castep_labels', labels)
    if custom_species is not None:
        atoms.new_array('castep_custom_species', np.array(custom_species))

    fixed_atoms = []
    constraints = []
    for (species, nic), value in raw_constraints.items():
        absolute_nr = atoms.calc._get_absolute_number(species, nic)
        if len(value) == 3:
            # Check if they are linearly independent
            if np.linalg.det(value) == 0:
                print('Error: Found linearly dependent constraints attached '
                      'to atoms %s' % (absolute_nr))
                continue
            fixed_atoms.append(absolute_nr)
        elif len(value) == 2:
            direction = np.cross(value[0], value[1])
            # Check if they are linearly independent
            if np.linalg.norm(direction) == 0:
                print('Error: Found linearly dependent constraints attached '
                      'to atoms %s' % (absolute_nr))
                continue
            constraint = ase.constraints.FixedLine(a=absolute_nr,
                                                   direction=direction)
            constraints.append(constraint)
        elif len(value) == 1:
            constraint = ase.constraints.FixedPlane(a=absolute_nr,
                                                    direction=np.array(
                                                        value[0],
                                                        dtype=np.float32))
            constraints.append(constraint)
        else:
            print('Error: Found %s statements attached to atoms %s' %
                  (len(value), absolute_nr))

    # we need to sort the fixed atoms list in order not to raise an assertion
    # error in FixAtoms
    if fixed_atoms:
        constraints.append(
            ase.constraints.FixAtoms(indices=sorted(fixed_atoms)))
    if constraints:
        atoms.set_constraint(constraints)

    atoms.calc.atoms = atoms
    atoms.calc.push_oldstate()

    return atoms
Esempio n. 6
0
0.0 0.0 1.0
0.0 0.0 0.0""" in mock_ccell.symmetry_ops.value

# check if the CastepOpt, CastepCell comparison mechanism works
if castep_keywords:
    p1 = CastepParam(castep_keywords)
    p2 = CastepParam(castep_keywords)

    assert p1._options == p2._options

    p1._options['xc_functional'].value = 'PBE'
    p1.xc_functional = 'PBE'

    assert p1._options != p2._options

c = Castep(directory=tmp_dir, label='test_label', keyword_tolerance=2)
if castep_keywords:
    c.xc_functional = 'PBE'
else:
    c.param.xc_functional = 'PBE'  # In "forgiving" mode, we need to specify

lattice = ase.lattice.cubic.BodyCenteredCubic('Li')

print('For the sake of evaluating this test, warnings')
print('about auto-generating pseudo-potentials are')
print('normal behavior and can be safely ignored')

lattice.set_calculator(c)

param_fn = os.path.join(tmp_dir, 'myParam.param')
param = open(param_fn, 'w')
Esempio n. 7
0
def read_castep_cell(fd, index=None):
    """Read a .cell file and return an atoms object.
    Any value found that does not fit the atoms API
    will be stored in the atoms.calc attribute.

    This routine has been modified to also be able to read *.cell files even if
    there is no CASTEP installation or castep_keywords.py available. We wil
    then make use of a fallback-mode which basically just read atoms positions
    and unit cell information. This can very highly useful for visualization
    using the ASE gui.
    """

    from ase.calculators.castep import Castep

    _fallback = False
    try:
        calc = Castep()
    except Exception as exception:
        print('read_cell: Warning - Was not able to initialize CASTEP '
              'calculator.')
        print('           This may be due to a non-existing '
              '"castep.keywords.py"')
        print('           file or a non-existing CASTEP installation.')
        print('           Original error message appears below:')
        print('')
        print(' ' * 11 + exception.__str__().replace('\n', '\n' + ' ' * 11))
        print('')
        print(
            '           Fallback-mode will be applied to provide at least the')
        print('           geometric information contained in the *.cell file.')
        calc = None
        _fallback = True

    # fd will be closed by embracing read() routine
    lines = fd.readlines()

    def get_tokens(lines, l, maxsplit=0):
        """Tokenizes one line of a *cell file."""
        comment_chars = '#!;'
        separator_re = '[\s=:]+'
        while l < len(lines):
            line = lines[l].strip()
            if len(line) == 0:
                l += 1
                continue
            elif any([line.startswith(comment_char)
                      for comment_char in comment_chars]):
                l += 1
                continue
            else:
                # Remove comments
                line = re.split('[{0}]+'.format(comment_chars), line, 1)[0]
                # Tokenize
                tokens = re.split(separator_re, line.strip(), maxsplit)
                return tokens, l + 1
        tokens = ''

    # This print statement is definitely not necessary
    #    print("read_cell: Warning - get_tokens has not found any more tokens")
        return tokens, l

    lat = []
    have_lat = False

    pos = []
    spec = []

    # Here we extract all the possible additional info
    # These are marked by their type
    add_info = {
        'SPIN': float,
        'MAGMOM': float,
        'LABEL': str,
    }
    add_info_arrays = dict((k, []) for k in add_info)

    # A convenient function that extracts this info from a line fragment
    def get_add_info(ai_arrays, line=''):
        re_keys = '({0})'.format('|'.join(add_info.keys()))
        ai_dict = {}
        sline = re.split(re_keys, line, flags=re.IGNORECASE)
        for t_i, tok in enumerate(sline):
            if tok in add_info:
                try:
                    ai_dict[tok] = re.split('[:=]',
                                            sline[t_i + 1],
                                            maxsplit=1)[1].strip()
                except IndexError:
                    ai_dict[tok] = None
        # Then turn these into values into the arrays
        for k in ai_arrays:
            if k not in ai_dict or ai_dict[k] is None:
                ai_arrays[k].append({str: 'NULL',
                                     float: 0.0,
                                     }[add_info[k]])
            else:
                ai_arrays[k].append(add_info[k](ai_dict[k]))

    constraints = []
    raw_constraints = {}
    have_pos = False
    pos_frac = False

    l = 0
    while l < len(lines):
        tokens, l = get_tokens(lines, l)
        if not tokens:
            continue
        elif tokens[0].upper() == '%BLOCK':
            if tokens[1].upper() == 'LATTICE_CART' and not have_lat:
                tokens, l = get_tokens(lines, l)
                if len(tokens) == 1:
                    print('read_cell: Warning - ignoring unit specifier in')
                    print('%BLOCK LATTICE_CART (assuming Angstrom instead)')
                    tokens, l = get_tokens(lines, l)
                for _ in range(3):
                    lat_vec = [float(a) for a in tokens[0:3]]
                    lat.append(lat_vec)
                    tokens, l = get_tokens(lines, l)
                if tokens[0].upper() != '%ENDBLOCK':
                    print('read_cell: Warning - ignoring more than three')
                    print('lattice vectors in invalid %BLOCK LATTICE_CART')
                    print('%s ...' % tokens[0].upper())
                have_lat = True

            elif tokens[1].upper() == 'LATTICE_ABC' and not have_lat:
                tokens, l = get_tokens(lines, l)
                if len(tokens) == 1:
                    print('read_cell: Warning - ignoring unit specifier in')
                    print('%BLOCK LATTICE_ABC (assuming Angstrom instead)')
                    tokens, l = get_tokens(lines, l)
                a, b, c = map(float, tokens[0:3])
                tokens, l = get_tokens(lines, l)
                alpha, beta, gamma = [np.radians(float(phi))
                                      for phi in tokens[0:3]]
                tokens, l = get_tokens(lines, l)
                if tokens[0].upper() != '%ENDBLOCK':
                    print('read_cell: Warning - ignoring additional lines in')
                    print('invalid %BLOCK LATTICE_ABC')
                lat_a = [a, 0, 0]
                lat_b = [b * np.cos(gamma), b * np.sin(gamma), 0]
                lat_c1 = c * np.cos(beta)
                lat_c2 = c * ((np.cos(alpha) - np.cos(beta) * np.cos(gamma)) /
                              np.sin(gamma))
                lat_c3 = np.sqrt(c * c - lat_c1 * lat_c1 - lat_c2 * lat_c2)
                lat_c = [lat_c1, lat_c2, lat_c3]
                lat = [lat_a, lat_b, lat_c]
                have_lat = True

            elif tokens[1].upper() == 'POSITIONS_ABS' and not have_pos:
                tokens, l = get_tokens(lines, l)
                if len(tokens) == 1:
                    print('read_cell: Warning - ignoring unit specifier in')
                    print('%BLOCK POSITIONS_ABS(assuming Angstrom instead)')
                    tokens, l = get_tokens(lines, l, maxsplit=4)
                # fix to be able to read initial spin assigned on the atoms
                while len(tokens) >= 4:
                    spec.append(tokens[0])
                    pos.append([float(p) for p in tokens[1:4]])
                    if len(tokens) > 4:
                        get_add_info(add_info_arrays, tokens[4])
                    else:
                        get_add_info(add_info_arrays)
                    tokens, l = get_tokens(lines, l, maxsplit=4)
                if tokens[0].upper() != '%ENDBLOCK':
                    print('read_cell: Warning - ignoring invalid lines in')
                    print('%%BLOCK POSITIONS_ABS:\n\t %s' % tokens)
                have_pos = True

            elif tokens[1].upper() == 'POSITIONS_FRAC' and not have_pos:
                pos_frac = True
                tokens, l = get_tokens(lines, l, maxsplit=4)
                # fix to be able to read initial spin assigned on the atoms
                while len(tokens) >= 4:
                    spec.append(tokens[0])
                    pos.append([float(p) for p in tokens[1:4]])
                    if len(tokens) > 4:
                        get_add_info(add_info_arrays, tokens[4])
                    else:
                        get_add_info(add_info_arrays)
                    tokens, l = get_tokens(lines, l, maxsplit=4)
                if tokens[0].upper() != '%ENDBLOCK':
                    print('read_cell: Warning - ignoring invalid lines')
                    print('%%BLOCK POSITIONS_FRAC:\n\t %s' % tokens)
                have_pos = True
            elif tokens[1].upper() == 'SPECIES_POT':
                if not _fallback:
                    tokens, l = get_tokens(lines, l)
                    while tokens and not tokens[0].upper() == '%ENDBLOCK':
                        if len(tokens) == 2:
                            calc.cell.species_pot = tuple(tokens)
                        tokens, l = get_tokens(lines, l)
            elif tokens[1].upper() == 'IONIC_CONSTRAINTS':

                while True:
                    if tokens and tokens[0].upper() == '%ENDBLOCK':
                        break
                    tokens, l = get_tokens(lines, l)
                    if not len(tokens) == 6:
                        continue
                    _, species, nic, x, y, z = tokens
                    # convert xyz to floats
                    x = float(x)
                    y = float(y)
                    z = float(z)

                    nic = int(nic)
                    if (species, nic) not in raw_constraints:
                        raw_constraints[(species, nic)] = []
                    raw_constraints[(species, nic)].append(np.array(
                                                           [x, y, z]))

            else:
                print('Warning: the keyword %s is not' % tokens[1].upper())
                print('         interpreted in cell files')
                while not tokens[0].upper() == '%ENDBLOCK':
                    tokens, l = get_tokens(lines, l)
                # raise UserWarning
        else:
            key = tokens[0]
            value = ' '.join(tokens[1:])
            if not _fallback:
                try:
                    calc.__setattr__(key, value)
                except:
                    print('Problem setting calc.cell.%s = %s' % (key, value))
                    raise

    # Get the relevant additional info
    magmom = np.array(add_info_arrays['SPIN'])
    # SPIN or MAGMOM are alternative keywords
    magmom = np.where(magmom != 0, magmom, add_info_arrays['MAGMOM'])
    labels = np.array(add_info_arrays['LABEL'])

    if pos_frac:
        atoms = ase.Atoms(
            calculator=calc,
            cell=lat,
            pbc=True,
            scaled_positions=pos,
            symbols=spec,
            magmoms=magmom)
    else:
        atoms = ase.Atoms(
            calculator=calc,
            cell=lat,
            pbc=True,
            positions=pos,
            symbols=spec,
            magmoms=magmom)

    atoms.new_array('castep_labels', labels)

    fixed_atoms = []
    for (species, nic), value in raw_constraints.items():
        absolute_nr = atoms.calc._get_absolute_number(species, nic)
        if len(value) == 3:
            fixed_atoms.append(absolute_nr)
        elif len(value) == 2:
            constraint = ase.constraints.FixedLine(
                a=absolute_nr,
                direction=np.cross(value[0], value[1]))
            constraints.append(constraint)
        elif len(value) == 1:
            # catch cases in which constraints are given in a single line in
            # the cell file
            # if np.count_nonzero(value[0]) == 3:
            #     fixed_atoms.append(absolute_nr)
            # elif np.count_nonzero(value[0]) == 2:
            #     # in this case we need a FixedLine instance
            #     # it is initialized with the atom's index
            #     constraint = ase.constraints.FixedLine(a=absolute_nr,
            #         direction=[not v for v in value[0]])
            #     constraints.append(constraint)
            # else:

            # I do not think you can have a fixed position of a fixed
            # line with only one constraint -- JML
            constraint = ase.constraints.FixedPlane(
                a=absolute_nr,
                direction=np.array(value[0], dtype=np.float32))
            constraints.append(constraint)
        else:
            print('Error: Found %s statements attached to atoms %s'
                  % (len(value), absolute_nr))
    # we need to sort the fixed atoms list in order not to raise an assertion
    # error in FixAtoms
    if fixed_atoms:
        constraints.append(
            ase.constraints.FixAtoms(indices=sorted(fixed_atoms)))
    if constraints:
        atoms.set_constraint(constraints)

    if not _fallback:
        # needs to go here again to have the constraints in
        # atoms.calc.atoms.constraints as well
        atoms.calc.atoms = atoms
        atoms.calc.push_oldstate()
    return atoms
Esempio n. 8
0
def test_castep_interface():
    """Simple shallow test of the CASTEP interface"""
    import os
    import re
    import tempfile
    import warnings

    import numpy as np
    import ase
    import ase.lattice.cubic
    from ase.calculators.castep import (Castep, CastepOption, CastepParam,
                                        CastepCell, make_cell_dict,
                                        make_param_dict, CastepKeywords,
                                        create_castep_keywords,
                                        import_castep_keywords,
                                        CastepVersionError)

    # XXX on porting this test to pytest it wasn't skipped as it should be.
    # At any rate it failed then.  Maybe someone should look into that ...
    #
    # Hence, call the constructor to trigger our test skipping hack:
    Castep()

    tmp_dir = tempfile.mkdtemp()

    # We have fundamentally two sets of tests: one if CASTEP is present, the other
    # if it isn't
    has_castep = False
    # Try creating and importing the castep keywords first
    try:
        create_castep_keywords(castep_command=os.environ['CASTEP_COMMAND'],
                               path=tmp_dir,
                               fetch_only=20)
        has_castep = True  # If it worked, it must be present
    except KeyError:
        print('Could not find the CASTEP_COMMAND environment variable - please'
              ' set it to run the full set of Castep tests')
    except CastepVersionError:
        print(
            'Invalid CASTEP_COMMAND provided - please set the correct one to '
            'run the full set of Castep tests')

    try:
        castep_keywords = import_castep_keywords(
            castep_command=os.environ.get('CASTEP_COMMAND', ''))
    except CastepVersionError:
        castep_keywords = None

    # Start by testing the fundamental parts of a CastepCell/CastepParam object
    boolOpt = CastepOption('test_bool', 'basic', 'defined')
    boolOpt.value = 'TRUE'
    assert boolOpt.raw_value is True

    float3Opt = CastepOption('test_float3', 'basic', 'real vector')
    float3Opt.value = '1.0 2.0 3.0'
    assert np.isclose(float3Opt.raw_value, [1, 2, 3]).all()

    # Generate a mock keywords object
    mock_castep_keywords = CastepKeywords(make_param_dict(), make_cell_dict(),
                                          [], [], 0)
    mock_cparam = CastepParam(mock_castep_keywords, keyword_tolerance=2)
    mock_ccell = CastepCell(mock_castep_keywords, keyword_tolerance=2)

    # Test special parsers
    mock_cparam.continuation = 'default'
    mock_cparam.reuse = 'default'
    assert mock_cparam.reuse.value is None

    mock_ccell.species_pot = ('Si', 'Si.usp')
    mock_ccell.species_pot = ('C', 'C.usp')
    assert 'Si Si.usp' in mock_ccell.species_pot.value
    assert 'C C.usp' in mock_ccell.species_pot.value
    symops = (np.eye(3)[None], np.zeros(3)[None])
    mock_ccell.symmetry_ops = symops
    assert """1.0 0.0 0.0
    0.0 1.0 0.0
    0.0 0.0 1.0
    0.0 0.0 0.0""" in mock_ccell.symmetry_ops.value

    # check if the CastepOpt, CastepCell comparison mechanism works
    if castep_keywords:
        p1 = CastepParam(castep_keywords)
        p2 = CastepParam(castep_keywords)

        assert p1._options == p2._options

        p1._options['xc_functional'].value = 'PBE'
        p1.xc_functional = 'PBE'

        assert p1._options != p2._options

    c = Castep(directory=tmp_dir, label='test_label', keyword_tolerance=2)
    if castep_keywords:
        c.xc_functional = 'PBE'
    else:
        c.param.xc_functional = 'PBE'  # In "forgiving" mode, we need to specify

    lattice = ase.lattice.cubic.BodyCenteredCubic('Li')

    print('For the sake of evaluating this test, warnings')
    print('about auto-generating pseudo-potentials are')
    print('normal behavior and can be safely ignored')

    lattice.set_calculator(c)

    param_fn = os.path.join(tmp_dir, 'myParam.param')
    param = open(param_fn, 'w')
    param.write('XC_FUNCTIONAL : PBE #comment\n')
    param.write('XC_FUNCTIONAL : PBE #comment\n')
    param.write('#comment\n')
    param.write('CUT_OFF_ENERGY : 450.\n')
    param.close()
    c.merge_param(param_fn)

    assert c.calculation_required(lattice)
    if has_castep:
        assert c.dryrun_ok()

    c.prepare_input_files(lattice)

    # detecting pseudopotentials tests

    # typical filenames
    files = [
        'Ag_00PBE.usp', 'Ag_00.recpot', 'Ag_C18_PBE_OTF.usp',
        'ag-optgga1.recpot', 'Ag_OTF.usp', 'ag_pbe_v1.4.uspp.F.UPF',
        'Ni_OTF.usp', 'fe_pbe_v1.5.uspp.F.UPF', 'Cu_01.recpot'
    ]

    pp_path = os.path.join(tmp_dir, 'test_pp')
    os.makedirs(pp_path)

    for f in files:
        with open(os.path.join(pp_path, f), 'w') as _f:
            _f.write('DUMMY PP')

    c = Castep(directory=tmp_dir,
               label='test_label_pspots',
               castep_pp_path=pp_path)
    c._pedantic = True
    atoms = ase.build.bulk('Ag')
    atoms.set_calculator(c)

    # I know, unittest would be nicer... maybe at a later point

    # disabled, but may be useful still
    # try:
    # # this should yield no files
    # atoms.calc.find_pspots(suffix='uspp')
    # raise AssertionError
    #    # this should yield no files
    #    atoms.calc.find_pspots(suffix='uspp')
    #    raise AssertionError
    # except RuntimeError as e:
    # #print(e)
    # pass
    #     # print(e)
    #     pass

    try:
        # this should yield non-unique files
        atoms.calc.find_pspots(suffix='recpot')
        raise AssertionError
    except RuntimeError:
        pass

    # now let's see if we find all...
    atoms.calc.find_pspots(pspot='00PBE', suffix='usp')
    assert atoms.calc.cell.species_pot.value.split()[-1] == 'Ag_00PBE.usp'

    atoms.calc.find_pspots(pspot='00', suffix='recpot')
    assert atoms.calc.cell.species_pot.value.split()[-1] == 'Ag_00.recpot'

    atoms.calc.find_pspots(pspot='C18_PBE_OTF', suffix='usp')
    assert atoms.calc.cell.species_pot.value.split(
    )[-1] == 'Ag_C18_PBE_OTF.usp'

    atoms.calc.find_pspots(pspot='optgga1', suffix='recpot')
    assert atoms.calc.cell.species_pot.value.split()[-1] == 'ag-optgga1.recpot'

    atoms.calc.find_pspots(pspot='OTF', suffix='usp')
    assert atoms.calc.cell.species_pot.value.split()[-1] == 'Ag_OTF.usp'

    atoms.calc.find_pspots(suffix='UPF')
    assert (atoms.calc.cell.species_pot.value.split()[-1] ==
            'ag_pbe_v1.4.uspp.F.UPF')

    # testing regular workflow
    c = Castep(directory=tmp_dir,
               label='test_label_pspots',
               castep_pp_path=pp_path,
               find_pspots=True,
               keyword_tolerance=2)
    c._build_missing_pspots = False
    atoms = ase.build.bulk('Ag')
    atoms.set_calculator(c)

    # this should raise an error due to ambuiguity
    try:
        c._fetch_pspots()
        raise AssertionError
    except RuntimeError:
        pass

    for e in ['Ni', 'Fe', 'Cu']:
        atoms = ase.build.bulk(e)
        atoms.set_calculator(c)
        c._fetch_pspots()

    # test writing to file
    tmp_dir = os.path.join(tmp_dir, 'input_files')
    c = Castep(directory=tmp_dir,
               find_pspots=True,
               castep_pp_path=pp_path,
               keyword_tolerance=2)
    c._label = 'test'
    atoms = ase.build.bulk('Cu')
    atoms.set_calculator(c)
    c.prepare_input_files()

    with open(os.path.join(tmp_dir, 'test.cell'), 'r') as f:
        assert re.search(r'Cu Cu_01\.recpot',
                         ''.join(f.readlines())) is not None

    # test keyword conflict management
    c = Castep(cut_off_energy=300.)
    assert float(c.param.cut_off_energy.value) == 300.0
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")

        c.basis_precision = 'MEDIUM'
        assert issubclass(w[-1].category, UserWarning)
        assert "conflicts" in str(w[-1].message)
        assert c.param.cut_off_energy.value is None
        assert c.param.basis_precision.value.strip() == 'MEDIUM'

    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")

        c.cut_off_energy = 200.0
        assert c.param.basis_precision.value is None
        assert issubclass(w[-1].category, UserWarning)
        assert 'option "cut_off_energy" conflicts' in str(w[-1].message)

    # test kpoint setup options
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        # This block of tests is going to generate a lot of conflict warnings.
        # We already tested that those work, so just hide them from the output.

        c = Castep(kpts=[
            (0.0, 0.0, 0.0, 1.0),
        ])
        assert c.cell.kpoint_list.value == '0.0 0.0 0.0 1.0'
        c.set_kpts(((0.0, 0.0, 0.0, 0.25), (0.25, 0.25, 0.3, 0.75)))
        assert c.cell.kpoint_list.value == '0.0 0.0 0.0 0.25\n0.25 0.25 0.3 0.75'
        c.set_kpts(c.cell.kpoint_list.value.split('\n'))
        assert c.cell.kpoint_list.value == '0.0 0.0 0.0 0.25\n0.25 0.25 0.3 0.75'
        c.set_kpts([3, 3, 2])
        assert c.cell.kpoint_mp_grid.value == '3 3 2'
        c.set_kpts(None)
        assert c.cell.kpoints_list.value is None
        assert c.cell.kpoint_list.value is None
        assert c.cell.kpoint_mp_grid.value is None
        c.set_kpts('2 2 3')
        assert c.cell.kpoint_mp_grid.value == '2 2 3'
        c.set_kpts({'even': True, 'gamma': True})
        assert c.cell.kpoint_mp_grid.value == '2 2 2'
        assert c.cell.kpoint_mp_offset.value == '0.25 0.25 0.25'
        c.set_kpts({'size': (2, 2, 4), 'even': False})
        assert c.cell.kpoint_mp_grid.value == '3 3 5'
        assert c.cell.kpoint_mp_offset.value == '0.0 0.0 0.0'
        atoms = ase.build.bulk('Ag')
        atoms.set_calculator(c)
        c.set_kpts({'density': 10, 'gamma': False, 'even': None})
        assert c.cell.kpoint_mp_grid.value == '27 27 27'
        assert c.cell.kpoint_mp_offset.value == '0.018519 0.018519 0.018519'
        c.set_kpts({
            'spacing': (1 / (np.pi * 10)),
            'gamma': False,
            'even': True
        })
        assert c.cell.kpoint_mp_grid.value == '28 28 28'
        assert c.cell.kpoint_mp_offset.value == '0.0 0.0 0.0'

    # test band structure setup
    from ase.dft.kpoints import BandPath
    atoms = ase.build.bulk('Ag')
    bp = BandPath(cell=atoms.cell,
                  path='GX',
                  special_points={
                      'G': [0, 0, 0],
                      'X': [0.5, 0, 0.5]
                  })
    bp = bp.interpolate(npoints=10)
    c = Castep(bandpath=bp)
    kpt_list = c.cell.bs_kpoint_list.value.split('\n')
    assert len(kpt_list) == 10
    assert list(map(float, kpt_list[0].split())) == [0., 0., 0.]
    assert list(map(float, kpt_list[-1].split())) == [0.5, 0.0, 0.5]
Esempio n. 9
0
def main():

    datenow = datetime.datetime.now()
    datenow = datenow.strftime("%d/%m/%Y %H:%M:%S")
    sys.argv[0] = sys.argv[0].replace(" ", "\ ")
    start = time.perf_counter()

    # Write a log file
    log = open("nc_cryst.log", "a+")
    log_line = " ".join(sys.argv)
    log.write(datenow + " " + log_line + "\n")
    log.close()

    warnings.filterwarnings("ignore")

    # Disable
    ##################################################################################

    def blockPrint():
        sys.stdout = open(os.devnull, 'w')

    # Restore
    def enablePrint():
        sys.stdout = sys.__stdout__

    def complementary(hex):
        """returns RGB components of complementary color"""
        hex = hex.lstrip('#')
        r, g, b = tuple(int(hex[i:i + 2], 16) for i in (0, 2, 4))

        hsv = rgb_to_hsv(r, g, b)

        return (r / 255, g / 255, b / 255), tuple(
            np.array(hsv_to_rgb(((hsv[0] + 0.5) % 1), hsv[1], hsv[2])) / 255)

    def update_axes_label_color(axes_actor, color=None):
        """Set the axes label color (internale helper)."""
        if color is None:
            color = rcParams['font']['color']
        color = parse_color(color)
        if isinstance(axes_actor, vtk.vtkAxesActor):
            prop_x = axes_actor.GetXAxisCaptionActor2D(
            ).GetCaptionTextProperty()
            prop_y = axes_actor.GetYAxisCaptionActor2D(
            ).GetCaptionTextProperty()
            prop_z = axes_actor.GetZAxisCaptionActor2D(
            ).GetCaptionTextProperty()
            for prop in [prop_x, prop_y, prop_z]:
                prop.SetColor(color[0], color[1], color[2])
                prop.SetShadow(False)
        elif isinstance(axes_actor, vtk.vtkAnnotatedCubeActor):
            axes_actor.GetTextEdgesProperty().SetColor(color)

        return

    def create_axes_marker2(label_color=None,
                            x_color=None,
                            y_color=None,
                            z_color=None,
                            xlabel='a',
                            ylabel='b',
                            zlabel='c',
                            labels_off=False,
                            line_width=50):
        """Return an axis actor to add in the scene."""
        if x_color is None:
            x_color = rcParams['axes']['x_color']
        if y_color is None:
            y_color = rcParams['axes']['y_color']
        if z_color is None:
            z_color = rcParams['axes']['z_color']
        axes_actor = vtk.vtkAxesActor()

        axes_actor.GetXAxisShaftProperty().SetColor(parse_color(x_color))
        axes_actor.GetXAxisTipProperty().SetColor(parse_color(x_color))
        axes_actor.GetYAxisShaftProperty().SetColor(parse_color(y_color))
        axes_actor.GetYAxisTipProperty().SetColor(parse_color(y_color))
        axes_actor.GetZAxisShaftProperty().SetColor(parse_color(z_color))
        axes_actor.GetZAxisTipProperty().SetColor(parse_color(z_color))

        transform = vtk.vtkTransform()
        mat = transform.GetMatrix()
        latt_or = np.array(latt)
        latt_or[:, 0] = 2 * latt_or[:, 0] / np.linalg.norm(latt_or[:, 0])
        latt_or[:, 1] = 2 * latt_or[:, 1] / np.linalg.norm(latt_or[:, 1])
        latt_or[:, 2] = 2 * latt_or[:, 2] / np.linalg.norm(latt_or[:, 2])
        for i in range(len(latt)):
            for j in range(len(latt)):
                mat.SetElement(i, j, 2 * latt_or[i, j])

        axes_actor.SetUserTransform(transform)

        text = vtk.vtkTextProperty()
        text.SetFontSize(100)
        text.SetBold(True)
        text.SetFontFamilyAsString("Times")

        # Set labels
        axes_actor.SetXAxisLabelText(xlabel)
        axes_actor.SetYAxisLabelText(ylabel)
        axes_actor.SetZAxisLabelText(zlabel)
        axes_actor.SetNormalizedLabelPosition((1.3, 1.3, 1.3))
        axes_actor.GetXAxisCaptionActor2D().SetCaptionTextProperty(text)
        axes_actor.GetYAxisCaptionActor2D().SetCaptionTextProperty(text)
        axes_actor.GetZAxisCaptionActor2D().SetCaptionTextProperty(text)

        if labels_off:
            axes_actor.AxisLabelsOff()
        # Set Line width
        axes_actor.GetXAxisShaftProperty().SetLineWidth(line_width)
        axes_actor.GetYAxisShaftProperty().SetLineWidth(line_width)
        axes_actor.GetZAxisShaftProperty().SetLineWidth(line_width)
        #axes_actor.SetNormalizedTipLength(1,1,1)
        #axes_actor.SetNormalizedShaftLength(2,2,2)

        update_axes_label_color(axes_actor, label_color)

        #axes_actor.SetNormalizedShaftLength(1.6,1.6,1.6)
        #axes_actor.SetNormalizedTipLength(0.4,0.4,0.4)
        #axes_actor.SetTotalLength(2,2,2)
        return axes_actor

    #atomic valency
    valency = np.array([
        1, 0, 1, 2, 3, 4, 5, 8, 1, 0, 1, 2, 3, 4, 5, 6, 6, 0, 1, 2, 3, 6, 5, 6,
        7, 6, 5, 6, 4, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 4, 3, 2,
        3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3,
        4, 5, 6, 7, 8, 6, 6, 7, 2, 3, 4, 5, 6, 4, 7, 0, 1, 2, 3, 4, 5, 6, 7, 7,
        7, 6, 4, 5, 4, 4, 3, 3, 3, 4, 5, 6, 7, 8, 6, 6, 3, 2, 1, 2, 3, 4, 0, 8
    ])

    # Some functions

    track_r = np.array([[-10, -10, -10]])

    def field_lines(point_grid):
        #ds=(V**(1/3))/N
        #ads=2/N
        mag_2 = []
        N = 100
        #ds=0.01
        track_n = []
        track_r = np.array([[-10, -10, -10]])

        lines = np.zeros((2 * len(point_grid), N, 3))
        mag = np.zeros((2 * len(point_grid), N))
        j = -1
        for o, r0 in enumerate(point_grid):
            F_old = [0, 0, 0]
            F_mag = 0
            for one in [1, -1]:
                j += 1
                xs = []
                ys = []
                zs = []

                r = r0
                for n in range(N):
                    s = 0.01
                    xs.append(r[0])
                    ys.append(r[1])
                    zs.append(r[2])
                    lines[j, n, :] = r
                    mag[j, n] = np.linalg.norm(F_mag)
                    #mag.append(np.linalg.norm(F_mag))
                    #mag_2.append(np.linalg.norm(F_mag))
                    if nc_fort.is_close(track_r, r, len(track_r), 5e-3):
                        break

                    track_r = np.vstack((track_r, r))

                    if r[0] >= np.max(X) or r[0] <= np.min(
                            X) or r[1] >= np.max(Y) or r[1] <= np.min(
                                Y) or r[2] >= np.max(Z) or r[2] <= np.min(Z):

                        break
                    x, y, z = r

                    try:
                        vec_x = f_x([x, y, z])
                    except:
                        vec_x = [0, 0, 0]

                    try:
                        vec_y = f_y([x, y, z])
                    except:
                        vec_y = [0, 0, 0]

                    try:
                        vec_z = f_z([x, y, z])
                    except:
                        vec_z = [0, 0, 0]

                    F = np.array([vec_x[0], vec_y[0], vec_z[0]])  #line_gen(r)
                    F_mag = F
                    F = F / np.linalg.norm(F)
                    phi = np.dot(F, F_old)
                    phi = np.arccos(phi) / np.pi
                    #print(n,phi)
                    if np.round(phi, 4) == 1:
                        break

                    r = r + one * F * s
                    F_old = F
                track_n.append(n)
                xs = np.array(xs)
                ys = np.array(ys)
                zs = np.array(zs)

                line_points = np.column_stack((xs, ys, zs))

        return lines, mag, track_n

    #############################################################

    #import matplotlib.pyplot as plt

    # LEFT BLANK

    ###############################################################

    ### #######          ######
    #  #     #          #     #   ##   #####   ####  ###### #####
    #  #     #          #     #  #  #  #    # #      #      #    #
    #  #     # #####    ######  #    # #    #  ####  #####  #    #
    #  #     #          #       ###### #####       # #      #####
    #  #     #          #       #    # #   #  #    # #      #   #
    ### #######          #       #    # #    #  ####  ###### #    #

    ################################################################

    # We want to read the parameters from a file called seed.nc_param

    # get from the commandline the seed
    parser = argparse.ArgumentParser(
        description=
        "Visualisation of cell structures and non-collinear magentic properties from a CASTEP job."
    )
    parser.add_argument("seed", help="The seed from the CASTEP calculation.")

    parser.add_argument("-v",
                        "--verbose",
                        action="store_true",
                        help="Turn on verbose output.")
    #parser.add_argument("-s","--sym",help="Tolerance for specifying reproduction of atoms outside unit cell (Ang)",default=1)
    parser.add_argument("-i",
                        "--initmag",
                        action="store_true",
                        help="Plot initial magnetic moment vectors.")
    parser.add_argument(
        "-c",
        "--castep",
        action="store_true",
        help=
        "Read <seed>.castep file to determine moments. Only for NCM calculation (BETA)"
    )
    parser.add_argument(
        "-f",
        "--field",
        help=
        "Read formatted potential or density to produce field. Only from VECTOR magnetic run.",
        action="store_true")
    parser.add_argument(
        "-o",
        "--orient",
        help=
        "Orientation of the crystal structure, takes values 'a,b,c,a*,b*,c*'.",
        default="sd")
    parser.add_argument("-B",
                        "--bond",
                        help="Set maximun bond length.",
                        default=2.4)
    parser.add_argument("--save", help="Save image.", action="store_true")
    parser.add_argument("-d", "--delete", help="Delete atoms", nargs='+')
    parser.add_argument("-p",
                        "--position",
                        help="Camera position vector",
                        nargs=6,
                        default=np.array([0., 0., 0., 0., 0., 0.]))
    parser.add_argument(
        "-V",
        "--volumetric",
        help=
        "Provide file with volumetric data: .xsf .den_fmt .pot_fmt accepted.")
    parser.add_argument("-I",
                        "--iso",
                        help="Isosurface value for volumetric data",
                        nargs="*")
    parser.add_argument("--colour",
                        help="HEX code for Isosurface colouring",
                        default="#0000FF")
    parser.add_argument("-z", "--zoom", help="Zoom multiplier", default=1)
    parser.add_argument("-e",
                        "--exclude",
                        help="Exclude atoms outside first unitcell",
                        action="store_false")
    parser.add_argument(
        "-l",
        "--lines",
        help="Disable plotting of field lines of a provoded field line",
        action="store_true")
    parser.add_argument(
        "-P",
        "--plane",
        help=
        "Three points in fractional coordinates to define a plane for B-field.",
        nargs="*")
    parser.add_argument("-w",
                        "--widget",
                        help="Disable interactive widgets",
                        action="store_false")
    parser.add_argument("-s",
                        "--saturation",
                        help="Saturation level for sections.",
                        default=1)
    parser.add_argument("-S",
                        "--spin",
                        help="Plot spin isosurfaces from .den_fmt",
                        action="store_true")
    parser.add_argument("-C",
                        "--charge",
                        help="Plot charge isosurfaces from .den_fmt",
                        action="store_true")
    parser.add_argument(
        "-r",
        "--reduction",
        help=
        "Factor used to reduce the size of atoms, useful for visualising volumetric data without loss of context.",
        default=1.0)
    args = parser.parse_args()
    seed = args.seed
    #do_legend = args.legend
    do_verbose = args.verbose
    do_init_mag = args.initmag
    do_magmom = args.castep
    #do_Bfield=args.B_XC
    field = args.field
    #sym_tol=np.float(args.sym)
    orient = args.orient
    bond_cut = np.float(args.bond)
    save = args.save
    hide = args.delete
    cam_pos = args.position
    z = np.float(args.zoom)
    hide_lines = args.lines
    plane = args.plane
    exclude = args.exclude
    widgets = args.widget
    sat = np.float(args.saturation)
    docharge = args.charge
    dospin = args.spin
    reduction = np.float(args.reduction)
    iso = args.iso

    if plane == None:
        do_plane = False

    elif len(plane) == 9:
        do_plane = True
    elif len(plane) == 0:
        do_plane = True
        widgets = True
    else:
        print("Insufficient points provided for plane")
        sys.exit()

    if iso == None:
        do_iso = False
    elif len(iso) == 0:
        do_iso = True
        iso = None
    elif len(iso) > 1:
        print("Incorrect number of ISO arguments")
        sys.exit()
    else:
        do_iso = True

    # Make Charge the default
    if not docharge and not dospin:
        docharge = True
    if dospin:
        docharge = False

    xsf_file = args.volumetric
    hex_col = args.colour
    sym_tol = bond_cut

    for i in range(len(cam_pos)):
        cam_pos[i] = np.float(cam_pos[i])

    if hide == None:
        hide = [""]

    # Define all the options (and the defaults)
    do_bonds = True
    #do_magmom = False
    #do_Bfield = False
    do_proj = False
    h = 0.5
    #b_xc_file=seed+".B_xc.pot_fmt"

    xsffile = False
    denfile = False
    potfile = False
    noncollinear = False

    # Set iso surface colourmap
    iso_colours = complementary(hex_col)
    colors = list(iso_colours)
    colours = [colors[1], colors[0]]
    cmap_name = "iso_colors"
    cm = LinearSegmentedColormap.from_list(cmap_name, colours, N=2)

    # Open the tkinter window
    window = Tk()
    window.resizable(False, False)
    window.title("NC_CRYST: " + seed)

    output = Text(window)
    output.grid(row=1, column=0, columnspan=4)  #,sticky=N+S+W)

    ##################################################################

    # Define all of the atom positions

    blockPrint()
    #if do_verbose:
    #    print("Parsing .cell")

    cell = io.read(seed + ".cell")
    ccalc = Castep()
    ase_cell = cell.get_cell()
    a, b, c, alpha, beta, gamma = cell.get_cell_lengths_and_angles()
    pos = cell.get_positions()
    prim_pos = pos
    cell.set_calculator(ccalc)
    latt = np.transpose(np.matmul(np.identity(3), cell.get_cell()))

    init_mag = np.zeros((len(pos), 3))
    init_spin = np.zeros((len(pos), 3))
    if do_init_mag:
        try:
            with open(seed + ".cell") as init_cell:
                data = init_cell.readlines()
        except:
            print("No file: " + seed + ".cell")
            sys.exit()
        pos_i = []

        counter = 0
        for i in data:
            if "spin" in i.lower():

                try:
                    i = i.replace("=", " ")
                    i = i.strip("\n")
                except:
                    None

                i = i.split()
                if len(i) > 6:
                    pos_i.append([np.float(j) for j in i[1:4]])
                    init_mag[counter] = [np.float(j) for j in i[5:8]]
                else:
                    pos_i.append([np.float(j) for j in i[1:4]])
                    temp = [0., 0., np.float(i[5])]
                    init_mag[counter] = temp
                counter += 1
        init_spin = np.zeros((len(pos), 3))
        sum_dat = "".join(data).lower()
        for i in range(len(pos_i)):
            for j in range(len(pos)):
                if "positions_frac" in sum_dat:
                    dist = np.sum((np.matmul(latt, pos_i[i]) - pos[j])**2)
                    if dist < 0.00001:
                        init_spin[j] = init_mag[i]
                else:
                    dist = np.sum((pos_i[i] - pos[j])**2)
                    if dist < 0.00001:
                        init_spin[j] = init_mag[i]

    cell.set_velocities(init_spin)

    # Make the supercell (gets all of the positions for me)
    scell = make_supercell(cell, 3 * np.identity(3))

    pos = scell.get_positions()
    atoms = scell.get_atomic_numbers()
    symb = scell.get_chemical_symbols()
    Vol = cell.get_volume()
    enablePrint()
    #print(pos)

    atom_colours = jmol_colors[atoms]
    atom_radii = vdw_radii[atoms]

    inv_latt = np.linalg.inv(np.array(ase_cell.T))
    init_spin = scell.get_velocities()
    #prim_count=0
    #prim_list=np.zeros(len(pos),order="F")

    prim_count, prim_list, pos, keep, n_atoms, bonds, n_bonds = nc_fort.sym_positions(
        bond_cut, len(pos), pos, latt, inv_latt, exclude)

    prim_list = np.array(keep[0:prim_count])

    #sys.exit()

    pos = pos[prim_list]
    atoms = atoms[prim_list]
    #pos=pos[prim_list]
    atom_radii = atom_radii[prim_list]
    atom_colours = atom_colours[prim_list]
    symb = np.array(symb)[prim_list]
    init_spin = init_spin[prim_list]

    unique_atom, atom_counts = np.unique(atoms, return_counts=True)
    atom_label = []
    sort = []

    ###################################### SYMMETRY POSITIONS ###################
    for j in unique_atom:
        for i in range(len(cell.get_atomic_numbers())):
            if atoms[i] == j:
                sort.append(i)

    sort = np.array(sort)

    if do_magmom:

        with open(seed + ".castep") as castep:
            castep_lines = castep.readlines()

        for no, test in enumerate(castep_lines):
            if "Noncollinear Spin Vectors" in test:
                line_no = no
        mom_vec = []
        vec_symb = []
        for i in range(line_no + 4, line_no + 4 + len(prim_pos)):
            cline = castep_lines[i]
            cline = cline.split()
            vec = [np.float(cline[2]), np.float(cline[3]), np.float(cline[4])]
            vec_symb.append(cline[0])
            mom_vec.append(vec)
        #print(mom_vec)
        vec_symb_2 = list(vec_symb)
        mom_vec_2 = list(mom_vec)
        for i in range(len(sort)):
            vec_symb_2[sort[i]] = vec_symb[i]
            mom_vec_2[sort[i]] = mom_vec[i]

        vec_symb = list(vec_symb_2)
        mom_vec = list(mom_vec_2)

    if do_magmom:
        ccell = cell.copy()
        ccell.set_velocities(mom_vec)
        ccell = make_supercell(ccell, 3 * np.identity(3))
        mom_vec = ccell.get_velocities()

    sort = np.argsort(atoms)

    atoms = atoms[sort]
    pos = pos[sort]
    atom_radii = atom_radii[sort]
    atom_colours = atom_colours[sort]
    symb = np.array(symb)[sort]
    if do_init_mag:
        init_spin = init_spin[sort]
    if do_magmom:
        mom_vec = np.array(mom_vec)
        mom_vec = mom_vec[sort]

    for i in atom_counts:
        for j in range(i):
            atom_label.append(j + 1)

    hide_num = []
    for i in range(len(symb)):
        for j in hide:
            if j == symb[i]:
                hide_num.append(i)

    # Time for some plotting

    pv.set_plot_theme("document")
    if save:
        p = pv.Plotter(off_screen=True)
    else:
        p = pv.Plotter()
    p.enable_parallel_projection()

    orientation = [latt[:, 0], latt[:, 1], latt[:, 2]]
    for i in range(3):
        orientation[i] = orientation[i] / 2 * np.linalg.norm(orientation[i])
    arrow_or=pv.Arrow([0,0,0],orientation[0],tip_length=0.25, tip_radius=0.09, tip_resolution=20, shaft_radius=0.03, shaft_resolution=20) +\
        pv.Arrow([0,0,0],orientation[1],tip_length=0.25, tip_radius=0.09, tip_resolution=20, shaft_radius=0.03, shaft_resolution=20) +\
        pv.Arrow([0,0,0],orientation[2],tip_length=0.25, tip_radius=0.09, tip_resolution=20, shaft_radius=0.03, shaft_resolution=20)+\
        pv.Sphere(0.1,[0,0,0])

    test = create_axes_marker2(label_color="black",
                               line_width=4,
                               x_color="r",
                               y_color="g",
                               z_color="b",
                               xlabel="a",
                               ylabel="b",
                               zlabel="c",
                               labels_off=False)
    p.add_orientation_widget(test)

    ########################################################################################################################
    # _______ _       _     _     ______      _             _            _
    #(_______|_)     | |   | |   / _____)    | |           | |      _   (_)
    # _____   _  ____| | _ | |  | /      ____| | ____ _   _| | ____| |_  _  ___  ____   ___
    #|  ___) | |/ _  ) |/ || |  | |     / _  | |/ ___) | | | |/ _  |  _)| |/ _ \|  _ \ /___)
    #| |     | ( (/ /| ( (_| |  | \____( ( | | ( (___| |_| | ( ( | | |__| | |_| | | | |___ |
    #|_|     |_|\____)_|\____|   \______)_||_|_|\____)\____|_|\_||_|\___)_|\___/|_| |_(___/
    ########################################################################################################################

    if xsf_file != None:

        # See what sort of file we have.
        if ".den_fmt" in xsf_file:
            denfile = True
        elif ".pot_fmt" in xsf_file:
            potfile = True
        elif ".xsf" in xsf_file:
            xsffile = True
            nx, ny, nz, mesh_mag = xsf.read_xsf(xsf_file)

        if potfile:
            docharge = False
            dospin = False
        if potfile or denfile:
            with open(xsf_file) as header:
                data = header.readlines()[0:11]

            nx, ny, nz = data[8].split()[0:3]
            nx, ny, nz = int(nx), int(ny), int(nz)

            #latt=np.array([data[3].split()[0:3],data[4].split()[0:3],data[5].split()[0:3]]).astype(float)

            #latt=np.transpose(latt)
            V = np.loadtxt(xsf_file, skiprows=11)

            #n= np.round(h*nz)

            #mask=(V[:,2] == n )
            V3D = V
            #V=V[mask]

            if potfile:
                if np.shape(V3D)[1] == 9:
                    noncollinear = True
                    V1 = V3D[:, 3] + 1j * V3D[:, 4]
                    V2 = V3D[:, 5] + 1j * V3D[:, 6]
                    V3 = V3D[:, 7] + 1j * V3D[:, 8]

                    B_x = np.real((V3 + np.conj(V3)) / 2)
                    B_y = np.real(1j * (V3 - np.conj(V3)) / 2)
                    B_z = np.real((V1 - V2) / 2)
                else:
                    noncollinear = False
                    print("3D data only accepted for NCM .pot_fmt, Exiting...")
                    sys.exit()

            if denfile:
                if np.shape(V3D)[1] == 7:
                    noncollinear = True
                    B_x = V3D[:, 4]
                    B_y = V3D[:, 5]
                    B_z = V3D[:, 6]
                    charge = V3D[:, 3]
                    mesh_mag = np.zeros((nx, ny, nz))
                    if docharge:
                        for i in range(len(V3D)):
                            mesh_mag[int(V3D[i, 0] - 1),
                                     int(V3D[i, 1] - 1),
                                     int(V3D[i, 2] - 1)] = charge[i]
                else:
                    noncollinear = False
                    charge = V3D[:, 3]
                    spin = V3D[:, 4]
                    mesh_mag = np.zeros((nx, ny, nz))
                    if docharge:
                        for i in range(len(V3D)):
                            mesh_mag[int(V3D[i, 0] - 1),
                                     int(V3D[i, 1] - 1),
                                     int(V3D[i, 2] - 1)] = charge[i]
                    if dospin:
                        for i in range(len(V3D)):
                            mesh_mag[int(V3D[i, 0] - 1),
                                     int(V3D[i, 1] - 1),
                                     int(V3D[i, 2] - 1)] = spin[i]
                mesh_mag = mesh_mag / (nx * ny * nz)

        #print("NCM: ",noncollinear)
        #print("POT: ",potfile)
        #print("DEN: ",denfile)
        #print("Charge: ",docharge)
        #print("Spin: ",dospin)
        if noncollinear and not docharge:

            mesh3D_x = np.zeros((nx, ny, nz))
            mesh3D_y = np.zeros((nx, ny, nz))
            mesh3D_z = np.zeros((nx, ny, nz))

            for i in range(len(V3D)):
                mesh3D_x[int(V3D[i, 0] - 1),
                         int(V3D[i, 1] - 1),
                         int(V3D[i, 2] - 1)] = B_x[i]
                mesh3D_y[int(V3D[i, 0] - 1),
                         int(V3D[i, 1] - 1),
                         int(V3D[i, 2] - 1)] = B_y[i]
                mesh3D_z[int(V3D[i, 0] - 1),
                         int(V3D[i, 1] - 1),
                         int(V3D[i, 2] - 1)] = B_z[i]
            mesh_mag = np.sqrt(mesh3D_x**2 + mesh3D_y**2 + mesh3D_z**2)

            # Calcuate the Div
            x_flux = np.sum(mesh3D_x[0:-1, 0, 0]) - np.sum(mesh3D_x[0:-1, -1,
                                                                    -1])
            y_flux = np.sum(mesh3D_x[0, 0:-1, 0]) - np.sum(mesh3D_x[-1, 0:-1,
                                                                    -1])
            z_flux = np.sum(mesh3D_x[0, 0, 0:-1]) - np.sum(mesh3D_x[-1, -1,
                                                                    0:-1])

            flux = x_flux + y_flux + z_flux

            # Play here might cause asymmetry
            Vx = (V[:, 0] - 1) / (nx)
            Vy = (V[:, 1] - 1) / (ny)
            Vz = (V3D[:, 2] - 1) / (nz)

            # Set up for fieldlines calc
            X = np.linspace(np.min(Vx), np.max(Vx), nx, endpoint=True)
            Y = np.linspace(np.min(Vy), np.max(Vy), ny, endpoint=True)
            Z = np.linspace(np.min(Vz), np.max(Vz), nz, endpoint=True)

            f_x = RegularGridInterpolator((X, Y, Z), mesh3D_x)
            f_y = RegularGridInterpolator((X, Y, Z), mesh3D_y)
            f_z = RegularGridInterpolator((X, Y, Z), mesh3D_z)

            #X,Y=np.meshgrid(X,Y)

            n_grid = 175

            ix = int(np.round(a / np.cbrt(Vol / n_grid)))
            iy = int(np.round(b / np.cbrt(Vol / n_grid)))
            iz = int(np.round(c / np.cbrt(Vol / n_grid)))

            if ix == 0:
                ix = 1
            if iy == 0:
                iy = 1
            if iz == 0:
                iz = 1

            #sys.exit()
            #ix,iy,iz=[8,8,3]
            N = 500
            k = 0.5
            x = np.linspace(0.1, 0.9, ix, endpoint=True)
            y = np.linspace(0.1, 0.9, iy, endpoint=True)
            z = np.linspace(0.1, 0.9, iz, endpoint=True)

            gx, gy, gz = np.meshgrid(x, y, z)
            gx = gx.reshape(ix * iy * iz)
            gy = gy.reshape(ix * iy * iz)
            gz = gz.reshape(ix * iy * iz)

            point_grid = np.zeros((ix * iy * iz, 3))
            point_grid[:, 0] = gx
            point_grid[:, 1] = gy
            point_grid[:, 2] = gz

            if field:
                #for m,gpoint in enumerate(point_grid):
                #i,j,k=gpoint
                #    track_r=field_lines(np.array([i,j,k]),track_r)#map[m][0:counter[m]])
                lines, mags, n_track = field_lines(point_grid)
                mags = np.power(mags, 0.45)
                #mags=mags/np.max(mags)

                for k in range(2 * len(point_grid)):
                    if n_track[k] > 1:
                        line_points = lines[k, 0:n_track[k], :]
                        mag = mags[k, 0:n_track[k]]

                        line_points = nc_fort.multmatmul(
                            latt, line_points, len(line_points))
                        mag[0] = mag[1]
                        #mag=mag/np.max(mag)
                        #mag[mag<0.3*np.max(mag)]=0#0.5*np.max(mag)

                        r = 0.008

                        polyLine = pv.PolyData(line_points)
                        polyLine.points = line_points
                        polyLine["scalars"] = np.array(mag)
                        theCell = np.arange(0, len(line_points), dtype=np.int)
                        theCell = np.insert(theCell, 0, len(line_points))
                        polyLine.lines = theCell
                        tube = polyLine.tube(radius=r)
                        #p.add_mesh(tube,color="black", smooth_shading=True)
                        p.add_mesh(
                            tube,
                            show_scalar_bar=False,
                            cmap="gist_yarg",
                            opacity=mag,
                            pickable=False
                        )  #,color="black")#cmap='binary',opacity=mag)

        #Flatten the mesh
        if not xsffile:
            mesh_mag = mesh_mag.flatten('F')
        else:
            mesh_mag = mesh_mag.flatten('C')

        #if not xsffile:
        xs = np.linspace(0, 1, nx, endpoint=True)
        ys = np.linspace(0, 1, ny, endpoint=True)
        zs = np.linspace(0, 1, nz, endpoint=True)

        points = np.zeros((mesh_mag.shape[0], 3))
        counter = 0

        for z in zs:
            for y in ys:
                for x in xs:
                    points[counter, :] = np.matmul(latt, [x, y, z])
                    counter += 1
        sgrid = pv.StructuredGrid()
        sgrid.points = points
        sgrid.dimensions = [nx, ny, nz]

        sgrid.point_arrays["values"] = mesh_mag

        if do_plane:
            # calculate the vectors
            if len(plane) > 1:
                plane = nc_fort.multmatmul(latt, plane, 3)

                v1 = plane[0] - plane[1]
                v2 = plane[0] - plane[2]
                print(v1, v2)
                norm = np.cross(v1, v2)
                if np.linalg.norm(norm) == 0:
                    print("Plane vectors colinear: Exiting...")
                    sys.exit()

                v2 = np.cross(norm, v1)

            cmap = "autumn"  #"plasma"
            if save or len(plane) > 1:
                slice = sgrid.slice(norm, plane[0])
                val = slice.point_arrays["values"]

                p.add_mesh(slice,
                           cmap=cmap,
                           show_scalar_bar=False,
                           clim=(np.min(val), sat * np.max(val)),
                           pickable=False)

            else:
                p.add_mesh_slice(sgrid,
                                 show_scalar_bar=False,
                                 cmap=cmap,
                                 show_edges=False,
                                 implicit=False,
                                 clim=(np.min(mesh_mag),
                                       sat * np.max(mesh_mag)),
                                 pickable=False)

        if do_iso:
            if iso == None:
                iso = 0.05 * np.max([np.max(mesh_mag), abs(np.min(mesh_mag))])
            else:
                iso = np.float(iso[0])

            output.insert(END, "VOLUMETRIC\n")
            output.insert(END, "----------\n")
            output.insert(
                END, "Max: " + str(np.max(mesh_mag)) + " Min: " +
                str(np.min(mesh_mag)) + "\n")
            output.insert(END, "Isovalue: " + str(iso) + "\n")
            output.insert(END, " \n")

            if save or not widgets:
                contours = sgrid.contour([iso, -iso])
                slider_contour = p.add_mesh(contours,
                                            opacity=0.4,
                                            cmap=cm,
                                            smooth_shading=True,
                                            show_scalar_bar=False,
                                            lighting=True,
                                            name="contour",
                                            pickable=False)
            else:

                def cont(iso_val):
                    contours = sgrid.contour([iso_val, -iso_val])
                    slider_contour = p.add_mesh(contours,
                                                opacity=0.4,
                                                cmap=cm,
                                                smooth_shading=True,
                                                show_scalar_bar=False,
                                                lighting=True,
                                                name="contour",
                                                pickable=False)
                    return

                p.add_slider_widget(cont,
                                    rng=(np.min(mesh_mag), np.max(mesh_mag)),
                                    value=iso,
                                    style="modern",
                                    title="Isosurface Value",
                                    pointa=(0.1, 0.9),
                                    pointb=(0.3, 0.9))

    ########################################################################################################################
    ########################################################################################################################
    ########################################################################################################################
    ########################################################################################################################
    ########################################################################################################################
    ########################################################################################################################

    # Add the box
    edges = np.array([[[0., 0., 0.], [0., 0., 1.]], [[0., 0., 0.],
                                                     [0., 1., 0.]],
                      [[0., 0., 0.], [1., 0., 0.]], [[1., 1., 1.],
                                                     [1., 0., 1.]],
                      [[1., 1., 1.], [1., 1., 0.]], [[1., 1., 1.],
                                                     [0., 1., 1.]],
                      [[0., 1., 1.], [0., 0., 1.]], [[0., 1., 1.],
                                                     [0., 1., 0.]],
                      [[1., 1., 0.], [1., 0., 0.]], [[1., 1., 0.],
                                                     [0., 1., 0.]],
                      [[1., 0., 0.], [1., 0., 1.]], [[0., 0., 1.],
                                                     [1., 0., 1.]]])

    for i, main in enumerate(edges):
        for j, sub in enumerate(main):
            edges[i, j] = np.matmul(latt, sub)

    box = pv.Box([0, 1, 0, 1, 0, 1])

    for i in range(0, 12):
        p.add_lines(edges[i], color="black", width=1.5)

    # Do the atoms
    for i, vec in enumerate(pos):

        if i in hide_num:
            continue
        sphere = pv.Sphere(atom_radii[i] / (reduction * 5),
                           vec,
                           theta_resolution=200,
                           phi_resolution=200)
        p.add_mesh(sphere,
                   color=atom_colours[i],
                   specular=0.3,
                   specular_power=30,
                   ambient=0.2,
                   diffuse=1,
                   pickable=True)

    def pick_atom(actor, two):
        if actor != None:
            centre = actor.center  #np.matmul(np.linalg.inv(latt),actor.center)
            for i in range(len(pos)):
                if (np.isclose(pos[i], centre)).all():
                    break
            vec = np.round(np.matmul(np.linalg.inv(latt), pos[i]), 4)
            string = "Atom " + str(i) + ":" + "  " + symb[i] + "  (" + str(
                vec[0]) + " , " + str(vec[1]) + " , " + str(vec[2]) + ")\n"
            #print(string)
            output.insert(END, string)

    #p.enable_cell_picking(through=True,callback=pick_atom,show_message=False,use_mesh=True)
    p.enable_point_picking(callback=pick_atom,
                           use_mesh=True,
                           show_message=False,
                           show_point=False)
    p.window_size = 1000, 1000
    p.store_image = True

    # Do the bonds
    #print("No. atoms: ",len(atoms))
    output.insert(END, "No. atoms: " + str(len(atoms)) + "\n")
    if do_bonds:
        bond_length = []
        bond_name = []
        bond_ind = []
        bonds = np.zeros(len(atoms))
        for atom1 in range(len(pos)):
            for atom2 in range(atom1, len(pos)):

                dist = np.sqrt(np.sum((pos[atom1] - pos[atom2])**2))
                if dist > 0 and dist < bond_cut:
                    bond_length.append(dist)
                    bond_name.append(symb[atom1] + str(atom_label[atom1]) +
                                     "-" + symb[atom2] +
                                     str(atom_label[atom2]))
                    bond_ind.append([atom1, atom2])
                    bonds[atom1] += 1
                    bonds[atom2] += 1

        bond_length = np.array(bond_length)
        valence = valency[atoms - 1]

        over = -valence + bonds
        pop = []
        for atom in range(len(over)):
            if over[atom] <= 0:
                continue

            atom_loc = []
            for n, i in enumerate(bond_ind):
                if atom == i[0] or atom == i[1]:
                    atom_loc.append(n)

            lengths = bond_length[atom_loc]

            loc = np.argsort(lengths)[-int(over[atom]):]

            for i in loc:
                pop.append(atom_loc[i])
        bond_length = list(bond_length)
        pop = np.flip(np.unique(pop))
        for pi in pop:
            bond_length.pop(pi)
            bond_name.pop(pi)
            bond_ind.pop(pi)

        for i, index in enumerate(bond_ind):

            i, j = index
            if i in hide_num or j in hide_num:
                continue
            points = np.array([pos[i], (pos[j] + pos[i]) / 2])
            direc = points[0] - points[1]
            mid = (points[1] + points[0]) / 2
            height = np.sqrt(np.sum((points[0] - points[1])**2))
            cyl = pv.Cylinder(mid, direc, np.min(atom_radii) / 20, height)

            p.add_mesh(cyl, color=atom_colours[i], pickable=False)
            points = np.array([pos[j], (pos[j] + pos[i]) / 2])
            direc = points[0] - points[1]
            mid = (points[1] + points[0]) / 2
            height = np.sqrt(np.sum((points[0] - points[1])**2))
            cyl = pv.Cylinder(mid, direc, np.min(atom_radii) / 20, height)

            p.add_mesh(cyl,
                       color=atom_colours[j],
                       specular=0.3,
                       specular_power=30,
                       ambient=0.2,
                       diffuse=1,
                       pickable=False)
            #for i in bond_name:
            #   print(i)

    if do_magmom:
        for i in range(len(pos)):
            if i in hide_num:
                continue
            temp_pos = pos[i] - np.array(mom_vec[i]) / 2
            arrow = pv.Arrow(temp_pos,
                             mom_vec[i],
                             tip_length=0.15,
                             tip_radius=0.09,
                             tip_resolution=20,
                             shaft_radius=0.04,
                             shaft_resolution=20,
                             scale='auto')
            p.add_mesh(arrow, color='b', pickable=False)

    if do_init_mag:

        for i in range(len(pos)):
            if i in hide_num:
                continue
            temp_pos = pos[i] - np.array(1.5 * init_spin[i]) / 2
            arrow = pv.Arrow(temp_pos,
                             1.5 * init_spin[i],
                             tip_length=0.15,
                             tip_radius=0.09,
                             tip_resolution=20,
                             shaft_radius=0.04,
                             shaft_resolution=20,
                             scale='auto')
            p.add_mesh(arrow, color='g', pickable=False)

    try:
        p.camera.zoom(z)
    except:
        output.insert(END,
                      "Zoom not implemented in this version of PyVista.\n")
        #print("Zoom not implemented in this version of PyVista.")


###############################################################################################

    def screenshot():
        wind = p.window_size
        height = 2560
        p.window_size = [
            height, int(height * p.window_size[1] / p.window_size[0])
        ]
        p.save_graphic(seed + ".eps")

        p.window_size = wind
        if do_verbose:
            output.insert(END, "Graphic saved!\n")

    p.add_key_event("s", screenshot)

    def perpendicular(a):
        b = np.empty_like(a)
        b[0] = -a[1]
        b[1] = a[0]
        return b

    # calculate the reciprocal lattice vectors
    a1 = latt[:, 0]
    a2 = latt[:, 1]
    a3 = latt[:, 2]
    b1 = np.cross(a2, a3)
    b2 = np.cross(a3, a1)
    b3 = np.cross(a1, a2)
    focus = np.matmul(latt, np.array([0.5, 0.5, 0.5]))

    if orient != None:
        cp = p.camera_position

        if orient == 'a':
            o = a3
            vpvec = a1 / np.linalg.norm(a1)
        elif orient == 'a*':
            o = a3
            vpvec = b1 / np.linalg.norm(b1)
        elif orient == 'b':
            o = a3
            vpvec = a2 / np.linalg.norm(a2)
        elif orient == 'b*':
            o = a3
            vpvec = b2 / np.linalg.norm(b2)
        elif orient == 'c':
            o = a2
            vpvec = a3 / np.linalg.norm(a3)
        elif orient == 'c*':
            o = a2
            vpvec = b3 / np.linalg.norm(b3)
        elif orient == "sd":
            o = a3
            #T=[latt[i,i] for i in range(0,3)]
            T = 0.9 * a1 + 0.4 * a2 + 0.1 * a3
            vpvec = T / np.linalg.norm(T)

        vp = focus + 15 * vpvec
        p.camera_position = [vp, focus, o]

    if np.sum(cam_pos) != 0:
        v = (cam_pos[0], cam_pos[1], cam_pos[2])
        o = (cam_pos[3], cam_pos[4], cam_pos[5])
        p.camera_position = [v, focus, o]

    def button_sd():

        o = a3
        T = 0.9 * a1 + 0.4 * a2 + 0.1 * a3
        vpvec = T / np.linalg.norm(T)
        vp = focus + 15 * vpvec
        p.camera_position = [vp, focus, o]

    def button_a():
        o = a3
        vpvec = a1 / np.linalg.norm(a1)

        vp = focus + 15 * vpvec
        p.camera_position = [vp, focus, o]

    def button_b():

        o = a3
        vpvec = a2 / np.linalg.norm(a2)
        vp = focus + 15 * vpvec
        p.camera_position = [vp, focus, o]

    def button_c():

        o = a2
        vpvec = a3 / np.linalg.norm(a3)

        vp = focus + 15 * vpvec
        p.camera_position = [vp, focus, o]

    end = time.perf_counter()
    p.add_key_event("o", button_sd)
    p.add_key_event("a", button_a)
    p.add_key_event("b", button_b)
    p.add_key_event("c", button_c)

    output.insert(END, "Time: " + str(np.round(end - start, 4)) + " s\n")
    if save:
        p.window_size = [5000, 5000]
        #p.save_graphic(seed+".pdf")
        p.show(title=seed, screenshot=seed + ".png")
    else:

        p.show(title=seed)

    if do_verbose:
        print("Final Camera Position:")
        print(p.camera_position[0][0], p.camera_position[0][1],
              p.camera_position[0][2], p.camera_position[2][0],
              p.camera_position[2][1], p.camera_position[2][2])

    sys.exit()

    window.mainloop()
Esempio n. 10
0
    traceback.print_exc()
    print(e)
    assert False, 'Castep calculator module could not be loaded'

try:
    __import__(ase_castep_dir + ".io.castep")
except Exception, e:
    assert False, 'Castep io module could not be loaded'

tmp_dir = tempfile.mkdtemp()
cwd = os.getcwd()

from ase.calculators.castep import Castep

try:
    c = Castep(directory=tmp_dir, label='test_label')
except Exception, e:
    traceback.print_exc()
    print(e)
    assert False, 'Could not instantiate castep calculator'

try:
    c.xc_functional = 'PBE'
except Exception, e:
    traceback.print_exc()
    print(e)
    assert False, 'Setting xc_functional  failed'

import ase.lattice.cubic
lattice = ase.lattice.cubic.BodyCenteredCubic('Li')