예제 #1
0
def coordgroups_reduced_rc_to_unitcellsites(coordgroups,
                                            basis,
                                            hall_symbol,
                                            backends=[
                                                'cif2cell', 'internal', 'ase'
                                            ]):
    # TODO: Make own, or use cif2cell to generate reduced unitcell
    if hall_symbol == 'P 1':
        return UnitcellSites.create(
            reduced_coordgroups=coordgroups), Cell.create(basis)

    for backend in backends:
        if backend == 'internal':
            newcoordgroups, newcell = internal_coordgroups_reduced_rc_to_unitcellsites(
                coordgroups, basis, hall_symbol)
            return UnitcellSites.create(
                reduced_coordgroups=newcoordgroups), Cell.create(basis)

        if backend == 'cif2cell':
            try:
                from httk.external import cif2cell_ext
                return cif2cell_ext.coordgroups_reduced_rc_to_unitcellsites(
                    coordgroups, basis, hall_symbol)
            except ImportError:
                pass
        if backend == 'ase':
            try:
                from httk.external import ase_glue
                return ase_glue.coordgroups_reduced_rc_to_unitcellsites(
                    coordgroups, basis, hall_symbol)
            except ImportError:
                raise
                pass
    raise Exception(
        "structure_to_p1structure: None of the available backends available.")
예제 #2
0
def cubic_supercell_transformation(structure,
                                   tolerance=None,
                                   max_search_cells=1000):
    # Note: a better name for tolerance is max_extension or similar, it is not really a tolerance, it regulates the maximum number of repetitions of the primitive cell
    # in any directions to reach the soughts supercell

    if tolerance is None:
        prim_cell = structure.uc_cell.basis
        inv = prim_cell.inv().simplify()
        transformation = (inv * inv.denom).simplify()
    else:
        maxtol = max(int(FracVector.use(tolerance)), 2)
        bestlen = None
        bestortho = None
        besttrans = None
        #TODO: This loop may be possible to do with fewer iterations, since I suppose the only thing that
        #matter is the prime factors?
        for tol in range(1, maxtol):
            prim_cell = structure.uc_cell.basis
            prim_cell = structure.uc_cell.basis
            approxinv = prim_cell.inv().set_denominator(tol).simplify()
            if approxinv[0] == [0, 0, 0] or approxinv[1] == [
                    0, 0, 0
            ] or approxinv[2] == [0, 0, 0]:
                continue
            transformation = (approxinv * approxinv.denom).simplify()
            try:
                cell = Cell.create(basis=transformation * prim_cell)
            except Exception:
                continue
            ortho = (abs(cell.niggli_matrix[1][0]) +
                     abs(cell.niggli_matrix[1][1]) +
                     abs(cell.niggli_matrix[1][2])).simplify()
            equallen = abs(cell.niggli_matrix[0][0] - cell.niggli_matrix[0][1]
                           ) + abs(cell.niggli_matrix[0][0] -
                                   cell.niggli_matrix[0][2])
            if ortho == 0 and equallen == 0:
                # Already perfectly cubic, use this
                besttrans = transformation
                break
            elif bestlen is None or not (bestortho < ortho
                                         and bestlen < equallen):
                bestlen = equallen
                bestortho = ortho
                besttrans = transformation
            elif besttrans == None:
                bestlen = equallen
                bestortho = ortho
                besttrans = transformation

        transformation = besttrans

    if transformation == None:
        raise Exception(
            "Not possible to find a cubic supercell with this limitation of number of repeated cell (increase tolerance.)"
        )

    return transformation
