Esempio n. 1
0
def read_json(filedesc, **kwargs):
    """Reads a JSON-style file with i-pi style comments and creates an Atoms and Cell object.

    Args:
        filedesc: An open readable file object from a json formatted file with i-PI header comments.

    Returns:
        An Atoms object with the appropriate atom labels, masses and positions.
        A Cell object.
    """

    try:
        line = json.loads(filedesc.readline())
    except ValueError:
        raise EOFError("The file descriptor hit EOF.")
    atoms = Atoms(line[0])
    atoms.q = np.asarray(line[8])
    atoms.names = np.asarray(line[9], dtype='|S4')
    atoms.m = np.asarray(map(Elements.mass, atoms.names))

    a = float(line[1])
    b = float(line[2])
    c = float(line[3])
    alpha = float(line[4]) * np.pi / 180
    beta = float(line[5]) * np.pi / 180
    gamma = float(line[6]) * np.pi / 180
    h = mt.abc2h(a, b, c, alpha, beta, gamma)
    cell = Cell(h)

    return {"atoms": atoms, "cell": cell}
Esempio n. 2
0
def create_random_xyz_traj_to_write(request):

    natoms, frames, comment, expected_cell, precision = request.param

    a, b, c, alpha, beta, gamma = mt.h2abc_deg(expected_cell)

    fmt_header = "# CELL(abcABC): %10.5f  %10.5f  %10.5f  %10.5f  %10.5f  %10.5f  %s"

    comment = fmt_header % (a, b, c, alpha, beta, gamma, comment)

    filedesc, xyz, atom_names = xyz_gen.xyz_traj_filedesc(natoms, frames, comment)
    filedesc.seek(0)

    masses = [Elements.mass(_am) for _am in atom_names]

    cell_list = []
    atoms_list = []

    for _fr in xrange(frames):
        cell = Cell(expected_cell)
        atoms = Atoms(natoms)
        atoms.q[:] = xyz[_fr * natoms * 3:(_fr + 1) * natoms * 3]
        atoms.names = atom_names[_fr * natoms:(_fr + 1) * natoms]
        atoms.m[:] = masses[_fr * natoms:(_fr + 1) * natoms]
        atoms_list.append(atoms)
        cell_list.append(cell)

    return (filedesc, atoms_list, cell_list, comment, precision)
Esempio n. 3
0
def process_units(comment, cell, data, names, masses, natoms, dimension="automatic", units="automatic", cell_units="automatic", mode="xyz"):
    """Convert the data in the file according to the units written in the i-PI format.

    Args:
        comment:
        cell:
        data:
        names:
        masses:
        output:

    Returns:

    """
    dimension, units, cell_units = auto_units(comment, dimension, units, cell_units, mode)

    info("Interpreting input with dimension %s, units %s and cell units %s" % (dimension, units, cell_units), verbosity.high)

    # Units transformation
    cell *= unit_to_internal('length', cell_units, 1)  # cell units transformation
    data *= unit_to_internal(dimension, units, 1)  # units transformation

    # Return data as i-PI structures
    cell = Cell(cell)
    atoms = Atoms(natoms)
    atoms.q[:] = data
    atoms.names[:] = names
    atoms.m[:] = masses

    return {
        "atoms": atoms,
      "cell": cell,
    }
