diff --git a/src/main/java/gov/usgs/earthquake/nshmp/model/Deserialize.java b/src/main/java/gov/usgs/earthquake/nshmp/model/Deserialize.java
index c227d51274ee5f5d13ce620d0a064cdb32fc9540..d2e8b0e4104f368bcc37da9586a3964dabdf147b 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/model/Deserialize.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/model/Deserialize.java
@@ -17,6 +17,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
+import java.util.OptionalDouble;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
@@ -578,7 +579,10 @@ class Deserialize {
     double spacing = obj.get(GRID_SPACING).getAsDouble();
     int smoothingDensity = obj.get(SMOOTHING_DENSITY).getAsInt();
     double smoothingLimit = obj.get(SMOOTHING_LIMIT).getAsDouble();
-    double distanceBin = obj.get(DISTANCE_BIN).getAsDouble();
+    JsonElement distanceBinElement = obj.get(DISTANCE_BIN);
+    OptionalDouble distanceBin = distanceBinElement.isJsonNull()
+        ? OptionalDouble.empty()
+        : OptionalDouble.of(distanceBinElement.getAsDouble());
     String typeStr = obj.get(POINT_SOURCE_TYPE).getAsString();
     String scalingStr = obj.get(RUPTURE_SCALING).getAsString();
     double maxDepth = obj.get(MAX_DEPTH).getAsDouble();
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/model/GridRuptureSet.java b/src/main/java/gov/usgs/earthquake/nshmp/model/GridRuptureSet.java
index 386299401dcda13350612cbf6156da52987e79ff..a84407170288dfd9b82a43b52d16a57f1863c4eb 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/model/GridRuptureSet.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/model/GridRuptureSet.java
@@ -81,12 +81,12 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
   private final PointSourceType pointSourceType;
 
   final DepthModel depthModel;
-  final boolean optimizable;
+  // final boolean optimizable;
 
   final double gridSpacing;
   final int smoothingDensity;
   final double smoothingLimit;
-  final double distanceBin;
+  final OptionalDouble distanceBin;
 
   /*
    * Enforce expectation that all MFDs are GR*
@@ -113,11 +113,6 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
     this.magMaster = builder.magMaster;
     this.maxDepth = builder.maxDepth;
 
-    this.optimizable =
-        (type() != SourceType.ZONE) &&
-            (pointSourceType() != FIXED_STRIKE) &&
-            this.magMaster.isPresent();
-
     double[] depthMags = this.mfds.get(0).data().xValues().toArray();
 
     this.depthModel = DepthModel.create(
@@ -158,7 +153,7 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
    * circumstances under which a grid optimization table can not be built.
    */
   public boolean optimizable() {
-    return optimizable;
+    return distanceBin.isPresent();
   }
 
   /**
@@ -274,7 +269,7 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
     private Double gridSpacing;
     private Integer smoothingDensity;
     private Double smoothingLimit;
-    private Double distanceBin;
+    private OptionalDouble distanceBin;
 
     private Builder() {
       super(SourceType.GRID);
@@ -776,7 +771,6 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
     private List<PointSource> initSources(boolean smoothed) {
       /* For now, should only be getting here for GR MFDs */
       Mfd modelMfd = parent.mfds.get(0);
-      Mfd.Properties props = modelMfd.properties(); // probably INCR
       double[] mags = modelMfd.data().xValues().toArray();
 
       // Can we get this from the mfd.properties?
@@ -792,12 +786,13 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
       // double mMin = parent.magMaster[0] - ΔmBy2;
       // double mMax = parent.magMaster[parent.magMaster.length - 1] + ΔmBy2;
       double rMax = parent.groundMotionModels().maxDistance();
+      double distanceBin = parent.distanceBin.orElseThrow();
       double[] smoothingOffsets = smoothingOffsets(
           parent.smoothingDensity,
           parent.gridSpacing);
 
       IntervalTable.Builder tableBuilder = new IntervalTable.Builder()
-          .rows(0.0, rMax, parent.distanceBin)
+          .rows(0.0, rMax, distanceBin)
           .columns(mMin, mMax, Δm);
 
       for (PointSource source : parent.iterableForLocation(origin)) {
@@ -844,7 +839,6 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
 
       /* For now, should only be getting here for GR MFDs */
       Mfd modelMfd = parent.mfds.get(0);
-      Mfd.Properties props = modelMfd.properties(); // probably INCR
       double[] mags = modelMfd.data().xValues().toArray();
 
       double Δm = mags[1] - mags[0];
@@ -857,20 +851,21 @@ public class GridRuptureSet extends AbstractRuptureSet<PointSource> {
       // double mMin = parent.magMaster[0] - ΔmBy2;
       // double mMax = parent.magMaster[parent.magMaster.length - 1] + ΔmBy2;
       double rMax = parent.groundMotionModels().maxDistance();
+      double distanceBin = parent.distanceBin.orElseThrow();
       double[] smoothingOffsets = smoothingOffsets(
           parent.smoothingDensity,
           parent.gridSpacing);
 
       IntervalTable.Builder ssTableBuilder = new IntervalTable.Builder()
-          .rows(0.0, rMax, parent.distanceBin)
+          .rows(0.0, rMax, distanceBin)
           .columns(mMin, mMax, Δm);
 
       IntervalTable.Builder rTableBuilder = new IntervalTable.Builder()
-          .rows(0.0, rMax, parent.distanceBin)
+          .rows(0.0, rMax, distanceBin)
           .columns(mMin, mMax, Δm);
 
       IntervalTable.Builder nTableBuilder = new IntervalTable.Builder()
-          .rows(0.0, rMax, parent.distanceBin)
+          .rows(0.0, rMax, distanceBin)
           .columns(mMin, mMax, Δm);
 
       // XySequence srcMfdSum = null;
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/model/SourceConfig.java b/src/main/java/gov/usgs/earthquake/nshmp/model/SourceConfig.java
index d08c01fbcf21ed7bb077cc3d7c81663d0925296b..9254007e925aa350aa4b2d515fd90c90d5c97406 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/model/SourceConfig.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/model/SourceConfig.java
@@ -8,6 +8,7 @@ import static gov.usgs.earthquake.nshmp.data.DoubleData.checkInRange;
 import java.nio.file.Path;
 import java.util.Map;
 import java.util.Optional;
+import java.util.OptionalDouble;
 
 import com.google.common.collect.Range;
 
@@ -178,7 +179,7 @@ abstract class SourceConfig {
     final double gridSpacing;
     final int smoothingDensity;
     final double smoothingLimit;
-    final double distanceBin;
+    final OptionalDouble distanceBin;
     final PointSourceType pointSourceType;
     final RuptureScaling ruptureScaling;
     final double maxDepth;
@@ -210,7 +211,7 @@ abstract class SourceConfig {
       private Double gridSpacing;
       private Integer smoothingDensity;
       private Double smoothingLimit;
-      private Double distanceBin;
+      private OptionalDouble distanceBin;
       private PointSourceType pointSourceType;
       private RuptureScaling ruptureScaling;
       private Double maxDepth;
@@ -242,8 +243,8 @@ abstract class SourceConfig {
 
       private static final Range<Double> DISTANCE_RANGE = Range.closed(0.01, 5.0);
 
-      Builder distanceBin(double distance) {
-        checkInRange(DISTANCE_RANGE, "Distance bin", distance);
+      Builder distanceBin(OptionalDouble distance) {
+        distance.ifPresent(d -> checkInRange(DISTANCE_RANGE, "Distance bin", d));
         this.distanceBin = distance;
         return this;
       }