diff --git a/pycgtool/bondset.py b/pycgtool/bondset.py
index ce30d97efb97d10630960df9f24c3bf54525f8b4..4cf7892b8eb5ceecb20c188c16f421412cb05a30 100644
--- a/pycgtool/bondset.py
+++ b/pycgtool/bondset.py
@@ -10,10 +10,11 @@ import logging
 
 import numpy as np
 
+
 try:
     from tqdm import tqdm
 except ImportError:
-    pass
+    from .util import tqdm_dummy as tqdm
 
 from .util import sliding, dist_with_pbc, transpose_and_sample
 from .util import extend_graph_chain, backup_file
@@ -373,11 +374,8 @@ class BondSet:
         bond_iter = itertools.chain(*self._molecules.values())
         bond_iter_wrap = bond_iter
         if progress:
-            try:
-                total = sum(map(len, self._molecules.values()))
-                bond_iter_wrap = tqdm(bond_iter, total=total, ncols=80)
-            except NameError:
-                pass
+            total = sum(map(len, self._molecules.values()))
+            bond_iter_wrap = tqdm(bond_iter, total=total, ncols=80)
 
         for bond in bond_iter_wrap:
             try:
diff --git a/pycgtool/frame.py b/pycgtool/frame.py
index a0a103dec9daee67a981c8a1a8b43e684dc92238..b61d6c8db101940f1da61aabe7c743017090c9db 100644
--- a/pycgtool/frame.py
+++ b/pycgtool/frame.py
@@ -176,20 +176,21 @@ class FrameReaderSimpleTraj(FrameReader):
         Parse a GROMACS GRO file and create Residues/Atoms
         Required before reading coordinates from XTC file
 
-        :param filename: Filename of GROMACS GRO to read
+        :param frame: Frame instance to initialise from GRO file
         """
         with open(self._topname) as gro:
             frame.name = gro.readline().strip()
             self.num_atoms = int(gro.readline())
             frame.natoms = self.num_atoms
             resnum_last = None
+            atnum = 0
 
-            unpacker = FixedFormatUnpacker("I5,A5,A5,X5,F8,F8,F8",
+            unpacker = FixedFormatUnpacker("I5,2A5,5X,3F8",
                                            FixedFormatUnpacker.FormatStyle.Fortran)
 
             for _ in range(self.num_atoms):
-                resnum, resname, atomname, x, y, z = unpacker.unpack(gro.readline())
-                coords = np.array([x, y, z], dtype=np.float32)
+                resnum, resname, atomname, *coords = unpacker.unpack(gro.readline())
+                coords = np.array(coords, dtype=np.float32)
 
                 if resnum != resnum_last:
                     frame.residues.append(Residue(name=resname,
@@ -201,8 +202,7 @@ class FrameReaderSimpleTraj(FrameReader):
                 frame.residues[-1].add_atom(atom)
                 atnum += 1
 
-            line = gro.readline()
-            frame.box = np.array([float(x) for x in line.split()[0:3]], dtype=np.float32)
+            frame.box = np.array([float(x) for x in gro.readline().split()[0:3]], dtype=np.float32)
 
     def _read_frame_number(self, number):
         """
@@ -234,6 +234,7 @@ class FrameReaderMDTraj(FrameReader):
             else:
                 e.msg = "The MDTraj FrameReader requires the module MDTraj (and probably Scipy)"
             raise
+        logger.warning("WARNING: Using MDTraj which renames solvent molecules")
 
         try:
             if trajname is None:
@@ -256,26 +257,16 @@ class FrameReaderMDTraj(FrameReader):
         Parse a GROMACS GRO file and create Residues/Atoms
         Required before reading coordinates from XTC file
 
-        :param filename: Filename of GROMACS GRO to read
+        :param frame: Frame instance to initialise from GRO file
         """
-        try:
-            import mdtraj
-        except ImportError as e:
-            if "scipy" in e.msg:
-                e.msg = "The MDTraj FrameReader also requires Scipy"
-            else:
-                e.msg = "The MDTraj FrameReader requires the module MDTraj (and probably Scipy)"
-            raise
-        logger.warning("WARNING: Using MDTraj which renames solvent molecules")
+        import mdtraj
         top = mdtraj.load(self._topname)
 
         frame.name = ""
         self.num_atoms = top.n_atoms
         frame.natoms = top.n_atoms
 
-        for residue in top.topology.residues:
-            frame.residues.append(Residue(name=residue.name,
-                                          num=residue.resSeq))
+        frame.residues = [Residue(name=res.name, num=res.resSeq) for res in top.topology.residues]
 
         for atom in top.topology.atoms:
             new_atom = Atom(name=atom.name, num=atom.serial,
@@ -304,6 +295,7 @@ class Frame:
         :param itp: GROMACS ITP file to read masses and charges
         :return: Frame instance
         """