Esempio n. 4
0
def main(prefix, suffix="pos", outprefix="fixcom"):

    ipos = []
    imode = []
    for filename in sorted(glob.glob(prefix + "." + suffix + "*")):
        imode.append(filename.split(".")[-1])
        ipos.append(open(filename, "r"))

    nbeads = len(ipos)
    natoms = 0
    ifr = 0

    lout = []
    for b in range(nbeads):
        # zero-padded bead number
        padb = ("%0" + str(int(1 + np.floor(np.log(nbeads) / np.log(10)))) + "d") % (b)
        lout.append(
            open(
                prefix + "." + outprefix + "." + suffix + "_" + padb + "." + imode[b],
                "a",
            )
        )

    while True:
        allbeads = []
        for i in range(nbeads):
            try:

                poscell = read_file(imode[i], ipos[i])
                cell = poscell["cell"]
                pos = poscell["atoms"]
                allbeads.append(pos)
                if natoms == 0:
                    natoms = pos.natoms
                    atoms = Atoms(natoms)

                atoms.q += pos.q
                atoms.names = pos.names
                atoms.m += pos.m
            except EOFError:  # finished reading files
                for ib in range(nbeads):
                    lout[ib].close()
                sys.exit(0)
        atoms.q /= nbeads
        atoms.m /= nbeads
        com = np.zeros(3)
        tm = 0
        for i in range(atoms.natoms):
            com += atoms.m[i] * atoms.q[3 * i : 3 * (i + 1)]
            tm += atoms.m[i]
        com /= tm
        for ib in range(nbeads):
            for i in range(allbeads[ib].natoms):
                allbeads[ib].q[3 * i : 3 * (i + 1)] -= com
            print_file(imode[ib], allbeads[ib], cell, filedesc=lout[ib])
        atoms.q[:] = 0.0
        atoms.m[:] = 0.0
        ifr += 1
Esempio n. 5
0
def read_pdb(filedesc):
    """Takes a pdb-style file and creates an Atoms and Cell object.

   Args:
      filedesc: An open readable file object from a pdb formatted file.

   Returns:
      An Atoms object with the appropriate atom labels, masses and positions,
      and a Cell object with the appropriate cell dimensions and an estimate
      of a reasonable cell mass.
   """

    header = filedesc.readline()
    if "TITLE" in header:
        header = filedesc.readline()  # skip the comment field
    if header == "":
        raise EOFError("End of file or empty header in PDB file")

    a = float(header[6:15])
    b = float(header[15:24])
    c = float(header[24:33])
    alpha = float(header[33:40])
    beta = float(header[40:47])
    gamma = float(header[47:54])
    alpha *= np.pi / 180.0
    beta *= np.pi / 180.0
    gamma *= np.pi / 180.0
    h = mt.abc2h(a, b, c, alpha, beta, gamma)
    cell = Cell(h)

    natoms = 0
    body = filedesc.readline()
    qatoms = []
    names = []
    masses = []
    while (body.strip() != "" and body.strip() != "END"):
        natoms += 1
        name = body[12:16].strip()
        names.append(name)
        masses.append(Elements.mass(name))
        x = float(body[31:39])
        y = float(body[39:47])
        z = float(body[47:55])
        qatoms.append(x)
        qatoms.append(y)
        qatoms.append(z)

        body = filedesc.readline()

    atoms = Atoms(natoms)
    atoms.q = np.asarray(qatoms)
    atoms.names = np.asarray(names, dtype='|S4')
    atoms.m = np.asarray(masses)

    return atoms, cell
Esempio n. 6
0
def read_pdb(filedesc):
   """Takes a pdb-style file and creates an Atoms and Cell object.

   Args:
      filedesc: An open readable file object from a pdb formatted file.

   Returns:
      An Atoms object with the appropriate atom labels, masses and positions,
      and a Cell object with the appropriate cell dimensions and an estimate
      of a reasonable cell mass.
   """

   header = filedesc.readline()
   if "TITLE" in header: header = filedesc.readline()   # skip the comment field
   if header == "":
      raise EOFError("End of file or empty header in PDB file")

   a = float(header[6:15])
   b = float(header[15:24])
   c = float(header[24:33])
   alpha = float(header[33:40])
   beta = float(header[40:47])
   gamma = float(header[47:54])
   alpha *= np.pi/180.0
   beta *= np.pi/180.0
   gamma *= np.pi/180.0
   h = mt.abc2h(a, b, c, alpha, beta, gamma)
   cell = Cell(h)

   natoms = 0
   body = filedesc.readline()
   qatoms = []
   names = []
   masses = []
   while (body.strip() != "" and body.strip() != "END"):
      natoms += 1
      name = body[12:16].strip()
      names.append(name)
      masses.append(Elements.mass(name))
      x = float(body[31:39])
      y = float(body[39:47])
      z = float(body[47:55])
      qatoms.append(x)
      qatoms.append(y)
      qatoms.append(z)

      body = filedesc.readline()

   atoms = Atoms(natoms)
   atoms.q = np.asarray(qatoms)
   atoms.names = np.asarray(names,dtype='|S4')
   atoms.m = np.asarray(masses)

   return atoms, cell
