diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -37,6 +37,7 @@
 import Language.R.QQ (r)
 import Math.Clustering.Hierarchical.Spectral.Types (getClusterItemsDend, EigenGroup (..))
 import Math.Clustering.Spectral.Sparse (b1ToB2, B1 (..), B2 (..))
+import Math.Modularity.Types (Q (..))
 import Options.Generic
 import System.IO (hPutStrLn, stderr)
 import Text.Read (readMaybe, readEither)
@@ -97,6 +98,7 @@
                , minSize :: Maybe Int <?> "([1] | INT) The minimum size of a cluster. Defaults to 1."
                , maxStep :: Maybe Int <?> "([Nothing] | INT) Only keep clusters that are INT steps from the root. Defaults to all steps."
                , maxProportion :: Maybe Double <?> "([Nothing] | DOUBLE) Stopping criteria to stop at the node immediate after a node with DOUBLE proportion split. So a node N with L and R children will stop with this criteria at 0.5 if |L| / |R| < 0.5 or > 2 (absolute log2 transformed), that is, if one child has over twice as many items as the other child. Includes L and R in the final result."
+               , minModularity :: Maybe Double <?> "([Nothing] | DOUBLE) Nearly the same as --min-distance, but for clustering instead of drawing (so the output json tree can be larger). Stopping criteria to stop at the node with DOUBLE modularity. So a node N with L and R children will stop with this criteria the distance at N to L and R is < DOUBLE. Does not include L and R in the final result."
                , minDistance :: Maybe Double <?> "([Nothing] | DOUBLE) Stopping criteria to stop at the node immediate after a node with DOUBLE distance. So a node N with L and R children will stop with this criteria the distance at N to L and R is < DOUBLE. Includes L and R in the final result."
                , minDistanceSearch :: Maybe Double <?> "([Nothing] | DOUBLE) Similar to --min-distance, but searches from the leaves to the root -- if a path from a subtree contains a distance of at least DOUBLE, keep that path, otherwise prune it. This argument assists in finding distant nodes."
                , smartCutoff :: Maybe Double <?> "([Nothing] | DOUBLE) Whether to set the cutoffs for --min-size, --max-proportion, --min-distance, and --min-distance-search based off of the distributions (median + (DOUBLE * MAD)) of all nodes. To use smart cutoffs, use this argument and then set one of the three arguments to an arbitrary number, whichever cutoff type you want to use. --min-size distribution is log2 transformed."
@@ -202,6 +204,7 @@
     short "maxStep"              = Just 'S'
     short "minDistance"          = Nothing
     short "minDistanceSearch"    = Nothing
+    short "minModularity"        = Nothing
     short "minSize"              = Just 'M'
     short "noFilter"             = Just 'F'
     short "normalization"        = Just 'z'
@@ -307,8 +310,9 @@
 
     liftIO $ when (isJust pca' && (elem normalization' [TfIdfNorm, BothNorm])) $
       hPutStrLn stderr "\nWarning: PCA (creating negative numbers) with tf-idf\
-                       \ normalization may lead to NaNs before spectral\
-                       \ clustering (leading to svdlibc to hang)! Continuing..."
+                       \ normalization may lead to NaNs or 0s before spectral\
+                       \ clustering (leading to svdlibc to hang or dense SVD\
+                       \ to error out)! Continuing..."
 
     mats <- MaybeT
           $ if null matrixPaths'
@@ -367,6 +371,7 @@
         maxProportion'    =
             fmap MaxProportion . unHelpful . maxProportion $ opts
         minDistance'       = fmap MinDistance . unHelpful . minDistance $ opts
+        minModularity'     = fmap Q . unHelpful . minModularity $ opts
         minDistanceSearch' = fmap MinDistanceSearch . unHelpful . minDistanceSearch $ opts
         smartCutoff'      = fmap SmartCutoff . unHelpful . smartCutoff $ opts
         customCut'        = CustomCut . Set.fromList . unHelpful . customCut $ opts
@@ -511,7 +516,7 @@
     originalClusterResults <- case prior' of
         Nothing -> do
             let (fullCr, _) =