+        self.name = ""
         self.residues = []
         self.number = frame_start - 1
         self.time = 0
diff --git a/pycgtool/mapping.py b/pycgtool/mapping.py
index 83204ca37d880d262cdeb75ab1ee04ff9b02e9a8..b42b88f16d0236e2cd95ea5e9a71560749dc8f48 100644
--- a/pycgtool/mapping.py
+++ b/pycgtool/mapping.py
@@ -20,8 +20,6 @@ except ImportError:
     from .util import NumbaDummy
     numba = NumbaDummy()
 
-np.seterr(all="raise")
-
 logger = logging.getLogger(__name__)
 
 
@@ -29,7 +27,7 @@ class BeadMap(Atom):
     """
     POD class holding values relating to the AA->CG transformation for a single bead.
     """
-    __slots__ = ["name", "type", "atoms", "charge", "mass", "weights"]
+    __slots__ = ["name", "type", "atoms", "charge", "mass", "weights", "weights_dict"]
 
     def __init__(self, name=None, type=None, atoms=None, charge=0, mass=0):
         """
@@ -45,8 +43,9 @@ class BeadMap(Atom):
         Atom.__init__(self, name=name, type=type, charge=charge, mass=mass)
         self.atoms = atoms
         # NB: Mass weights are added in Mapping.__init__ if an itp file is provided
-        self.weights = {"geom": np.array([[1. / len(atoms)] for _ in atoms], dtype=np.float32),
-                        "first": np.array([[1.]] + [[0.] for _ in atoms[1:]], dtype=np.float32)}
+        self.weights_dict = {"geom": np.array([[1. / len(atoms)] for _ in atoms], dtype=np.float32),
+                             "first": np.array([[1.]] + [[0.] for _ in atoms[1:]], dtype=np.float32)}
+        self.weights = self.weights_dict["geom"]
 
     def __iter__(self):
         """
@@ -118,7 +117,7 @@ class Mapping:
                     mass_array = np.array([[atoms[atom][1]] for atom in bead], dtype=np.float32)
                     bead.mass = sum(mass_array)
                     mass_array /= bead.mass
-                    bead.weights["mass"] = mass_array
+                    bead.weights_dict["mass"] = mass_array
 
                     for atom in bead:
                         if self._manual_charges[molname]:
@@ -128,6 +127,12 @@ class Mapping:
 
                 self._masses_are_set = True
 
+        if self._map_center == "mass" and not self._masses_are_set:
+            self._guess_atom_masses()
+        for molname, mapping in self._mappings.items():
+            for bmap in mapping:
+                bmap.weights = bmap.weights_dict[self._map_center]
+
     def __len__(self):
         return len(self._mappings)
 
@@ -170,7 +175,7 @@ class Mapping:
                         raise RuntimeError(msg)
 
                     mass_array /= bead.mass
-                    bead.weights["mass"] = mass_array
+                    bead.weights_dict["mass"] = mass_array
 
         self._masses_are_set = True
 
@@ -220,9 +225,6 @@ class Mapping:
         :param cgframe: CG Frame to remap - optional
         :return: Frame instance containing the CG frame
         """
-        if self._map_center == "mass" and not self._masses_are_set:
-            self._guess_atom_masses()
-
         if cgframe is None:
             # Frame needs initialising
             cgframe = self._cg_frame_setup(frame.yield_resname_in(self._mappings), frame.name)
@@ -231,20 +233,19 @@ class Mapping:
         cgframe.number = frame.number
         cgframe.box = frame.box
 
+        coord_func = calc_coords_weight if frame.box[0] * frame.box[1] * frame.box[2] else calc_coords_weight_nobox
+
         for aares, cgres in zip(frame.yield_resname_in(self._mappings), cgframe):
             molmap = self._mappings[aares.name]
 
             for i, (bead, bmap) in enumerate(zip(cgres, molmap)):
                 ref_coords = aares[bmap[0]].coords
-                coords = np.array([aares[atom].coords for atom in bmap], dtype=np.float32)
-
-                try:
-                    weights = bmap.weights[self._map_center]
-                except KeyError as e:
-                    e.args = ("Unknown mapping type '{0}'.".format(e.args[0]),)
-                    raise
+                if len(bmap) == 1:
+                    bead.coords = ref_coords
+                    continue
 
-                bead.coords = calc_coords_weight(ref_coords, coords, cgframe.box, weights)
+                coords = np.asarray([aares[atom].coords for atom in bmap], dtype=np.float32)
+                bead.coords = coord_func(ref_coords, coords, cgframe.box, bmap.weights)
 
         return cgframe
 
@@ -260,13 +261,25 @@ def calc_coords_weight(ref_coords, coords, box, weights):
     :param weights: Array of atom weights, must sum to 1
     :return: Coordinates of CG bead
     """