Esempio n. 7
0
def read_xyz(filedesc):
    """Takes a xyz-style file and creates an Atoms object.

   Args:
      filedesc: An open readable file object from a xyz formatted file.

   Returns:
      An Atoms object with the appropriate atom labels, masses and positions.
   """

    natoms = filedesc.readline()
    if natoms == "":
        raise EOFError("The file descriptor hit EOF.")
    natoms = int(natoms)
    comment = filedesc.readline()

    qatoms = []
    names = []
    masses = []
    iat = 0
    while (iat < natoms):
        body = filedesc.readline()
        if body.strip() == "":
            break
        body = body.split()
        name = body[0]
        names.append(name)
        masses.append(Elements.mass(name))
        x = float(body[1])
        y = float(body[2])
        z = float(body[3])
        qatoms.append(x)
        qatoms.append(y)
        qatoms.append(z)
        iat += 1

    if natoms != len(names):
        raise ValueError(
            "The number of atom records does not match the header of the xyz file."
        )

    atoms = Atoms(natoms)
    #   for i in range(natoms):
    #      nat = atoms[i]
    #      nat.q = qatoms[i]
    #      nat.name = names[i]
    #      nat.m = Elements.mass(names[i])
    atoms.q = np.asarray(qatoms)
    atoms.names = np.asarray(names, dtype='|S4')
    atoms.m = np.asarray(masses)

    return atoms
Esempio n. 8
0
def main(filename, natoms):

   ipos=open(filename,"r")
   imode=filename[-3:]
   natoms = int(natoms)
   
   ifr = 0
   nn = 2.5
   while True:
      try:
         ret = read_file(imode,ipos,readcell=True)
         pos = ret["atoms"]
         cell = ret["cell"]
         q=depstrip(pos.q).copy()
         cell.array_pbc(q)
         
         natin = pos.natoms
         q.shape=(natin,3)
         s=np.dot(depstrip(cell.ih),q.T).T
         
         # now replicate in scaled coordinates
         nrep  = int((natoms/natin*nn)**(1./3.))
         
         natrep = natin*(2*nrep+1)**3
         
         ns = np.zeros((natrep,3))
         ik = 0
         for ix in range(-nrep,nrep+1):
          for iy in range(-nrep,nrep+1):
		   for iz in range(-nrep,nrep+1):
			for i in range(natin):
			 ns[ik] = s[i]+[ix,iy,iz]
			 ik+=1
		
         ns = np.dot(depstrip(cell.h),ns.T).T          
         
         # now removes atoms until we only have natoms
         d = np.zeros(natrep)
         for i in range(natrep):
           d[i] = np.sqrt(np.dot(ns[i],ns[i]))
         di = np.argsort(d)
		 
         npos = Atoms(natoms)
         for i in range(natoms):           
           npos.q[3*i:3*(i+1)]=ns[di[i]]
         
      except EOFError: # finished reading files
         sys.exit(0)

      print_file("pdb",npos, cell)
      ifr+=1