예제 #3
0
def transform(structure, transformation, max_search_cells=20, max_atoms=1000):

    transformation = FracVector.use(transformation).simplify()
    #if transformation.denom != 1:
    #    raise Exception("Structure.transform requires integer transformation matrix")

    old_cell = structure.uc_cell
    new_cell = Cell.create(basis=transformation * old_cell.basis)
    conversion_matrix = (old_cell.basis * new_cell.inv).simplify()

    volume_ratio = abs(
        (new_cell.basis.det() / abs(old_cell.basis.det()))).simplify()
    seek_counts = [
        int((volume_ratio * x).simplify()) for x in structure.uc_counts
    ]
    #print("HMM",(new_cell.basis.det()/old_cell.basis.det()).simplify())
    #print("SEEK_COUNTS",seek_counts, volume_ratio, structure.uc_counts, transformation)
    total_seek_counts = sum(seek_counts)
    if total_seek_counts > max_atoms:
        raise Exception("Structure.transform: more than " + str(max_atoms) +
                        " needed. Change limit with max_atoms parameter.")

    #if max_search_cells != None and maxvec[0]*maxvec[1]*maxvec[2] > max_search_cells:
    #    raise Exception("Very obtuse angles in cell, to search over all possible lattice vectors will take a very long time. To force, set max_search_cells = None when calling find_prototypeid()")

    ### Collect coordinate list of all sites inside the new cell
    coordgroups = structure.uc_reduced_coordgroups
    extendedcoordgroups = [[] for x in range(len(coordgroups))]

    if max_search_cells is not None:
        max_search = [max_search_cells, max_search_cells, max_search_cells]
    else:
        max_search = None

    for offset in breath_first_idxs(dim=3, end=max_search, negative=True):
        #print("X",offset, seek_counts)
        for idx in range(len(coordgroups)):
            coordgroup = coordgroups[idx]
            newcoordgroup = coordgroup + FracVector([offset] * len(coordgroup))
            new_reduced = newcoordgroup * conversion_matrix
            #print("NEW:",FracVector.use(new_reduced).to_floats(),)
            new_reduced = [
                x for x in new_reduced if x[0] >= 0 and x[1] >= 0 and x[2] >= 0
                and x[0] < 1 and x[1] < 1 and x[2] < 1
            ]
            extendedcoordgroups[idx] += new_reduced
            c = len(new_reduced)
            seek_counts[idx] -= c
            total_seek_counts -= c
            #print("ADD",str(c))
            if seek_counts[idx] < 0:
                #print("X",offset, seek_counts)
                raise Exception(
                    "Structure.transform safety check error, internal error: too many atoms in supercell."
                )
        if total_seek_counts == 0:
            break
    else:
        raise Exception(
            "Very obtuse angles in cell, to search over all possible lattice vectors will take a very long time. To force, set max_search_cells = None when calling find_prototypeid()"
        )

    return structure.create(uc_reduced_coordgroups=extendedcoordgroups,
                            uc_basis=new_cell.basis,
                            assignments=structure.assignments)
예제 #4
0
def build_supercell_old(structure, transformation, max_search_cells=1000):
    ### New basis matrix, note: works in units of old_cell.scale to avoid floating point errors
    #print("BUILD SUPERCELL",structure.uc_sites.cell.basis.to_floats(), repetitions)

    transformation = FracVector.use(transformation).simplify()
    if transformation.denom != 1:
        raise Exception(
            "Structure.build_supercell requires integer transformation matrix")

    old_cell = structure.uc_sites.cell.get_normalized_longestvec()
    new_cell = Cell.create(basis=transformation * old_cell.basis)
    #conversion_matrix = (new_cell.inv*old_cell.basis).T().simplify()
    conversion_matrix = (old_cell.basis * new_cell.inv).T().simplify()

    volume_ratio = (new_cell.basis.det() /
                    abs(old_cell.basis.det())).simplify()

    # Generate the reduced (old cell) coordinates of each corner in the new cell
    # This determines how far we must loop the old cell to cover all these corners
    nb = new_cell.basis
    corners = FracVector.create([(0, 0, 0), nb[0], nb[1], nb[2], nb[0] + nb[1],
                                 nb[0] + nb[2], nb[1] + nb[2],
                                 nb[0] + nb[1] + nb[2]])
    reduced_corners = corners * (old_cell.basis.inv().T())

    maxvec = [
        int(reduced_corners[:, 0].max()) + 2,
        int(reduced_corners[:, 1].max()) + 2,
        int(reduced_corners[:, 2].max()) + 2
    ]
    minvec = [
        int(reduced_corners[:, 0].min()) - 2,
        int(reduced_corners[:, 1].min()) - 2,
        int(reduced_corners[:, 2].min()) - 2
    ]

    if max_search_cells is not None and maxvec[0] * maxvec[1] * maxvec[
            2] > max_search_cells:
        raise Exception(
            "Very obtuse angles in cell, to search over all possible lattice vectors will take a very long time. To force, set max_search_cells = None when calling find_prototypeid()"
        )

    ### Collect coordinate list of all sites inside the new cell
    coordgroups = structure.uc_reduced_coordgroups
    extendedcoordgroups = [[] for x in range(len(coordgroups))]
    for idx in range(len(coordgroups)):
        coordgroup = coordgroups[idx]
        for i in range(minvec[0], maxvec[0]):
            for j in range(minvec[1], maxvec[1]):
                for k in range(minvec[2], maxvec[2]):
                    newcoordgroup = coordgroup + FracVector(
                        ((i, j, k), ) * len(coordgroup))
                    new_reduced = newcoordgroup * conversion_matrix
                    new_reduced = [
                        x for x in new_reduced if x[0] >= 0 and x[1] >= 0
                        and x[2] >= 0 and x[0] < 1 and x[1] < 1 and x[2] < 1
                    ]
                    extendedcoordgroups[idx] += new_reduced

    # Safety check for avoiding bugs that change the ratio of atoms
    new_counts = [len(x) for x in extendedcoordgroups]
    for i in range(len(structure.uc_counts)):
        if volume_ratio * structure.uc_counts[i] != new_counts[i]:
            print("Volume ratio:", float(volume_ratio), volume_ratio)
            print("Extended coord groups:",
                  FracVector.create(extendedcoordgroups).to_floats())
            print("Old counts:", structure.uc_counts,
                  structure.assignments.symbols)
            print("New counts:", new_counts, structure.assignments.symbols)
            #raise Exception("Structure.build_supercell safety check failure. Volume changed by factor "+str(float(volume_ratio))+", but atoms in group "+str(i)+" changed by "+str(float(new_counts[i])/float(structure.uc_counts[i])))

    return structure.create(uc_reduced_coordgroups=extendedcoordgroups,
                            basis=new_cell.basis,
                            assignments=structure.assignments,
                            cell=structure.uc_cell)
