Skip to content

Commit

Permalink
add sim state pipeline (#24)
Browse files Browse the repository at this point in the history
* add sim state pipeline

* update filename
  • Loading branch information
bischoffz authored Jul 24, 2024
1 parent a143851 commit 1a542b3
Show file tree
Hide file tree
Showing 13 changed files with 418 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package gov.hhs.aspr.ms.gcm.pipeline.pipelines;

import java.nio.file.Path;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;

import gov.hhs.aspr.ms.gcm.pipeline.input.SimulationStatePipelineInput;
import gov.hhs.aspr.ms.gcm.pipeline.support.GCMPipelineFileHeaders;
import gov.hhs.aspr.ms.gcm.pipeline.IPipeline;
import gov.hhs.aspr.ms.gcm.simulation.nucleus.SimulationState;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.nucleus.NucleusTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.nucleus.input.SimulationStateInput;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.nucleus.translationSpecs.SimulationStateTranslationSpec;
import gov.hhs.aspr.ms.taskit.core.TranslationController;
import gov.hhs.aspr.ms.taskit.core.TranslationEngineType;
import gov.hhs.aspr.ms.taskit.protobuf.ProtobufTranslationEngine;
import gov.hhs.aspr.ms.util.errors.ContractException;
import gov.hhs.aspr.ms.util.readers.TextTableReader;
import gov.hhs.aspr.ms.util.resourcehelper.ResourceError;
import gov.hhs.aspr.ms.util.resourcehelper.ResourceHelper;

/**
* A utility for constructing the SimulationState file from the
* simStateSettingsFile
*/
public class SimulationStatePipeline implements IPipeline {

/**
* Given a {@link SimulationStatePipelineInput}, an input directory path and an
* output directory path, return a {@link SimulationStatePipeline}
*
* @throws ContractException
* <ul>
* <li>{@link ResourceError#UNKNOWN_FILE} if any of
* the input paths are not valid</li>
* <li>{@link ResourceError#FILE_PATH_IS_DIRECTORY} if
* any of the input paths or output path point to a directory</li>
* </ul>
*/
public static SimulationStatePipeline from(SimulationStatePipelineInput pipelineInput, Path inputDirectory,
Path outputDirectory) {
String simStateSettingsFile = inputDirectory.resolve(pipelineInput.getSimulationSettingsFile()).toString();
String simStateFile = outputDirectory.resolve(pipelineInput.getSimulationStateFile()).toString();

Path simStateSettingsPath = ResourceHelper.validateFile(simStateSettingsFile);

Path simStatePath = ResourceHelper.validateFilePath(simStateFile);

return new SimulationStatePipeline(simStateSettingsPath, simStatePath);
}

private final Path simStateSettingsFile;

private final Path simStateFile;

private SimulationStatePipeline(Path simStateSettingsFile, Path simStateDataFile) {
this.simStateSettingsFile = simStateSettingsFile;
this.simStateFile = simStateDataFile;
}

/**
* Executes the pipeline with the given input files
* <p>
* writes out the resulting simState file to the path provided
*/
public void execute() {
SimulationState.Builder builder = SimulationState.builder();

System.out.println("SimulationState Pipeline::loading simulation state settings file");
loadSimStateSettingsFile(builder);

System.out.println("SimulationState Pipeline::translating simulation state");
// create the translation engine and have it use the nucleus translator and
ProtobufTranslationEngine protobufTranslationEngine = ProtobufTranslationEngine.builder()//
.addTranslator(NucleusTranslator.getTranslator())//
.build();//

SimulationStateTranslationSpec translationSpec = new SimulationStateTranslationSpec();
translationSpec.init(protobufTranslationEngine);

SimulationStateInput input = translationSpec.convert(builder.build());
builder = null;

System.out.println("SimulationState Pipeline::writing simulation state");
// build the translation controller and have it write the SimulationState file
TranslationController.builder()
.addTranslationEngine(protobufTranslationEngine)//
.build()//
.writeOutput(input, simStateFile, TranslationEngineType.PROTOBUF);//

System.out.println("SimulationState Pipeline::done");

}

private void loadSimStateSettingsFile(SimulationState.Builder builder) {
List<DateTimeFormatter> formatters = new ArrayList<>();

formatters.add(DateTimeFormatter.ofPattern("M-d-uuuu"));
formatters.add(DateTimeFormatter.ofPattern("M/d/uuuu"));
formatters.add(DateTimeFormatter.ofPattern("uuuu-M-d"));
formatters.add(DateTimeFormatter.ofPattern("uuuu/M/d"));

TextTableReader.read(",", GCMPipelineFileHeaders.SIM_STATE_SETTINGS, simStateSettingsFile, (values) -> {
switch (values[0]) {
case "start_time":
builder.setStartTime(Double.parseDouble(values[1]));
break;
case "base_date":
LocalDate localDate;
RuntimeException runtimeException = null;
for (DateTimeFormatter formatter : formatters) {
try {
localDate = LocalDate.parse(values[1], formatter);
builder.setBaseDate(localDate);
runtimeException = null;
break;
} catch (DateTimeParseException e) {
runtimeException = new RuntimeException(e);
}
}

if (runtimeException != null) {
throw runtimeException;
}

break;
// plans are not currently supported
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gov.hhs.aspr.ms.gcm.pipeline.support;

public class GCMPipelineFileHeaders {

private GCMPipelineFileHeaders() {
}

public static String SIM_STATE_SETTINGS = "property,value";
}
10 changes: 10 additions & 0 deletions src/main/proto/gov/hhs/aspr/ms/gcm/pipeline/pipeline.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
syntax = "proto3";
package gov.hhs.aspr.ms.gcm.pipeline;

option java_multiple_files = true;
option java_package = "gov.hhs.aspr.ms.gcm.pipeline.input";

message SimulationStatePipelineInput {
string simulationSettingsFile = 1;
string simulationStateFile = 2;
}
35 changes: 35 additions & 0 deletions src/test/java/gov/hhs/aspr/ms/gcm/pipeline/PipelineTestHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package gov.hhs.aspr.ms.gcm.pipeline;

import gov.hhs.aspr.ms.gcm.taskit.protobuf.nucleus.NucleusTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.plugins.groups.GroupsTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.plugins.people.PeopleTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.plugins.personproperties.PersonPropertiesTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.plugins.properties.PropertiesTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.plugins.regions.RegionsTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.plugins.reports.ReportsTranslator;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.plugins.stochastics.StochasticsTranslator;
import gov.hhs.aspr.ms.taskit.protobuf.ProtobufTranslationEngine;

/**
* This class is to help test the pipelines
*/
public class PipelineTestHelper {

/*
* static method to return a new protobuf translation engine.
*
* used in a method due to nature of static access
*/
public static ProtobufTranslationEngine getProtobufTranslationEngine() {
return ProtobufTranslationEngine.builder()
.addTranslator(PeopleTranslator.getTranslator())
.addTranslator(PersonPropertiesTranslator.getTranslator())
.addTranslator(GroupsTranslator.getTranslator())
.addTranslator(RegionsTranslator.getTranslator())
.addTranslator(ReportsTranslator.getTranslator())
.addTranslator(StochasticsTranslator.getTranslator())
.addTranslator(PropertiesTranslator.getTranslator())
.addTranslator(NucleusTranslator.getTranslator())
.build();
}
}
92 changes: 92 additions & 0 deletions src/test/java/gov/hhs/aspr/ms/gcm/pipeline/PipelineTestPaths.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package gov.hhs.aspr.ms.gcm.pipeline;

import java.nio.file.Path;

import gov.hhs.aspr.ms.util.resourcehelper.ResourceHelper;

/**
* This is a class that contains all of the paths for all pipeline test output
* files and the test resource directory.
*
* When adding a new dimension or plugin data, first add the file name in the
* respective section, and then add the path in the respective section.
*/
public class PipelineTestPaths {
// name of expected output directory
private static final String EXPECTED_OUTPUT_DIR_NAME = "expectedOutput";

// name of test output directory
private static final String TEST_OUTPUT_DIR_NAME = "testOutput";

// name of pipelineInput directory
private static final String PIPELINE_INPUT_DIR_NAME = "pipelineInput";

// name of each of the pipeline input files
private static final String SIM_STATE_PIPELINE_INPUT_F_NAME = "simulationStatePipeline.json";

// name of each the plugin data files
private static final String POPULATION_P_DATA_F_NAME = "populationPluginData.json";
private static final String PEOPLE_P_DATA_F_NAME = "peoplePluginData.json";
private static final String PEOPLE_P_DATA_EMPTY_F_NAME = "peoplePluginData_empty.json";
private static final String PERSON_PROPS_P_DATA_F_NAME = "personPropertiesPluginData.json";
private static final String GROUPS_P_DATA_F_NAME = "groupsPluginData.json";
private static final String REGIONS_P_DATA_F_NAME = "regionsPluginData.json";
private static final String STOCHASTICS_P_DATA_F_NAME = "stochasticsPluginData.json";

// name of gcm experiment params and sim state files
private static final String EXPERIMENT_PARAMS_F_NAME = "experimentParameterData.json";
private static final String SIM_STATE_F_NAME = "simulationState.json";

// Absolute path to the resource directory used in testing
public static final Path RESOURCE_DIR = ResourceHelper.getResourceDir(PipelineTestPaths.class);

// absolute path to the expected output directory based on the resource dir
// above
public static final Path EXPECTED_OUTPUT_DIR = getResolvedResourcePath(EXPECTED_OUTPUT_DIR_NAME);

// absolute path to the test output directory based on the resource dir above
public static final Path TEST_OUTPUT_DIR = getResolvedResourcePath(TEST_OUTPUT_DIR_NAME);

// absolute path to the pipeline input directory based on the resource dir above
public static final Path PIPELINE_INPUT_DIR = getResolvedResourcePath(PIPELINE_INPUT_DIR_NAME);

// absolute paths to each of the pipeline input files
public static final Path SIM_STATE_PIPELINE = getResolvedPipelineInputPath(SIM_STATE_PIPELINE_INPUT_F_NAME);

// absolute paths to each of the expected output plugin data files
public static final Path EXPECTED_POPULATION_PLUGIN_DATA = getResolvedOutputPath(POPULATION_P_DATA_F_NAME);
public static final Path EXPECTED_PEOPLE_PLUGIN_DATA = getResolvedOutputPath(PEOPLE_P_DATA_F_NAME);
public static final Path EXPECTED_PEOPLE_PLUGIN_DATA_EMPTY = getResolvedOutputPath(PEOPLE_P_DATA_EMPTY_F_NAME);
public static final Path EXPECTED_PERSON_PROPS_PLUGIN_DATA = getResolvedOutputPath(PERSON_PROPS_P_DATA_F_NAME);
public static final Path EXPECTED_GROUPS_PLUGIN_DATA = getResolvedOutputPath(GROUPS_P_DATA_F_NAME);
public static final Path EXPECTED_REGIONS_PLUGIN_DATA = getResolvedOutputPath(REGIONS_P_DATA_F_NAME);
public static final Path EXPECTED_STOCHASTICS_PLUGIN_DATA = getResolvedOutputPath(STOCHASTICS_P_DATA_F_NAME);

// absolute paths to the gcm experiment params and sim state files
public static final Path EXPECTED_EXPERIMENT_PARAMETER_DATA = getResolvedOutputPath(EXPERIMENT_PARAMS_F_NAME);
public static final Path EXPECTED_SIMULATION_STATE = getResolvedOutputPath(SIM_STATE_F_NAME);

private PipelineTestPaths() {
}

/**
* Given a path, return it resolved against the resource directory path
*/
public static Path getResolvedResourcePath(String path) {
return RESOURCE_DIR.resolve(path).toAbsolutePath();
}

/**
* Given a path, return it resolved against the expected output directory path
*/
public static Path getResolvedOutputPath(String path) {
return EXPECTED_OUTPUT_DIR.resolve(path);
}

/**
* Given a path, return it resolved against the pipeline input directory path
*/
public static Path getResolvedPipelineInputPath(String path) {
return PIPELINE_INPUT_DIR.resolve(path);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package gov.hhs.aspr.ms.gcm.pipeline.pipelines;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.nio.file.Path;
import java.time.format.DateTimeParseException;

import org.junit.jupiter.api.Test;

import gov.hhs.aspr.ms.gcm.pipeline.PipelineTestHelper;
import gov.hhs.aspr.ms.gcm.pipeline.PipelineTestPaths;
import gov.hhs.aspr.ms.gcm.pipeline.input.SimulationStatePipelineInput;
import gov.hhs.aspr.ms.gcm.pipeline.testsupport.PipelineTestSupport;
import gov.hhs.aspr.ms.gcm.simulation.nucleus.SimulationState;
import gov.hhs.aspr.ms.gcm.taskit.protobuf.nucleus.input.SimulationStateInput;
import gov.hhs.aspr.ms.util.annotations.UnitTestMethod;
import gov.hhs.aspr.ms.util.errors.ContractException;
import gov.hhs.aspr.ms.util.resourcehelper.ResourceError;
import gov.hhs.aspr.ms.util.resourcehelper.ResourceHelper;

public class AT_SimulationStatePipeline {
PipelineTestSupport<SimulationStatePipelineInput> pipelineInputTestSupport = new PipelineTestSupport<>(
PipelineTestHelper.getProtobufTranslationEngine(),
SimulationStatePipelineInput.getDefaultInstance(), SimulationStatePipelineInput.class,
PipelineTestPaths::getResolvedResourcePath, PipelineTestPaths.TEST_OUTPUT_DIR);

SimulationStatePipelineInput unresolvedPipelineInput = pipelineInputTestSupport
.getUnresolvedPipelineInput(PipelineTestPaths.SIM_STATE_PIPELINE.toString());

SimulationStatePipelineInput resolvedPipelineInput = pipelineInputTestSupport
.getResolvedPipelineInput(unresolvedPipelineInput, false, false);

@Test
@UnitTestMethod(target = SimulationStatePipeline.class, name = "from", args = {
SimulationStatePipelineInput.class,
Path.class, Path.class })
public void testFrom() {

Path inputPath = Path.of("");
Path outputPath = Path.of("");

SimulationStatePipelineInput pipelineInput = resolvedPipelineInput;

SimulationStatePipeline pipeline = SimulationStatePipeline.from(pipelineInput, inputPath, outputPath);

assertNotNull(pipeline);

// preconditions
// simStateSettingsFile is invalid
ContractException contractException = assertThrows(ContractException.class, () -> {
SimulationStatePipeline.from(
pipelineInput.toBuilder().setSimulationSettingsFile("invalidPath").build(),
inputPath, outputPath);
});

assertEquals(ResourceError.UNKNOWN_FILE, contractException.getErrorType());
}

@Test
@UnitTestMethod(target = SimulationStatePipeline.class, name = "execute", args = {})
public void testExecute() {

Path resourceDir = PipelineTestPaths.RESOURCE_DIR;

Path inputPath = Path.of("");
Path outputPath = Path.of("");

SimulationStatePipelineInput pipelineInput = resolvedPipelineInput;

Path dataFile = resourceDir.resolve(pipelineInput.getSimulationStateFile());

ResourceHelper.createDirectory(dataFile.getParent());
ResourceHelper.createFile(dataFile.getParent(), dataFile.getFileName().toString());

SimulationStatePipeline pipeline = SimulationStatePipeline.from(pipelineInput, inputPath, outputPath);

pipeline.execute();

assertTrue(pipelineInputTestSupport.filesAreSame(SimulationStateInput.class,
SimulationState.class, PipelineTestPaths.EXPECTED_SIMULATION_STATE, dataFile));

// preconditions
// base date is invalid
SimulationStatePipelineInput badDate = pipelineInput.toBuilder()
.setSimulationSettingsFile(pipelineInput.getSimulationSettingsFile()
.replace("simulation_settings.csv", "simulation_settings_bad_date.csv"))
.build();

RuntimeException runtimeException = assertThrows(RuntimeException.class, () -> {
SimulationStatePipeline.from(badDate, inputPath, outputPath).execute();
});

assertEquals(DateTimeParseException.class, runtimeException.getCause().getClass());
}
}
Loading

0 comments on commit 1a542b3

Please sign in to comment.