Esempio n. 9
0
def main(filename, natoms):

    ipos = open(filename, "r")
    imode = filename[-3:]
    natoms = int(natoms)

    ifr = 0
    nn = 2.5
    while True:
        try:
            ret = read_file(imode, ipos, readcell=True)
            pos = ret["atoms"]
            cell = ret["cell"]
            q = depstrip(pos.q).copy()
            cell.array_pbc(q)

            natin = pos.natoms
            q.shape = (natin, 3)
            s = np.dot(depstrip(cell.ih), q.T).T

            # now replicate in scaled coordinates
            nrep = int((natoms / natin * nn)**(1. / 3.))

            natrep = natin * (2 * nrep + 1)**3

            ns = np.zeros((natrep, 3))
            ik = 0
            for ix in range(-nrep, nrep + 1):
                for iy in range(-nrep, nrep + 1):
                    for iz in range(-nrep, nrep + 1):
                        for i in range(natin):
                            ns[ik] = s[i] + [ix, iy, iz]
                            ik += 1

            ns = np.dot(depstrip(cell.h), ns.T).T

            # now removes atoms until we only have natoms
            d = np.zeros(natrep)
            for i in range(natrep):
                d[i] = np.sqrt(np.dot(ns[i], ns[i]))
            di = np.argsort(d)

            npos = Atoms(natoms)
            for i in range(natoms):
                npos.q[3 * i:3 * (i + 1)] = ns[di[i]]

        except EOFError:  # finished reading files
            sys.exit(0)

        print_file("pdb", npos, cell)
        ifr += 1
Esempio n. 10
0
def read_xyz(filedesc):
    """Takes a xyz-style file and creates an Atoms object.

   Args:
      filedesc: An open readable file object from a xyz formatted file.

   Returns:
      An Atoms object with the appropriate atom labels, masses and positions.
   """

    natoms = filedesc.readline()
    if natoms == "":
        raise EOFError("The file descriptor hit EOF.")
    natoms = int(natoms)
    comment = filedesc.readline()

    qatoms = []
    names = []
    masses = []
    iat = 0
    while iat < natoms:
        body = filedesc.readline()
        if body.strip() == "":
            break
        body = body.split()
        name = body[0]
        names.append(name)
        masses.append(Elements.mass(name))
        x = float(body[1])
        y = float(body[2])
        z = float(body[3])
        qatoms.append(x)
        qatoms.append(y)
        qatoms.append(z)
        iat += 1

    if natoms != len(names):
        raise ValueError("The number of atom records does not match the header of the xyz file.")

    atoms = Atoms(natoms)
    #   for i in range(natoms):
    #      nat = atoms[i]
    #      nat.q = qatoms[i]
    #      nat.name = names[i]
    #      nat.m = Elements.mass(names[i])
    atoms.q = np.asarray(qatoms)
    atoms.names = np.asarray(names, dtype="|S4")
    atoms.m = np.asarray(masses)

    return atoms
Esempio n. 11
0
def process_units(
    comment,
    cell,
    data,
    names,
    masses,
    natoms,
    dimension="automatic",
    units="automatic",
    cell_units="automatic",
    mode="xyz",
):
    """Convert the data in the file according to the units written in the i-PI format.

    Args:
        comment:
        cell:
        data:
        names:
        masses:
        output:

    Returns:

    """
    dimension, units, cell_units = auto_units(comment, dimension, units,
                                              cell_units, mode)

    info(
        " # Interpreting input with dimension %s, units %s and cell units %s" %
        (dimension, units, cell_units),
        verbosity.high,
    )

    # Units transformation
    cell *= unit_to_internal("length", cell_units,
                             1)  # cell units transformation
    data *= unit_to_internal(dimension, units, 1)  # units transformation

    # Return data as i-PI structures
    cell = Cell(cell)
    atoms = Atoms(natoms)
    atoms.q[:] = data
    atoms.names[:] = names
    atoms.m[:] = masses

    return {
        "atoms": atoms,
        "cell": cell,
    }
