diff --git a/src/main/java/gov/usgs/earthquake/nshmp/calc/CalcConfig.java b/src/main/java/gov/usgs/earthquake/nshmp/calc/CalcConfig.java
index 3945369d7dc145929938c43af79328ac9e97a9df..b1b08fab85a6223ba65e40a7864a16005c794510 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/calc/CalcConfig.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/calc/CalcConfig.java
@@ -77,9 +77,6 @@ public final class CalcConfig {
   /** Earthquake rate configuration. */
   public final Rate rate;
 
-  /** Default site settings. */
-  public final SiteDefaults site;
-
   /** Output configuration. */
   public final Output output;
 
@@ -88,7 +85,6 @@ public final class CalcConfig {
 
   private CalcConfig(Builder builder) {
     hazard = builder.hazard.build();
-    site = builder.site.build();
     performance = builder.performance.build();
     output = builder.output.build();
     disagg = builder.disagg.build();
@@ -101,7 +97,6 @@ public final class CalcConfig {
   public static Builder copyOf(CalcConfig config) {
     Builder b = new Builder();
     b.hazard.copy(config.hazard);
-    b.site.copy(config.site);
     b.performance.copy(config.performance);
     b.output.copy(config.output);
     b.disagg.copy(config.disagg);
@@ -688,87 +683,6 @@ public final class CalcConfig {
     }
   }
 
-  /**
-   * Default site settings.
-   */
-  public static final class SiteDefaults {
-
-    /**
-     * The default average shear-wave velocity down to 30 meters depth.
-     *
-     * <p><b>Default:</b> {@code 760.0} m/sec
-     */
-    public final double vs30;
-
-    /**
-     * Whether Vs30 was inferred, {@code true}, or measured, {@code false}.
-     *
-     * <p><b>Default:</b> {@code true} (inferred)
-     */
-    public final boolean vsInferred;
-
-    /**
-     * Depth to the shear-wave velocity horizon of 1.0 km/sec, in km.
-     *
-     * <p><b>Default:</b> {@code NaN} ({@link GroundMotionModel}s will use a
-     * default value or model)
-     */
-    public final double z1p0;
-
-    /**
-     * Depth to the shear-wave velocity horizon of 2.5 km/sec, in km;
-     *
-     * <p><b>Default:</b> {@code NaN} ({@link GroundMotionModel}s will use a
-     * default value or model)
-     */
-    public final double z2p5;
-
-    private SiteDefaults(Builder b) {
-      this.vs30 = b.vs30;
-      this.vsInferred = b.vsInferred;
-      this.z1p0 = b.z1p0;
-      this.z2p5 = b.z2p5;
-    }
-
-    private static final class Builder {
-
-      Double vs30;
-      Boolean vsInferred;
-      Double z1p0;
-      Double z2p5;
-
-      SiteDefaults build() {
-        checkNotNull(vs30);
-        checkNotNull(vsInferred);
-        z1p0 = (z1p0 == null) ? Double.NaN : z1p0;
-        z2p5 = (z2p5 == null) ? Double.NaN : z2p5;
-        return new SiteDefaults(this);
-      }
-
-      void copy(SiteDefaults that) {
-        this.vs30 = that.vs30;
-        this.vsInferred = that.vsInferred;
-        this.z1p0 = that.z1p0;
-        this.z2p5 = that.z2p5;
-      }
-
-      void extend(Builder that) {
-        if (that.vs30 != null) {
-          this.vs30 = that.vs30;
-        }
-        if (that.vsInferred != null) {
-          this.vsInferred = that.vsInferred;
-        }
-        if (that.z1p0 != null) {
-          this.z1p0 = that.z1p0;
-        }
-        if (that.z2p5 != null) {
-          this.z2p5 = that.z2p5;
-        }
-      }
-    }
-  }
-
   /**
    * Data and file output settings.
    */
@@ -931,21 +845,9 @@ public final class CalcConfig {
       .setPrettyPrinting()
       .enableComplexMapKeySerialization()
       .serializeNulls()
-      .registerTypeAdapter(Double.class, new DoubleSerializer())
       .registerTypeHierarchyAdapter(Path.class, new PathConverter())
       .create();
 
-  private static class DoubleSerializer implements JsonSerializer<Double> {
-
-    @Override
-    public JsonElement serialize(
-        Double value,
-        Type type,
-        JsonSerializationContext context) {
-      return Double.isNaN(value) ? null : new JsonPrimitive(value);
-    }
-  }
-
   private static class PathConverter implements JsonSerializer<Path>, JsonDeserializer<Path> {
 
     @Override
@@ -984,7 +886,6 @@ public final class CalcConfig {
   public static final class Builder {
 
     private Hazard.Builder hazard;
-    private SiteDefaults.Builder site;
     private Performance.Builder performance;
     private Output.Builder output;
     private Disagg.Builder disagg;
@@ -992,7 +893,6 @@ public final class CalcConfig {
 
     private Builder() {
       hazard = new Hazard.Builder();
-      site = new SiteDefaults.Builder();
       performance = new Performance.Builder();
       output = new Output.Builder();
       disagg = new Disagg.Builder();
@@ -1006,7 +906,6 @@ public final class CalcConfig {
     public Builder extend(Builder that) {
       checkNotNull(that);
       this.hazard.extend(that.hazard);
-      this.site.extend(that.site);
       this.performance.extend(that.performance);
       this.output.extend(that.output);
       this.disagg.extend(that.disagg);
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardCalcs.java b/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardCalcs.java
index 11ad060bb847f8e760fc460d45375001368b787c..3ad3434d9eeed6cde35a5ba192b404defa29e3ef 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardCalcs.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardCalcs.java
@@ -141,7 +141,7 @@ public class HazardCalcs {
       Site site,
       Executor ex) throws InterruptedException, ExecutionException {
 
-    AsyncList<HazardCurveSet> curveSets = AsyncList.createWithCapacity(model.size());
+    AsyncList<HazardCurveSet> curveSets = AsyncList.create();
     AsyncList<RuptureSet<? extends Source>> gridTables = AsyncList.create();
     var settingFilter = model.settingFilter(config, site.location());
     var typeFilter = new SourceTypeFilter(config);
@@ -209,7 +209,7 @@ public class HazardCalcs {
       Site site,
       Logger log) {
 
-    List<HazardCurveSet> curveSets = new ArrayList<>(model.size());
+    List<HazardCurveSet> curveSets = new ArrayList<>();
     var settingsFilter = model.settingFilter(config, site.location());
     var typeFilter = new SourceTypeFilter(config);
 
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java b/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java
index cde75e2aadc6bffddc2345d7e33e8e9a2f96b4a8..c29b0bdf21b0e5665ca99915171f9dfa83bba560 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java
@@ -66,12 +66,16 @@ public final class Sites {
     /*
      * Site parameter precedence:
      *
-     * (3) Defaults from site builder and config (2) site data (1) sites file
+     * (4) Defaults from Site.java, (3) site-data from model, (2) sites file,
+     * and (1) Vs30 from config
      *
      * If a sites file contains columns for basin depths, a 'null' value string
      * indicates a GMM should do whatever it defines as default behavior and
      * will override values that may be included in the site data of a model. If
-     * the value is empty, then whatever values from (2) or (3) will be used.
+     * the value is empty, then whatever values from (3) or (4) will be used. If
+     * the config.hazard.vs30s array is not empty, this indicates we're running
+     * batch jobs across multiple site classes and the vs30 of the job will
+     * override all lower precedence vs30 values.
      */
 
     Location loc = Location.create(
@@ -109,13 +113,7 @@ public final class Sites {
       CalcConfig config,
       SiteData siteData) {
 
-    Site.Builder builder = Site.builder()
-        .location(loc)
-        .vs30(config.site.vs30)
-        .vsInferred(config.site.vsInferred)
-        .z1p0(config.site.z1p0)
-        .z2p5(config.site.z2p5);
-
+    Site.Builder builder = Site.builder().location(loc);
     SiteData.Values sdValues = siteData.get(loc);
     sdValues.z1p0.ifPresent(builder::z1p0);
     sdValues.z2p5.ifPresent(builder::z2p5);
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java b/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java
index 98f2003d3f63869675306d050efe9afa6c5ceb51..c89c991bcc2d4d6706499add1a9f55be8f802099 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java
@@ -43,12 +43,13 @@ import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass;
 
 /**
  * A {@code HazardModel} is the top-level wrapper for earthquake {@link Source}
- * definitions and attendant {@link GroundMotionModel}s used in probabilisitic
+ * definitions and attendant {@link GroundMotionModel}s used in probabilistic
  * seismic hazard analyses (PSHAs) and related calculations. A
- * {@code HazardModel} contains of a number of {@link RuptureSet}s that define
- * logical groupings of sources by {@link SourceType}. Each {@code RuptureSet}
- * carries with it references to the {@code GroundMotionModel}s and associated
- * weights to be used when evaluating hazard.
+ * {@code HazardModel} is a List of {@link SourceTree}s that define logical
+ * trees of sources. The tips of the source logic tree branches are
+ * {@code RuptureSet}s that carry the necessary information to carries with it
+ * references to the {@code GroundMotionModel}s and associated weights to be
+ * used when evaluating hazard.
  *
  * @author U.S. Geological Survey
  * @see Source
@@ -87,8 +88,8 @@ public final class HazardModel implements Iterable<SourceTree> {
    *
    * <p>For more information on a HazardModel directory and file structure, see
    * the <a
-   * href="https://github.com/usgs/nshmp-haz/wiki/Earthquake-Source-Models">
-   * nshmp-haz wiki</a>.
+   * href="https://code.usgs.gov/ghsc/nshmp/nshmp-haz/-/blob/main/docs/pages/Hazard-Model.md">
+   * nshmp-haz documentation</a>.
    *
    * <p><b>Notes:</b> HazardModel loading is not thread safe. There are also a
    * wide variety of exceptions that may be encountered when loading a model.
@@ -103,10 +104,10 @@ public final class HazardModel implements Iterable<SourceTree> {
   }
 
   /**
-   * The number of {@code RuptureSet}s in this {@code HazardModel}.
+   * The number of {@code SourceTree}s in this model.
    */
-  public int size() { // TODO this should be the number of trees
-    return ruptureSetMap.size();
+  public int size() {
+    return idMap.size();
   }
 
   @Override
diff --git a/src/main/resources/calc/calc-config-defaults.json b/src/main/resources/calc/calc-config-defaults.json
index d23efcf322afe43f6f7d847e861926c1c83107e3..35ab5ca37a20f35b4d907bf3dc7d3cbfc40f52d1 100644
--- a/src/main/resources/calc/calc-config-defaults.json
+++ b/src/main/resources/calc/calc-config-defaults.json
@@ -43,15 +43,6 @@
     "valueFormat": "ANNUAL_RATE",
     "timespan": 30.0
   },
-  "siteData": {
-    "service": null
-  },
-  "site": {
-    "vs30": 760.0,
-    "vsInferred": true,
-    "z1p0": null,
-    "z2p5": null
-  },
   "output": {
     "directory": "hazout",
     "dataTypes": [
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/calc/CalcConfigTests.java b/src/test/java/gov/usgs/earthquake/nshmp/calc/CalcConfigTests.java
index 3010f5baa6b29b3ca95453c8d25bc303c1c46649..bc606ae9a4f0e855dbc5f2470d7f50d5beb45ebb 100644
--- a/src/test/java/gov/usgs/earthquake/nshmp/calc/CalcConfigTests.java
+++ b/src/test/java/gov/usgs/earthquake/nshmp/calc/CalcConfigTests.java
@@ -121,8 +121,6 @@ class CalcConfigTests {
         .timespan(timespan)
         .build();
     assertEquals(imts, config.hazard.imts);
-    System.out.println(dataTypes);
-    System.out.println(config.output.dataTypes);
     assertEquals(dataTypes, config.output.dataTypes);
     assertEquals(distance, config.rate.distance);
     assertEquals(timespan, config.rate.timespan);
@@ -340,27 +338,6 @@ class CalcConfigTests {
     assertEquals(30.0, def.timespan);
   }
 
-  @Test
-  void testSiteMember() {
-    CalcConfig.SiteDefaults def = DEFAULTS.site;
-    assertEquals(760.0, def.vs30);
-    assertEquals(true, def.vsInferred);
-    assertEquals(Double.NaN, def.z1p0);
-    assertEquals(Double.NaN, def.z2p5);
-
-    def = EXTENDS.site;
-    assertEquals(530.0, def.vs30);
-    assertEquals(false, def.vsInferred);
-    assertEquals(0.3, def.z1p0);
-    assertEquals(1.0, def.z2p5);
-
-    def = EXTENDS_EMPTY.site;
-    assertEquals(760.0, def.vs30);
-    assertEquals(true, def.vsInferred);
-    assertEquals(Double.NaN, def.z1p0);
-    assertEquals(Double.NaN, def.z2p5);
-  }
-
   @Test
   void testOutputMember() {
     List<Integer> defaultReturnPeroiods = List.of(475, 975, 2475, 10000);
diff --git a/src/test/resources/calc/calc-config-extends.json b/src/test/resources/calc/calc-config-extends.json
index 0c6c907131bbcb38d6ca73b122534d7ba19bc7d7..37f44268de927407d0d93cdcf780aae14a0eaa04 100644
--- a/src/test/resources/calc/calc-config-extends.json
+++ b/src/test/resources/calc/calc-config-extends.json
@@ -41,15 +41,6 @@
     "valueFormat": "POISSON_PROBABILITY",
     "timespan": 20.0
   },
-  "siteData": {
-    "service": "http://localhost"
-  },
-  "site": {
-    "vs30": 530.0,
-    "vsInferred": false,
-    "z1p0": 0.3,
-    "z2p5": 1.0
-  },
   "output": {
     "directory": "custom",
     "dataTypes": [