-                  hSpecClust dense' eigenGroup' normalization' numEigen'
+                  hSpecClust dense' eigenGroup' normalization' numEigen' minModularity'
                     . extractSc
                     $ processedSc
 
diff --git a/src/TooManyCells/MakeTree/Cluster.hs b/src/TooManyCells/MakeTree/Cluster.hs
--- a/src/TooManyCells/MakeTree/Cluster.hs
+++ b/src/TooManyCells/MakeTree/Cluster.hs
@@ -27,6 +27,7 @@
 import Data.List (sortBy, groupBy, zip4, genericLength)
 import Data.Int (Int32)
 import Data.Maybe (fromMaybe, catMaybes, mapMaybe)
+import Math.Modularity.Types (Q (..))
 import Data.Monoid ((<>))
 import Data.Tree (Tree (..))
 import Data.Tuple (swap)
@@ -135,9 +136,10 @@
            -> EigenGroup
            -> NormType
            -> Maybe NumEigen
+           -> Maybe Q
            -> SingleCells
            -> (ClusterResults, ClusterGraph CellInfo)
-hSpecClust (DenseFlag isDense) eigenGroup norm numEigen sc =
+hSpecClust (DenseFlag isDense) eigenGroup norm numEigen minModMay sc =
     ( ClusterResults { _clusterList = clustering
                      , _clusterDend = dendToTree dend
                      }
@@ -169,7 +171,7 @@
           True
           (fmap unNumEigen numEigen)
           Nothing
-          Nothing
+          minModMay
           items
         . Left
     hSpecCommand BothNorm False =
@@ -178,7 +180,7 @@
           True
           (fmap unNumEigen numEigen)
           Nothing
-          Nothing
+          minModMay
           items
         . Left
     hSpecCommand _ False =
@@ -187,7 +189,7 @@
           False
           (fmap unNumEigen numEigen)
           Nothing
-          Nothing
+          minModMay
           items
         . Left
     hSpecCommand TfIdfNorm True =
@@ -196,7 +198,7 @@
           True
           (fmap unNumEigen numEigen)
           Nothing
-          Nothing
+          minModMay
           items
         . Left
         . sparseToHMat
@@ -206,7 +208,7 @@
           True
           (fmap unNumEigen numEigen)
           Nothing
-          Nothing
+          minModMay
           items
         . Left
         . sparseToHMat
@@ -216,7 +218,7 @@
           False
           (fmap unNumEigen numEigen)
           Nothing
-          Nothing
+          minModMay
           items
         . Left
         . sparseToHMat
diff --git a/src/TooManyCells/Matrix/Preprocess.hs b/src/TooManyCells/Matrix/Preprocess.hs
--- a/src/TooManyCells/Matrix/Preprocess.hs
+++ b/src/TooManyCells/Matrix/Preprocess.hs
@@ -333,11 +333,11 @@
 shiftPositiveMat = MatObsRow
               . S.fromColsL
               . fmap (\ xs -> bool xs (shift . S.toDenseListSV $ xs)
-                            . any (< 0)
+                            . any (<= 0)
                             . S.toDenseListSV
                             $ xs
                      )
               . S.toColsL
               . unMatObsRow
   where
-    shift xs = S.sparsifySV . S.vr . fmap (+ minimum xs) $ xs
+    shift xs = S.sparsifySV . S.vr . fmap (\x -> x + minimum xs + 1) $ xs
diff --git a/too-many-cells.cabal b/too-many-cells.cabal
--- a/too-many-cells.cabal
+++ b/too-many-cells.cabal
@@ -1,6 +1,6 @@
 cabal-version: >=1.10
 name: too-many-cells
-version: 0.1.10.0
+version: 0.1.11.0
 license: GPL-3
 license-file: LICENSE
 copyright: 2019 Gregory W. Schwartz
@@ -118,6 +118,7 @@
         inline-r >=0.9.2,
         lens >=4.16.1,
         matrix-market-attoparsec >=0.1.0.8,
+        modularity >=0.2.1.0,
         mtl >=2.2.2,
         optparse-generic >=1.3.0,
         palette >=0.3.0.1,