Esempio n. 12
0
    def resize(self, natoms, nbeads):
        """Creates all the data arrays needed in the simulation.

        Effectively initializes the whole Beads object, according to the
        specified number of atoms and beads. Is also used, as the name suggests,
        to resize the data to a new number of beads when this is necessary, for
        example in initialization from a simulation with a different number of
        beads.

        Also creates, or recreates, the dependency network, as this requires
        the data arrays to be created for it to work.

        Args:
           natoms: The number of atoms.
           nbeads: The number of beads.
        """

        self.natoms = natoms
        self.nbeads = nbeads

        dself = dd(self)

        dself.names = depend_array(name="names", value=np.zeros(natoms, np.dtype('|U6')))

        # atom masses, and mass-related arrays
        dself.m = depend_array(name="m", value=np.zeros(natoms, float))   # this is the prototype mass array (just one independent of bead n)
        dself.m3 = depend_array(name="m3", value=np.zeros((nbeads, 3 * natoms), float),    # this is m conveniently replicated to be (nb,3*nat)
                                func=self.mtom3, dependencies=[dself.m])
        dself.sm3 = depend_array(name="sm3", value=np.zeros((nbeads, 3 * natoms), float),   # this is just the square root of m3
                                 func=self.m3tosm3, dependencies=[dself.m3])

        # positions and momenta. bead representation, base storage used everywhere
        dself.q = depend_array(name="q", value=np.zeros((nbeads, 3 * natoms), float))
        dself.p = depend_array(name="p", value=np.zeros((nbeads, 3 * natoms), float))

        # position and momentum of the centroid
        dself.qc = depend_array(name="qc", value=np.zeros(3 * natoms, float),
                                func=self.get_qc, dependencies=[dself.q])
        dself.pc = depend_array(name="pc", value=np.zeros(3 * natoms, float),
                                func=self.get_pc, dependencies=[dself.p])

        # path springs potential and force
        dself.vpath = depend_value(name="vpath", func=self.get_vpath,
                                   dependencies=[dself.q, dself.m3])
        dself.fpath = depend_array(name="fpath", value=np.zeros((nbeads, 3 * natoms), float),
                                   func=self.get_fpath, dependencies=[dself.q])

        # create proxies to access the individual beads as Atoms objects
        # TODO: ACTUALLY THIS IS ONLY USED HERE METHINK, SO PERHAPS WE COULD REMOVE IT TO DECLUTTER THE CODE.
        self._blist = [Atoms(natoms, _prebind=(self.q[i, :], self.p[i, :], self.m, self.names)) for i in range(nbeads)]

        # kinetic energies of thhe beads, and total (classical) kinetic stress tensor
        dself.kins = depend_array(name="kins", value=np.zeros(nbeads, float),
                                  func=self.kin_gather,
                                  dependencies=[dd(b).kin for b in self._blist])
        dself.kin = depend_value(name="kin", func=self.get_kin,
                                 dependencies=[dself.kins])
        dself.kstress = depend_array(name="kstress", value=np.zeros((3, 3), float),
                                     func=self.get_kstress,
                                     dependencies=[dd(b).kstress for b in self._blist])
Esempio n. 13
0
def create_xyz_sample_file(request):
    """ Create a fake xyz file and build the atoms and cell object from it.
    """

    natoms, frames, comment, expected_cell, units_conv_at, units_conv_cell = request.param

    filedesc, xyz, atoms_names = xyz_gen.xyz_traj_filedesc(natoms, frames, comment)

    # init_file needs to read from a real file...
    tmp_file = tmp.NamedTemporaryFile(mode='wr', prefix='ipi_testing-tmp', delete=False)
    tmp_file.seek(0)
    tmp_file.write(filedesc.read())
    tmp_file.close()
    filedesc.close()

    masses = np.zeros(natoms * frames)
    for _ii, _at in enumerate(atoms_names):
        masses[_ii] = Elements.mass(_at)

    ratoms = []
    for _fr in range(frames):
        ratoms.append(Atoms(natoms))
        ratoms[-1].q = xyz[_fr * natoms * 3:3 * (_fr + 1) * natoms] * units_conv_at
        ratoms[-1].m = masses[_fr * natoms:(_fr + 1) * natoms]
        ratoms[-1].names = atoms_names[_fr * natoms:(_fr + 1) * natoms]

    cell = Cell(expected_cell * units_conv_cell)

    # remove temp file created during this test
    def delete_files_after_testing():
        if os.path.isfile(tmp_file.name):
            os.remove(tmp_file.name)

    request.addfinalizer(delete_files_after_testing)
    return tmp_file, cell, ratoms