예제 #5
0
def orthogonal_supercell_transformation(structure,
                                        tolerance=None,
                                        ortho=[True, True, True]):
    # TODO: How to solve for exact orthogonal cell?
    if tolerance is None:
        prim_cell = structure.uc_cell.basis
        print("Starting cell:", prim_cell)
        inv = prim_cell.inv().simplify()
        if ortho[0]:
            row0 = (inv[0] / max(inv[0])).simplify()
        else:
            row0 = [1, 0, 0]
        if ortho[1]:
            row1 = (inv[1] / max(inv[1])).simplify()
        else:
            row1 = [0, 1, 0]
        if ortho[2]:
            row2 = (inv[2] / max(inv[2])).simplify()
        else:
            row2 = [0, 0, 1]
        transformation = FracVector.create(
            [row0 * row0.denom, row1 * row1.denom, row2 * row2.denom])
    else:
        maxtol = max(int(FracVector.use(tolerance)), 2)
        bestval = None
        besttrans = None
        for tol in range(1, maxtol):
            prim_cell = structure.uc_cell.basis
            inv = prim_cell.inv().set_denominator(tol).simplify()
            if inv[0] == [0, 0, 0] or inv[1] == [0, 0, 0
                                                 ] or inv[2] == [0, 0, 0]:
                continue
            absinv = abs(inv)
            if ortho[0]:
                row0 = (inv[0] / max(absinv[0])).simplify()
            else:
                row0 = [1, 0, 0]
            if ortho[1]:
                row1 = (inv[1] / max(absinv[1])).simplify()
            else:
                row1 = [0, 1, 0]
            if ortho[2]:
                row2 = (inv[2] / max(absinv[2])).simplify()
            else:
                row2 = [0, 0, 1]
            transformation = FracVector.create(
                [row0 * row0.denom, row1 * row1.denom, row2 * row2.denom])
            try:
                cell = Cell.create(basis=transformation * prim_cell)
            except Exception:
                continue
            maxval = (abs(cell.niggli_matrix[1][0]) +
                      abs(cell.niggli_matrix[1][1]) +
                      abs(cell.niggli_matrix[1][2])).simplify()
            if maxval == 0:
                besttrans = transformation
                break
            if bestval is None or maxval < bestval:
                bestval = maxval
                besttrans = transformation
        transformation = besttrans

    if transformation == None:
        raise Exception(
            "Not possible to find a othogonal supercell with this limitation of number of repeated cell (increase tolerance.)"
        )

    return transformation
