docs: def read(fn, **kwargs):
      """
      Convenience function: Detect file extension and read via Atomistica or ASE.
      If reading a NetCDF files, frame numbers can be appended via '@'.
      e.g., a = read('traj.nc@5')
      """
      ext = fn[fn.rfind('.'):].split('@')
      if len(ext) == 1:
          if ext[0] == '.out' or ext[0] == '.dat':
              dirname = os.path.dirname(fn)
              if len(dirname) == 0:
                  dirname = '.'
              cycfn = dirname+'/cyc.dat'
              if os.path.exists(cycfn):
                  return read_atoms(fn, cycfn=cycfn)
              return read_atoms(fn)
          elif ext[0] == '.nc':
              traj = NetCDFTrajectory(fn, **kwargs)
              return traj[-1]
          else:
              return ase.io.read(fn, **kwargs)
      elif len(ext) == 2:
          if ext[0] == '.nc':
              frame = int(ext[1])
              fn = fn[:fn.rfind('@')]
              traj = NetCDFTrajectory(fn)
              return traj[frame]
          else:
              return ase.io.read(fn, **kwargs)
      else:
          return ase.io.read(fn, **kwargs)