Esempio n. 14
0
def process_units(comment, cell, qatoms, names, masses, output='objects'):
    """ Converts the data in the file according to the units written in the ipi format.
    """
    # Extracting trajectory units
    family, unit = 'undefined', ''
    is_comment_useful = filter(None, [key.search(comment.strip())
                                      for key in traj_re])
    if len(is_comment_useful) > 0:
        traj = is_comment_useful[0].group()[:-1].split('{')
        family, unit = traj_dict[traj[0]]['dimension'], traj[1]

    # Extracting cell units
    cell_unit = ''
    tmp = cell_unit_re.search(comment)
    if tmp is not None:
        cell_unit = tmp.group(1)

    # Units transformation
    cell *= unit_to_internal('length', cell_unit, 1) # cell units transformation
    qatoms *= unit_to_internal(family, unit, 1) # units transformation
    if output == 'objects':

        cell = Cell(cell)
        atoms = Atoms(len(names))
        atoms.q[:] = qatoms
        atoms.names[:] = names
        atoms.m[:] = masses

        return {
          "atoms": atoms,
          "cell": cell,
        }

    else:

        return {
          "data": qatoms,
          "masses": masses,
          "names": names,
          "natoms": len(names),
          "cell": cell,
        }
Esempio n. 15
0
def contract_trajectory(fns_in, fn_out_template, n_new, cell_units_in, cell_units_out):

    verbosity.level = "low"
    n = len(fns_in)

    # Generate output file names.
    if n_new == 1:
        fns_out = [fn_out_template]
    else:
        fns_out = [fn_out_template.format(i) for i in range(n_new)]

    print("Contracting {:d} beads to {:d} beads.".format(n, n_new))
    print()

    print("input file names:")
    for fn in fns_in:
        print(fn)
    print()

    print("output file names:")
    for fn in fns_out:
        print(fn)
    print()

    # Open input trajectory iterators.
    trjs_in = [iter_file_name_raw(fn) for fn in fns_in]
    mode = os.path.splitext(fn)[-1]

    # Open output files.
    fs_out = [open_backup(fn, "w") for fn in fns_out]
    mode_out = os.path.splitext(fn_out_template)[-1]

    # prepare ring polymer rescaler
    rescale = nm_rescale(n, n_new)

    # Loop over all frames.
    i_frame = 0
    while True:
        try:
            # Get the frames for all beads.
            frames = [trj.next() for trj in trjs_in]
        except StopIteration:
            # Stop when any of the trajectories runs out of frames.
            break

        # gets units from first frame
        dimension, units, cell_units = auto_units(comment=frames[0]["comment"], cell_units=cell_units_in)
        if cell_units_out == "automatic": cell_units_out = cell_units  # re-use units unless otherwise specified

        # Consistency check.
        h = frames[0]["cell"]
        natoms = len(frames[0]["data"]) / 3
        for i in range(n):

            # Check that all the cells are the same.
            if (frames[i]["cell"] != h).any():
                msg = "Cell for beads {:d} and {:d} differ in frame {:d}."
                raise ValueError(msg.format(0, i, i_frame))

            # Check that the numbers of atoms are the same.
            if len(frames[i]["data"]) != 3 * natoms:
                msg = "Different numbers of atoms for beads {:d} and {:d} in frame {:d}."
                raise ValueError(msg.format(0, i, i_frame))

        cell = Cell()
        cell.h = frames[0]["cell"]
        atoms = Atoms(natoms)
        atoms.names = frames[0]["names"]

        # Compose the ring polymer.
        q = np.vstack([frame["data"] for frame in frames]) * unit_to_internal(dimension, units, 1)  # units transformation

        # Contract the coordinates to `n_new` beads.
        q_c = rescale.b1tob2(q)

        # Save the output data.
        for i, f_out in enumerate(fs_out):
            atoms.q = q_c[i, :]
            print_file(mode_out, atoms, cell, f_out, dimension=dimension, units=units, cell_units=cell_units_out)

        # Count frames and print information on progress.
        i_frame += 1
        if i_frame % 100 == 0:
            print("\rframe {:d}".format(i_frame), end="")
        sys.stdout.flush()

    for f_out in fs_out:
        f_out.close()

    print()
    print()
    print("Processed {:d} frames.".format(i_frame))