예제 #6
0
    def create(cls,
               structure=None,
               rc_cell=None,
               rc_basis=None,
               rc_lengths=None,
               rc_angles=None,
               rc_niggli_matrix=None,
               rc_metric=None,
               rc_a=None,
               rc_b=None,
               rc_c=None,
               rc_alpha=None,
               rc_beta=None,
               rc_gamma=None,
               rc_sites=None,
               rc_reduced_coordgroups=None,
               rc_cartesian_coordgroups=None,
               rc_reduced_coords=None,
               rc_cartesian_coords=None,
               rc_reduced_occupationscoords=None,
               rc_cartesian_occupationscoords=None,
               rc_occupancies=None,
               rc_counts=None,
               wyckoff_symbols=None,
               multiplicities=None,
               spacegroup=None,
               hall_symbol=None,
               spacegroupnumber=None,
               setting=None,
               rc_scale=None,
               rc_scaling=None,
               rc_volume=None,
               vol_per_atom=None,
               assignments=None,
               periodicity=None,
               nonperiodic_vecs=None,
               refs=None,
               tags=None):
        """
        A Structure represents N sites of, e.g., atoms or ions, in any periodic or non-periodic arrangement.

        This is a swiss-army-type constructor that allows a selection between a large number of optional arguments.

        To create a new structure, three primary components are:
           - cell: defines the basis vectors in which reduced coordinates are expressed, and the
                   unit of repetition (*if* the structure has any periodicity - see the 'periodicity' parameter)
           - assignments: a list of 'things' (atoms, ions, etc.) that goes on the sites in the structure
           - sites: a sensible representation of location / coordinates of the sites.

        Note: `rc_`-prefixes are consistently enforced for any quantity that would be different in a UnitcellStructure. This is to
        allow for painless change between the various structure-type objects without worrying about accidently using
        the wrong type of sites object.

        Input parameters:

           - ONE OF: 'cell'; 'basis', 'length_and_angles'; 'niggli_matrix'; 'metric'; all of: a,b,c, alpha, beta, gamma.
             (cell requires a Cell object or a very specific format, so unless you know what you are doing, use one of the others.)

           - ONE OF: 'assignments', 'atomic_numbers', 'occupancies'
             (assignments requires an Assignments object or a sequence.), occupations repeats similar site assignments as needed

           - ONE OF: 'rc_sites', 'rc_coords' (IF rc_occupations OR rc_counts are also given),
             'uc_coords' (IF uc_occupations OR uc_counts are also given)
             'rc_B_C', where B=reduced or cartesian, C=coordgroups, coords, or occupationscoords

             Notes:

                  - occupationscoords may differ from coords by *order*, since giving occupations as, e.g., ['H','O','H'] does not necessarily
                    have the same order of the coordinates as the format of counts+coords as (2,1), ['H','O'].

                  - rc_sites and uc_sites requires a Sites object or a very specific format, so unless you know what you are doing,
                    use one of the others.)

           - ONE OF: scale or volume:
                scale = multiply the basis vectors with this scaling factor,
                volume = the representative (conventional) cell volume (overrides 'scale' if both are given)
                volume_per_atom = cell volume / number of atoms

           - ONE OF periodicity or nonperiodic_vecs

        See help(Structure) for more information on the data format of all these data representations.
        """
        if structure is not None:
            return cls.use(structure)

        if isinstance(spacegroup, Spacegroup):
            hall_symbol = spacegroup.hall_symbol
        else:
            try:
                spacegroupobj = Spacegroup.create(
                    spacegroup=spacegroup,
                    hall_symbol=hall_symbol,
                    spacegroupnumber=spacegroupnumber,
                    setting=setting)
                hall_symbol = spacegroupobj.hall_symbol
            except Exception:
                spacegroupobj = None
                hall_symbol = None

        if rc_cell is not None:
            rc_cell = Cell.use(rc_cell)
        else:
            try:
                rc_cell = Cell.create(cell=rc_cell,
                                      basis=rc_basis,
                                      metric=rc_metric,
                                      niggli_matrix=rc_niggli_matrix,
                                      a=rc_a,
                                      b=rc_b,
                                      c=rc_c,
                                      alpha=rc_alpha,
                                      beta=rc_beta,
                                      gamma=rc_gamma,
                                      lengths=rc_lengths,
                                      angles=rc_angles,
                                      scale=rc_scale,
                                      scaling=rc_scaling,
                                      volume=rc_volume)
            except Exception:
                rc_cell = None

        if rc_sites is not None:
            rc_sites = RepresentativeSites.use(rc_sites)
        else:
            if rc_reduced_coordgroups is None and \
                    rc_reduced_coords is None and \
                    rc_occupancies is not None:
                # Structure created by occupationscoords and occupations, this is a slightly tricky representation
                if rc_reduced_occupationscoords is not None:
                    assignments, rc_reduced_coordgroups = occupations_and_coords_to_assignments_and_coordgroups(
                        rc_reduced_occupationscoords, rc_occupancies)

            if rc_reduced_coordgroups is not None or \
                    rc_reduced_coords is not None:

                try:
                    rc_sites = RepresentativeSites.create(
                        reduced_coordgroups=rc_reduced_coordgroups,
                        reduced_coords=rc_reduced_coords,
                        counts=rc_counts,
                        hall_symbol=hall_symbol,
                        periodicity=periodicity,
                        wyckoff_symbols=wyckoff_symbols,
                        multiplicities=multiplicities,
                        occupancies=rc_occupancies)
                except Exception as e:
                    raise
                    #print("Ex",e)
                    rc_sites = None
            else:
                rc_sites = None

        if rc_sites is None and rc_reduced_coordgroups is None and \
                rc_reduced_coords is None and rc_reduced_occupationscoords is None:
            # Cartesian coordinate input must be handled here in structure since scalelessstructure knows nothing about cartesian coordinates...
            if rc_cartesian_coordgroups is None and rc_cartesian_coords is None and \
                    rc_occupancies is not None and rc_cartesian_occupationscoords is not None:
                assignments, rc_cartesian_coordgroups = occupations_and_coords_to_assignments_and_coordgroups(
                    rc_cartesian_occupationscoords, rc_occupancies)

            if rc_cartesian_coords is not None and rc_cartesian_coordgroups is None:
                rc_cartesian_coordgroups = coords_and_counts_to_coordgroups(
                    rc_cartesian_coords, rc_counts)

            if rc_cell is not None:
                rc_reduced_coordgroups = coordgroups_cartesian_to_reduced(
                    rc_cartesian_coordgroups, rc_cell)

        if rc_sites is None:
            raise Exception(
                "Structure.create: representative sites specifications invalid."
            )

        if assignments is not None:
            assignments = Assignments.use(assignments)

        if assignments is None or (rc_sites is None or rc_cell is None
                                   or hall_symbol is None):
            raise Exception(
                "Structure.create: not enough information given to create a structure object."
            )

        new = cls(assignments=assignments, rc_sites=rc_sites, rc_cell=rc_cell)

        return new
