diff --git a/src/main/java/gov/usgs/earthquake/nshmp/calc/EqRateExport.java b/src/main/java/gov/usgs/earthquake/nshmp/calc/EqRateExport.java
index 3629306b7991b226f375ac5c0c3f5903c1b7ae4f..9d2e2e10fc77118fdfbd88fc7095cc234a512f31 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/calc/EqRateExport.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/calc/EqRateExport.java
@@ -1,19 +1,15 @@
 package gov.usgs.earthquake.nshmp.calc;
 
-import static com.google.common.base.Preconditions.checkState;
 import static java.nio.file.StandardOpenOption.APPEND;
 
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.OptionalDouble;
 import java.util.function.Function;
-import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import java.util.stream.DoubleStream;
 
-import com.google.common.base.Stopwatch;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -38,33 +34,25 @@ public final class EqRateExport {
   private static final String RATE_FILE = "rates.csv";
   private static final String PROB_FILE = "probs.csv";
 
-  private final Logger log;
-  private final Path dir;
+  private final Path out;
   private final String file;
   private final String valueFormat;
   private final HazardModel model;
   private final boolean exportSource;
 
-  private final Stopwatch batchWatch;
-  private final Stopwatch totalWatch;
-  private int batchCount = 0;
-  private int resultCount = 0;
-
   private final boolean namedSites;
   private boolean firstRecord = true;
-  private boolean used = false;
 
   private EqRateExport(
       HazardModel model,
       CalcConfig config,
       List<Site> sites,
-      Logger log) throws IOException {
+      Path out) {
 
     // whether or not rates or probabilities have been calculated
     boolean rates = config.rate.valueFormat == ValueFormat.ANNUAL_RATE;
 
-    this.log = log;
-    this.dir = HazardExport.createOutputDir(config.output.directory, OptionalDouble.empty());
+    this.out = out;
     this.file = rates ? RATE_FILE : PROB_FILE;
     this.valueFormat = rates ? RATE_FORMAT : PROB_FORMAT;
     this.model = model;
@@ -72,9 +60,6 @@ public final class EqRateExport {
 
     Site demoSite = sites.get(0);
     this.namedSites = demoSite.name() != Site.NO_NAME;
-
-    this.batchWatch = Stopwatch.createStarted();
-    this.totalWatch = Stopwatch.createStarted();
   }
 
   /**
@@ -83,15 +68,16 @@ public final class EqRateExport {
    * @param model being run
    * @param config that specifies output options and formats
    * @param sites reference to the sites to be processed (not retained)
-   * @param log shared logging instance from calling class
+   * @param out results directory; this may have been been modified from
+   *        {@code config.output.directory} by calling program
    */
   public static EqRateExport create(
       HazardModel model,
       CalcConfig config,
       List<Site> sites,
-      Logger log) throws IOException {
+      Path out) {
 
-    return new EqRateExport(model, config, sites, log);
+    return new EqRateExport(model, config, sites, out);
   }
 
   /**
@@ -100,54 +86,6 @@ public final class EqRateExport {
    * @param rate data container to add
    */
   public void write(EqRate rate) throws IOException {
-    checkState(!used, "This result handler is expired");
-    writeRate(rate);
-    resultCount++;
-    if (resultCount % 10 == 0) {
-      batchCount++;
-      log.info(String.format(
-          "     batch: %s in %s – %s sites in %s",
-          batchCount, batchWatch, resultCount, totalWatch));
-      batchWatch.reset().start();
-    }
-  }
-
-  /**
-   * Flushes any remaining results, stops all timers and sets the state of this
-   * exporter to 'used'; no more results may be added.
-   */
-  public void expire() throws IOException {
-    batchWatch.stop();
-    totalWatch.stop();
-    used = true;
-  }
-
-  /**
-   * The number of rate results passed to this handler thus far.
-   */
-  public int resultCount() {
-    return resultCount;
-  }
-
-  /**
-   * A string representation of the time duration that this result handler has
-   * been running.
-   */
-  public String elapsedTime() {
-    return totalWatch.toString();
-  }
-
-  /**
-   * The target output directory established by this handler.
-   */
-  public Path outputDir() {
-    return dir;
-  }
-
-  /*
-   * Write the current list of {@code EqRate}s to file.
-   */
-  private void writeRate(EqRate rate) throws IOException {
 
     Iterable<Double> emptyValues = Doubles.asList(new double[rate.totalMfd.size()]);
     Function<Double, String> formatter = Parsing.formatDoubleFunction(valueFormat);
@@ -171,10 +109,10 @@ public final class EqRateExport {
     String emptyLine = toLine(locData, emptyValues, formatter);
 
     /* write/append */
-    Path totalFile = dir.resolve(file);
+    Path totalFile = out.resolve(file);
     HazardExport.writeLine(totalFile, totalLine, APPEND);
     if (exportSource) {
-      Path parentDir = dir.resolve(HazardExport.TYPE_DIR);
+      Path parentDir = out.resolve(HazardExport.TYPE_DIR);
       for (SourceType type : model.types()) {
         Path typeFile = parentDir.resolve(type.name()).resolve(file);
         String typeLine = emptyLine;
@@ -192,11 +130,11 @@ public final class EqRateExport {
         Lists.newArrayList(namedSites ? "name" : null, "lon", "lat"),
         demo.totalMfd.xValues().boxed().collect(Collectors.toList()));
     String header = Parsing.join(headerElements, Delimiter.COMMA);
-    Path totalFile = dir.resolve(file);
+    Path totalFile = out.resolve(file);
     HazardExport.writeLine(totalFile, header);
 
     if (exportSource) {
-      Path parentDir = dir.resolve(HazardExport.TYPE_DIR);
+      Path parentDir = out.resolve(HazardExport.TYPE_DIR);
       Files.createDirectory(parentDir);
       for (SourceType type : model.types()) {
         Path typeDir = parentDir.resolve(type.name());
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardExport.java b/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardExport.java
index 451012eceb1c6d5063d09f37085f12a2d1a66357..47ccd92e82dc3bc96579438e7745172c9763a5d1 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardExport.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/calc/HazardExport.java
@@ -1,6 +1,5 @@
 package gov.usgs.earthquake.nshmp.calc;
 
-import static com.google.common.base.Preconditions.checkState;
 import static gov.usgs.earthquake.nshmp.calc.ValueFormat.POISSON_PROBABILITY;
 import static gov.usgs.earthquake.nshmp.data.MutableXySequence.emptyCopyOf;
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -51,21 +50,13 @@ import gov.usgs.earthquake.nshmp.model.SourceType;
  */
 public final class HazardExport {
 
-  /*
-   * Developer notes
-   *
-   * Export of gmm and source type is unreliable for results where multiple
-   * models may have been run (e.g. Hazard2018) because the model.types() and
-   * model.gmms() methods will only reflect a single model.
-   */
-
   private static final String GMM_DIR = "gmm";
   static final String TYPE_DIR = "source";
   private static final String CURVE_FILE_ASCII = "curves.csv";
   private static final String CURVE_FILE_ASCII_TRUNCATED = "curves-truncated.csv";
   private static final String VALUE_FMT = "%.8e";
 
-  private final Path dir;
+  private final Path out;
   private final HazardModel model;
   private final CalcConfig config;
   private final boolean exportGmm;
@@ -73,18 +64,15 @@ public final class HazardExport {
 
   private final Function<Double, String> valueFormatter;
 
-  private int resultCount = 0;
-
   private final boolean namedSites;
-  private boolean used = false;
 
   private HazardExport(
       HazardModel model,
       CalcConfig config,
       List<Site> sites,
-      OptionalDouble vs30) throws IOException {
+      Path out) throws IOException {
 
-    this.dir = createOutputDir(config.output.directory, vs30);
+    this.out = out;
     this.model = model;
     this.config = config;
     this.exportGmm = config.output.dataTypes.contains(DataType.GMM);
@@ -107,16 +95,16 @@ public final class HazardExport {
    * @param model being run
    * @param config that specifies output options and formats
    * @param sites reference to the sites to be processed (not retained)
-   * @param vs30 optional vs30 value used when running multiple site classes
-   * @param log shared logging instance from calling class
+   * @param out results directory; this may have been been modified from
+   *        {@code config.output.directory} by calling program
    */
   public static HazardExport create(
       HazardModel model,
       CalcConfig config,
       List<Site> sites,
-      OptionalDouble vs30) throws IOException {
+      Path out) throws IOException {
 
-    return new HazardExport(model, config, sites, vs30);
+    return new HazardExport(model, config, sites, out);
   }
 
   /* Initialize output directories. */
@@ -124,7 +112,7 @@ public final class HazardExport {
 
     for (Imt imt : config.hazard.imts) {
 
-      Path imtDir = dir.resolve(imt.name());
+      Path imtDir = out.resolve(imt.name());
       Files.createDirectory(imtDir);
       Path totalFile = imtDir.resolve(CURVE_FILE_ASCII);
       Path totalFileTruncated = imtDir.resolve(CURVE_FILE_ASCII_TRUNCATED);
@@ -176,6 +164,7 @@ public final class HazardExport {
     }
     if (vs30.isPresent()) {
       String vs30dir = "vs30-" + ((int) vs30.getAsDouble());
+
       outDir = outDir.resolve(vs30dir);
     }
     Files.createDirectories(outDir);
@@ -188,35 +177,6 @@ public final class HazardExport {
    * @param hazard to write
    */
   public void write(Hazard hazard) throws IOException {
-    checkState(!used, "This result handler is expired");
-    writeHazard(hazard);
-    resultCount++;
-  }
-
-  /**
-   * Stops all timers and sets the state of this {@code Results} instance to
-   * 'used'; no more results may be added.
-   */
-  public void expire() {
-    used = true;
-  }
-
-  /**
-   * The number of hazard results passed to this handler thus far.
-   */
-  public int resultCount() {
-    return resultCount;
-  }
-
-  /**
-   * The target output directory established by this handler.
-   */
-  public Path outputDir() {
-    return dir;
-  }
-
-  /* Write the supplied hazard results to file(s). */
-  private void writeHazard(Hazard hazard) throws IOException {
 
     Set<Gmm> gmms = hazard.model.gmms();
     Set<SourceType> types = hazard.model.types();
@@ -240,7 +200,7 @@ public final class HazardExport {
       String emptyLine = locStr + "0.0" + ",0.0".repeat(totalCurve.size() - 1);
 
       Imt imt = imtEntry.getKey();
-      Path imtDir = dir.resolve(imt.name());
+      Path imtDir = out.resolve(imt.name());
 
       String totalLine = locStr + valueStr(totalCurve, valueFormatter);
       String totalLineTrunc = locStr + truncValueStr(totalCurve, valueFormatter, TRUNCATION_LIMIT);
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/model/LoaderTests.java b/src/test/java/gov/usgs/earthquake/nshmp/model/LoaderTests.java
index d7518714db457d7a47df746e2f3829df35acafe5..0b79509351ee2bb23c1705a18ec4e8230811faa3 100644
--- a/src/test/java/gov/usgs/earthquake/nshmp/model/LoaderTests.java
+++ b/src/test/java/gov/usgs/earthquake/nshmp/model/LoaderTests.java
@@ -214,14 +214,13 @@ class LoaderTests {
       List<Site> sites) throws IOException {
 
     ExecutorService exec = initExecutor(config.performance.threadCount);
-    HazardExport handler = HazardExport.create(model, config, sites, OptionalDouble.empty());
+    HazardExport handler = HazardExport.create(model, config, sites, config.output.directory);
     for (Site site : sites) {
       Hazard hazard = HazardCalcs.hazard(model, config, site, exec);
       handler.write(hazard);
     }
-    handler.expire();
     exec.shutdown();
-    return handler.outputDir();
+    return config.output.directory;
   }
 
   private static ExecutorService initExecutor(ThreadCount threadCount) {