-    n = len(coords)
-    result = np.zeros(3, dtype=np.float32)
-    for i in range(n):
-        tmp_coords = coords[i]
-        tmp_coords -= ref_coords
-        if box[0] * box[1] * box[2] != 0:
-            tmp_coords -= box * np.rint(tmp_coords / box)
-        result += weights[i] * tmp_coords
+    vectors = coords - ref_coords
+    vectors -= box * np.rint(vectors / box)
+    result = np.sum(weights * vectors, axis=0)
+    result += ref_coords
+    return result
+
+
+@numba.jit
+def calc_coords_weight_nobox(ref_coords, coords, box, weights):
+    """
+    Calculate the coordinates of a single CG bead from weighted component atom coordinates.
+
+    :param ref_coords: Coordinates of reference atom, usually first atom in bead
+    :param coords: Array of coordinates of component atoms
+    :param box: PBC box vectors
+    :param weights: Array of atom weights, must sum to 1
+    :return: Coordinates of CG bead
+    """
+    vectors = coords - ref_coords
+    result = np.sum(weights * vectors, axis=0)
     result += ref_coords
     return result
diff --git a/pycgtool/util.py b/pycgtool/util.py
index aa88d684460d3454f4b986f9acf416a1cc8d9b36..6a08c9d41a28d20491e74d768a1f6459378aa1f5 100644
--- a/pycgtool/util.py
+++ b/pycgtool/util.py
@@ -13,7 +13,6 @@ import re
 from collections import namedtuple
 
 import numpy as np
-np.seterr(all="raise")
 
 logger = logging.getLogger(__name__)
 
@@ -24,7 +23,7 @@ class NumbaDummy:
     """
     def __getattr__(self, item):
         if item == "jit":
-            return self.jit
+            return NumbaDummy.jit
         return self
 
     def __getitem__(self, item):
@@ -33,7 +32,8 @@ class NumbaDummy:
     def __call__(self, *args, **kwargs):
         return None
 
-    def jit(self, *args, **kwargs):
+    @staticmethod
+    def jit(*args, **kwargs):
         """
         Dummy version of numba.jit decorator, does nothing
         """
@@ -144,7 +144,7 @@ def dist_with_pbc(pos1, pos2, box):
     :return: Vector between two points
     """
     d = pos2 - pos1
-    if box[0] * box[1] * box[2] != 0:
+    if box[0] * box[1] * box[2]:
         d -= box * np.rint(d / box)
     return d
 
@@ -417,11 +417,13 @@ class SimpleEnum(object):
             return self.value == other.value
 
     @classmethod
-    def enum(cls, name, keys=list(), values=None):
+    def enum(cls, name, keys=None, values=None):
         def returner(val):
             return lambda _: val
         enum_cls = type(name, (cls.Enum,), {})
 
+        if keys is None:
+            keys = []
         if values is None:
             for key in keys:
                 prop = property(returner(cls.EnumItem(name, key)))
@@ -439,7 +441,6 @@ class SimpleEnum(object):
         return cls.enum(name, key_val_dict.keys(), key_val_dict.values())
 
 
-# TODO testing
 class FixedFormatUnpacker(object):
     """
     Unpack strings printed in fixed format.
@@ -503,3 +504,7 @@ class FixedFormatUnpacker(object):
             if format_item.type is not None:
                 items.append(format_item.type(string_part.strip()))
         return items
+
+
+def tqdm_dummy(iterable, **kwargs):
+    return iterable
diff --git a/test/test_util.py b/test/test_util.py
index 5d50a1efb137a41b2d9375fc674e8b913efa3b10..604fa494a0c1a71c9315d60e0c8cc6ea79e4ad44 100644
--- a/test/test_util.py
+++ b/test/test_util.py
@@ -130,7 +130,7 @@ class UtilTest(unittest.TestCase):
 
         enum2 = SimpleEnum.enum("enum2", ["one", "two", "three"])
         with self.assertRaises(TypeError):
-            tmp = enum2.one == enum.one
+            assert enum2.one == enum.one
 
         self.assertTrue("one" in enum)
         self.assertFalse("four" in enum)
@@ -155,7 +155,7 @@ class UtilTest(unittest.TestCase):
 
         enum2 = SimpleEnum.enum("enum2", ["one", "two", "three"])
         with self.assertRaises(TypeError):
-            tmp = enum2.one == enum.one
+            assert enum2.one == enum.one
 
         self.assertTrue("one" in enum)
         self.assertEqual(111, enum.one.value)