Esempio n. 16
0
def contract_trajectory(fns_in, fn_out_template, n_new, cell_units_in,
                        cell_units_out):

    verbosity.level = "low"
    n = len(fns_in)

    # Generate output file names.
    if n_new == 1:
        fns_out = [fn_out_template]
    else:
        fns_out = [fn_out_template.format(i) for i in range(n_new)]

    print("Contracting {:d} beads to {:d} beads.".format(n, n_new))
    print()

    print("input file names:")
    for fn in fns_in:
        print(fn)
    print()

    print("output file names:")
    for fn in fns_out:
        print(fn)
    print()

    # Open input trajectory iterators.
    trjs_in = [iter_file_name_raw(fn) for fn in fns_in]
    mode = os.path.splitext(fn)[-1]

    # Open output files.
    fs_out = [open_backup(fn, "w") for fn in fns_out]
    mode_out = os.path.splitext(fn_out_template)[-1]

    # prepare ring polymer rescaler
    rescale = nm_rescale(n, n_new)

    # Loop over all frames.
    i_frame = 0
    while True:
        try:
            # Get the frames for all beads.
            frames = [trj.next() for trj in trjs_in]
        except StopIteration:
            # Stop when any of the trajectories runs out of frames.
            break

        # gets units from first frame
        dimension, units, cell_units = auto_units(comment=frames[0]["comment"],
                                                  cell_units=cell_units_in)
        if cell_units_out == "automatic":
            cell_units_out = cell_units  # re-use units unless otherwise specified

        # Consistency check.
        h = frames[0]["cell"]
        natoms = len(frames[0]["data"]) / 3
        for i in range(n):

            # Check that all the cells are the same.
            if (frames[i]["cell"] != h).any():
                msg = "Cell for beads {:d} and {:d} differ in frame {:d}."
                raise ValueError(msg.format(0, i, i_frame))

            # Check that the numbers of atoms are the same.
            if len(frames[i]["data"]) != 3 * natoms:
                msg = "Different numbers of atoms for beads {:d} and {:d} in frame {:d}."
                raise ValueError(msg.format(0, i, i_frame))

        cell = Cell()
        cell.h = frames[0]["cell"]
        atoms = Atoms(natoms)
        atoms.names = frames[0]["names"]

        # Compose the ring polymer.
        q = np.vstack([frame["data"] for frame in frames]) * unit_to_internal(
            dimension, units, 1)  # units transformation

        # Contract the coordinates to `n_new` beads.
        q_c = rescale.b1tob2(q)

        # Save the output data.
        for i, f_out in enumerate(fs_out):
            atoms.q = q_c[i, :]
            print_file(mode_out,
                       atoms,
                       cell,
                       f_out,
                       dimension=dimension,
                       units=units,
                       cell_units=cell_units_out)

        # Count frames and print information on progress.
        i_frame += 1
        if i_frame % 100 == 0:
            print("\rframe {:d}".format(i_frame), end="")
        sys.stdout.flush()

    for f_out in fs_out:
        f_out.close()

    print()
    print()
    print("Processed {:d} frames.".format(i_frame))