예제 #7
0
    def create(cls,
               structure=None,
               uc_cell=None,
               uc_basis=None,
               uc_lengths=None,
               uc_angles=None,
               uc_niggli_matrix=None,
               uc_metric=None,
               uc_a=None,
               uc_b=None,
               uc_c=None,
               uc_alpha=None,
               uc_beta=None,
               uc_gamma=None,
               uc_sites=None,
               uc_reduced_coordgroups=None,
               uc_cartesian_coordgroups=None,
               uc_reduced_coords=None,
               uc_cartesian_coords=None,
               uc_reduced_occupationscoords=None,
               uc_cartesian_occupationscoords=None,
               uc_occupancies=None,
               uc_counts=None,
               uc_scale=None,
               uc_scaling=None,
               uc_volume=None,
               volume_per_atom=None,
               assignments=None,
               periodicity=None,
               nonperiodic_vecs=None,
               other_reps=None,
               refs=None,
               tags=None):
        """
        A FullStructure represents N sites of, e.g., atoms or ions, in any periodic or non-periodic arrangement, where the positions
        of all cites are given (as opposed to a set of unique sites + symmetry operations).

        This is a swiss-army-type constructor that allows several different ways to create a FullStructure object.

        To create a new structure, three primary components are:

           - cell: defines the basis vectors in which reduced coordinates are expressed, and the
             unit of repetition (*if* the structure has any periodicity - see the 'periodicity' parameter)

           - assignments: a list of 'things' (atoms, ions, etc.) that goes on the sites in the structure

           - sites: a sensible representation of location / coordinates of the sites.

        Note: `uc_`-prefixes are consistently enforced for any quantity that would be different in a UniqueSitesStructure. This is to
        allow for painless change between the various structure-type objects without worrying about accidently using
        the wrong type of sites object.

        Note: see help(Structure) for parameter naming conventions, i.e., what type of object is expected given a parameter name.

        Input parameters:

           - ONE OF: 'uc_cell'; 'uc_basis', 'uc_length_and_angles'; 'uc_niggli_matrix'; 'uc_metric';
             all of: uc_a,uc_b,uc_c, uc_alpha, uc_beta, uc_gamma.
             (cell requires a Cell object or a very specific format, so unless you know what you are doing, use one of the others.)

           - ONE OF: 'uc_assignments', 'uc_atomic_numbers', 'uc_occupations'
             (uc_assignments requires an Assignments object or a sequence.), uc_occupations repeats similar site assignments as needed

           - ONE OF: 'uc_sites', 'uc_coords' (IF uc_occupations OR uc_counts are also given), or
             'uc_B_C', where B=reduced or cartesian, C=coordgroups, coords, or occupationscoords

             Notes:

                  - occupationscoords may differ from coords by *order*, since giving occupations as, e.g., ['H','O','H'] does not necessarily
                    have the same order of the coordinates as the format of counts+coords as (2,1), ['H','O'].

                  - uc_sites requires a Sites object or a python list on a very specific format, (so unless you know what you are doing,
                    use one of the others.)

           - ONE OF: uc_scale, uc_volume, or volume_per_atom:
                scale = multiply the basis vectors with this scaling factor,
                volume = the unit cell volume (overrides 'scale' if both are given)
                volume_per_atom = cell volume / number of atoms

           - ONE OF periodicity or nonperiodic_vecs

        """
        if structure is not None:
            UnitcellStructure.use(structure)

        #TODO: Handle that vollume_per_atom is given instead, move this block below sorting out sites and if uc_volume is not set,
        #calculate a new volume
        if uc_cell is not None:
            Cell.use(uc_cell)
        else:
            uc_cell = Cell.create(cell=uc_cell,
                                  basis=uc_basis,
                                  metric=uc_metric,
                                  niggli_matrix=uc_niggli_matrix,
                                  a=uc_a,
                                  b=uc_b,
                                  c=uc_c,
                                  alpha=uc_alpha,
                                  beta=uc_beta,
                                  gamma=uc_gamma,
                                  lengths=uc_lengths,
                                  angles=uc_angles,
                                  scale=uc_scale,
                                  scaling=uc_scaling,
                                  volume=uc_volume)

        if uc_sites is not None:
            uc_sites = UnitcellSites.use(uc_sites)
        else:
            if uc_reduced_coordgroups is None and \
                    uc_reduced_coords is None and \
                    uc_occupancies is not None:
                # Structure created by occupationscoords and occupations, this is a slightly tricky representation
                if uc_reduced_occupationscoords is not None:
                    assignments, uc_reduced_coordgroups = occupations_and_coords_to_assignments_and_coordgroups(
                        uc_reduced_occupationscoords, uc_occupancies)

            if uc_reduced_coordgroups is not None or \
                    uc_reduced_coords is not None:

                try:
                    uc_sites = UnitcellSites.create(
                        reduced_coordgroups=uc_reduced_coordgroups,
                        reduced_coords=uc_reduced_coords,
                        counts=uc_counts,
                        periodicity=periodicity,
                        occupancies=uc_occupancies)
                except Exception:
                    uc_sites = None

            else:
                uc_sites = None

        if uc_sites is None and uc_reduced_coordgroups is None and \
                uc_reduced_coords is None and uc_reduced_occupationscoords is None:
            # Cartesian coordinate input must be handled here in structure since scalelessstructure knows nothing about cartesian coordinates...
            if uc_cartesian_coordgroups is None and uc_cartesian_coords is None and \
                    uc_occupancies is not None and uc_cartesian_occupationscoords is not None:
                assignments, uc_cartesian_coordgroups = occupations_and_coords_to_assignments_and_coordgroups(
                    uc_cartesian_occupationscoords, uc_occupancies)

            if uc_cartesian_coords is not None and uc_cartesian_coordgroups is None:
                uc_cartesian_coordgroups = coords_and_counts_to_coordgroups(
                    uc_cartesian_coords, uc_counts)

            if uc_cell is not None:
                uc_reduced_coordgroups = coordgroups_cartesian_to_reduced(
                    uc_cartesian_coordgroups, uc_cell)

        if assignments is not None:
            assignments = Assignments.use(assignments)

        if uc_sites is None:
            raise Exception(
                "Structure.create: not enough information for information about sites."
            )

        new = cls(assignments=assignments, uc_sites=uc_sites, uc_cell=uc_cell)

        return new