TEST_F(ProjectFixture, RubyMeasureRecord_RubyScript) {
    // Measures
    MeasureVector measures;

    // Null Measure
    measures.push_back(NullMeasure());

    openstudio::path rubyLibDirPath = openstudio::toPath(rubyLibDir());
    openstudio::path perturbScript = rubyLibDirPath/openstudio::toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
    RubyMeasure rubyMeasure(perturbScript,
                            FileReferenceType::OSM,
                            FileReferenceType::OSM);
    rubyMeasure.addArgument("inputPath", "in.osm");
    rubyMeasure.addArgument("outputPath", "out.osm");
    rubyMeasure.addArgument("objectType", "OS:Material");
    rubyMeasure.addArgument("nameRegex", "I02 50mm insulation board");
    rubyMeasure.addArgument("field", "3");
    rubyMeasure.addArgument("value", "0.10");

    // RubyMeasure
    measures.push_back(rubyMeasure);

    // Variables
    VariableVector variables;

    variables.push_back(MeasureGroup("Wall Construction",measures));

    // Workflow
    openstudio::runmanager::Workflow workflow;

    // Problem
    Problem problem("Variable",variables,workflow);

    // Save to database
    {
        ProjectDatabase database = getCleanDatabase("RubyMeasureRecord_RubyScript");

        bool didStartTransaction = database.startTransaction();
        EXPECT_TRUE(didStartTransaction);

        // Problem Record
        ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problem,database);

        database.save();
        if (didStartTransaction) {
            EXPECT_TRUE(database.commitTransaction());
        }

        // Variable Records
        InputVariableRecordVector measureGroupRecords = problemRecord.inputVariableRecords();
        EXPECT_EQ(1u,measureGroupRecords.size());

        // Discrete Variable Record
        MeasureGroupRecord measureGroupRecord = measureGroupRecords.at(0).cast<MeasureGroupRecord>();
        EXPECT_EQ(2u,measureGroupRecord.measureRecordIds(true).size());
        EXPECT_EQ(2u,measureGroupRecord.measureRecords(true).size());
        RubyMeasureRecord rubyMeasureRecord(rubyMeasure,measureGroupRecord,0);
        EXPECT_EQ("MeasureRecords",rubyMeasureRecord.databaseTableName());
        ObjectRecordVector objectRecordVector = rubyMeasureRecord.children();
        EXPECT_EQ(6u,objectRecordVector.size()); // arguments
        objectRecordVector = rubyMeasureRecord.resources();
        EXPECT_EQ(1u,objectRecordVector.size()); // script
        FileReferenceRecord scriptRecord = rubyMeasureRecord.fileReferenceRecord();
        EXPECT_EQ("FileReferenceRecords",scriptRecord.databaseTableName());

        Measure measure = rubyMeasureRecord.measure();
        EXPECT_EQ(true,measure.isSelected());
        ASSERT_TRUE(measure.optionalCast<RubyMeasure>());
        RubyMeasure rubyMeasureCopy = measure.cast<RubyMeasure>();
        EXPECT_FALSE(rubyMeasureCopy.usesBCLMeasure());
        EXPECT_FALSE(rubyMeasureCopy.isUserScript());
        EXPECT_EQ(6u,rubyMeasureCopy.arguments().size());

        MeasureGroupRecord measureGroupRecordFromRuby = rubyMeasureRecord.measureGroupRecord().get();
        EXPECT_EQ(measureGroupRecord.databaseTableName(),measureGroupRecordFromRuby.databaseTableName());
        EXPECT_EQ(measureGroupRecord.id(),measureGroupRecordFromRuby.id());
    }

    // Reopen database
    {
        ProjectDatabase database = getExistingDatabase("RubyMeasureRecord_RubyScript");

        ProblemRecordVector problemRecords = ProblemRecord::getProblemRecords(database);
        ASSERT_FALSE(problemRecords.empty());
        EXPECT_EQ(1u,problemRecords.size());
        ProblemRecord problemRecord = problemRecords[0];

        // COPY-PASTED FROM ABOVE

        // Variable Records
        InputVariableRecordVector measureGroupRecords = problemRecord.inputVariableRecords();
        EXPECT_EQ(1u,measureGroupRecords.size());

        // Discrete Variable Record
        MeasureGroupRecord measureGroupRecord = measureGroupRecords.at(0).cast<MeasureGroupRecord>();
        EXPECT_EQ(2u,measureGroupRecord.measureRecordIds(true).size());
        EXPECT_EQ(2u,measureGroupRecord.measureRecords(true).size());
        RubyMeasureRecord rubyMeasureRecord(rubyMeasure,measureGroupRecord,0);
        EXPECT_EQ("MeasureRecords",rubyMeasureRecord.databaseTableName());
        ObjectRecordVector objectRecordVector = rubyMeasureRecord.children();
        EXPECT_EQ(6u,objectRecordVector.size()); // arguments
        objectRecordVector = rubyMeasureRecord.resources();
        EXPECT_EQ(1u,objectRecordVector.size()); // script
        FileReferenceRecord scriptRecord = rubyMeasureRecord.fileReferenceRecord();
        EXPECT_EQ("FileReferenceRecords",scriptRecord.databaseTableName());

        Measure measure = rubyMeasureRecord.measure();
        EXPECT_EQ(true,measure.isSelected());
        ASSERT_TRUE(measure.optionalCast<RubyMeasure>());
        RubyMeasure rubyMeasureCopy = measure.cast<RubyMeasure>();
        EXPECT_FALSE(rubyMeasureCopy.usesBCLMeasure());
        EXPECT_FALSE(rubyMeasureCopy.isUserScript());
        EXPECT_EQ(6u,rubyMeasureCopy.arguments().size());

        MeasureGroupRecord measureGroupRecordFromRuby = rubyMeasureRecord.measureGroupRecord().get();
        EXPECT_EQ(measureGroupRecord.databaseTableName(),measureGroupRecordFromRuby.databaseTableName());
        EXPECT_EQ(measureGroupRecord.id(),measureGroupRecordFromRuby.id());
    }
}
TEST_F(AnalysisFixture,DDACEAlgorithm_CompatibleProblemType) {
  // continuous problem with five variables
  VariableVector variables;
  BCLMeasure bclMeasure(resourcesPath() / toPath("utilities/BCL/Measures/v2/SetWindowToWallRatioByFacade"));
  RubyMeasure measure(bclMeasure);
  variables.push_back(RubyContinuousVariable("Var 1",OSArgument::makeDoubleArgument("wwr1"),measure));
  variables.push_back(RubyContinuousVariable("Var 2",OSArgument::makeDoubleArgument("wwr2"),measure));
  variables.push_back(RubyContinuousVariable("Var 3",OSArgument::makeDoubleArgument("wwr3"),measure));
  variables.push_back(RubyContinuousVariable("Var 4",OSArgument::makeDoubleArgument("wwr4"),measure));
  variables.push_back(RubyContinuousVariable("Var 5",OSArgument::makeDoubleArgument("wwr5"),measure));
  Problem cProblem("Continuous Problem",variables,runmanager::Workflow());
  EXPECT_EQ(5,cProblem.numVariables());
  EXPECT_EQ(5,cProblem.numContinuousVariables());
  variables.clear();

  // mixed problem with three variables, ignorable discrete variable
  MeasureVector measures;
  variables.push_back(RubyContinuousVariable("Var 1",OSArgument::makeDoubleArgument("wwr1"),measure));
  measures.push_back(RubyMeasure(toPath("script.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Var 2",measures));
  measures.clear();
  variables.push_back(RubyContinuousVariable("Var 3",OSArgument::makeDoubleArgument("wwr3"),measure));
  Problem mProblem("Mixed Problem",variables,runmanager::Workflow());
  EXPECT_EQ(3,mProblem.numVariables());
  EXPECT_EQ(2,mProblem.numContinuousVariables());
  EXPECT_EQ(1,mProblem.numDiscreteVariables());
  EXPECT_EQ(1,mProblem.numStaticTransformations());
  variables.clear();

  // discrete problem
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(toPath("script1.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("wwr","0.2");
  measures.push_back(RubyMeasure(toPath("script1.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("wwr","0.4");
  variables.push_back(MeasureGroup("Var 1",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.0");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.3");
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.5");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.3");
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.0");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.5");
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.5");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.5");
  variables.push_back(MeasureGroup("Var 2",measures));
  measures.clear();
  Problem dProblem("Discrete Problem",variables,runmanager::Workflow());
  EXPECT_EQ(2,dProblem.numVariables());
  EXPECT_EQ(2,dProblem.numDiscreteVariables());
  EXPECT_EQ(0,dProblem.numStaticTransformations());
  variables.clear();

  // box-behnken
  DDACEAlgorithmOptions options(DDACEAlgorithmType::box_behnken);
  DDACEAlgorithm algorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_FALSE(algorithm.isCompatibleProblemType(dProblem));
  options.setSamples(DDACEAlgorithmOptions::samplesForBoxBehnken(cProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(81,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_EQ(5,DDACEAlgorithmOptions::samplesForBoxBehnken(mProblem));
  EXPECT_EQ(0,DDACEAlgorithmOptions::samplesForBoxBehnken(dProblem));

  // central-composite
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::central_composite);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_FALSE(algorithm.isCompatibleProblemType(dProblem));
  options.setSamples(DDACEAlgorithmOptions::samplesForCentralComposite(cProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(43,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_EQ(9,DDACEAlgorithmOptions::samplesForCentralComposite(mProblem));
  EXPECT_EQ(0,DDACEAlgorithmOptions::samplesForCentralComposite(dProblem));

  // grid
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::grid);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_FALSE(algorithm.isCompatibleProblemType(dProblem));
  EXPECT_TRUE(options.setSamplesForGrid(3,cProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().symbols());
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(3,algorithm.ddaceAlgorithmOptions().symbols().get());
  EXPECT_EQ(243,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_TRUE(algorithm.ddaceAlgorithmOptions().setSamplesForGrid(5,mProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().symbols());
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(5,algorithm.ddaceAlgorithmOptions().symbols().get());
  EXPECT_EQ(25,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_FALSE(options.setSamplesForGrid(2,dProblem));

  // lhs
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::lhs);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));

  // oa_lhs
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::oa_lhs);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));

  // oas
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::oas);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));

  // random
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::random);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));
}
TEST_F(AnalysisFixture, DDACEAlgorithmOptions) {

  // problem with three variables
  VariableVector variables;
  BCLMeasure bclMeasure(resourcesPath() / toPath("utilities/BCL/Measures/v2/SetWindowToWallRatioByFacade"));
  RubyMeasure measure(bclMeasure);
  variables.push_back(RubyContinuousVariable("Var 1",OSArgument::makeDoubleArgument("wwr1"),measure));
  variables.push_back(RubyContinuousVariable("Var 2",OSArgument::makeDoubleArgument("wwr2"),measure));
  variables.push_back(RubyContinuousVariable("Var 3",OSArgument::makeDoubleArgument("wwr3"),measure));
  Problem problem("Null Problem",variables,runmanager::Workflow());

  DDACEAlgorithmOptions options(DDACEAlgorithmType::grid);
  EXPECT_EQ(DDACEAlgorithmType(DDACEAlgorithmType::grid),options.algorithmType());
  EXPECT_FALSE(options.samples());
  options.setSamplesForGrid(2,problem);
  ASSERT_TRUE(options.samples());
  EXPECT_EQ(8,options.samples().get());

  options.setSamplesForGrid(5,problem);
  ASSERT_TRUE(options.samples());
  EXPECT_EQ(125,options.samples().get());

  DDACEAlgorithmOptions optionsClone = options.clone().cast<DDACEAlgorithmOptions>();
  // all Attributes should be equivalent but have different UUIDs
  AttributeVector attributes = options.options();
  AttributeVector attributeClones = optionsClone.options();
  ASSERT_EQ(attributes.size(),attributeClones.size());
  for (unsigned i = 0, n = attributes.size(); i < n; ++i) {
    EXPECT_TRUE(attributes[i] == attributeClones[i]);
    EXPECT_FALSE(attributes[i].uuid() == attributeClones[i].uuid());
    EXPECT_FALSE(attributes[i].versionUUID() == attributeClones[i].versionUUID());
  }

  options = DDACEAlgorithmOptions(DDACEAlgorithmType::lhs);
  options.setSeed(891678);
  options.setSamples(62);
  ASSERT_TRUE(options.seed());
  EXPECT_EQ(891678,options.seed().get());
  ASSERT_TRUE(options.samples());
  EXPECT_EQ(62,options.samples().get());
  EXPECT_FALSE(options.symbols());
  options.setSeed(1);
  ASSERT_TRUE(options.seed());
  EXPECT_EQ(1,options.seed().get());
  options.clearSeed();
  EXPECT_FALSE(options.seed());

  options = DDACEAlgorithmOptions(DDACEAlgorithmType::oas);
  options.setSamplesAndSymbolsForOrthogonalArray(13,1);
  EXPECT_TRUE(options.seed()); // default is to generate a fixed seed
  ASSERT_TRUE(options.symbols());
  EXPECT_EQ(13,options.symbols().get());
  ASSERT_TRUE(options.samples());
  EXPECT_EQ(169,options.samples().get());

  optionsClone = options.clone().cast<DDACEAlgorithmOptions>();
  // all Attributes should be equivalent but have different UUIDs
  attributes = options.options();
  attributeClones = optionsClone.options();
  ASSERT_EQ(attributes.size(),attributeClones.size());
  for (unsigned i = 0, n = attributes.size(); i < n; ++i) {
    EXPECT_TRUE(attributes[i] == attributeClones[i]);
    EXPECT_FALSE(attributes[i].uuid() == attributeClones[i].uuid());
    EXPECT_FALSE(attributes[i].versionUUID() == attributeClones[i].versionUUID());
  }

  options.clearSymbols();
  EXPECT_FALSE(options.symbols());
  EXPECT_TRUE(options.samples());
  options.clearSamples();
  EXPECT_FALSE(options.samples());

  int n = DDACEAlgorithmOptions::samplesForCentralComposite(problem);
  EXPECT_EQ(15,n);
  n = DDACEAlgorithmOptions::samplesForBoxBehnken(problem);
  EXPECT_EQ(13,n);
}
openstudio::analysis::Analysis AnalysisFixture::analysis1(AnalysisState state) {
  // Create problem and analysis
  Problem problem("My Problem");

  BCLMeasure bclMeasure(resourcesPath() / toPath("utilities/BCL/Measures/SetWindowToWallRatioByFacade"));
  RubyMeasure measure(bclMeasure);

  // Measure Group
  StringVector choices;
  choices.push_back("North");
  choices.push_back("South");
  choices.push_back("East");
  choices.push_back("West");
  OSArgument facade = OSArgument::makeChoiceArgument("facade",choices);
  OSArgument arg = facade.clone();
  arg.setValue("South");
  measure.setArgument(arg);
  OSArgument wwr = OSArgument::makeDoubleArgument("wwr");
  MeasureVector measures(1u,NullMeasure());
  measures.push_back(measure.clone().cast<Measure>());
  arg = wwr.clone();
  arg.setValue(0.1);
  measures.back().cast<RubyMeasure>().setArgument(arg);
  measures.push_back(measure.clone().cast<Measure>());
  arg = wwr.clone();
  arg.setValue(0.2);
  measures.back().cast<RubyMeasure>().setArgument(arg);
  measures.push_back(measure.clone().cast<Measure>());
  arg = wwr.clone();
  arg.setValue(0.3);
  measures.back().cast<RubyMeasure>().setArgument(arg);
  problem.push(MeasureGroup("South Windows",measures));

  // Continuous Variables Attached to Arguments
  arg = facade.clone();
  arg.setValue("North");
  measure.setArgument(arg);
  arg = wwr.clone();
  RubyContinuousVariable wwrCV("Window to Wall Ratio",arg,measure);
  wwrCV.setMinimum(0.0);
  wwrCV.setMaximum(1.0);
  TriangularDistribution td(0.2,0.0,0.5);
  wwrCV.setUncertaintyDescription(td);
  problem.push(wwrCV);
  OSArgument offset = OSArgument::makeDoubleArgument("offset");
  RubyContinuousVariable offsetCV("Offset",offset,measure);
  offsetCV.setMinimum(0.0);
  offsetCV.setMaximum(1.5);
  NormalDistribution nd(0.9,0.05);
  offsetCV.setUncertaintyDescription(nd);
  problem.push(offsetCV);

  // Simulation
  problem.push(WorkItem(JobType::ModelToIdf));
  problem.push(WorkItem(JobType::EnergyPlusPreProcess));
  problem.push(WorkItem(JobType::EnergyPlus));
  problem.push(WorkItem(JobType::OpenStudioPostProcess));

  // Responses
  LinearFunction response1("Energy Use Intensity",
                           VariableVector(1u,OutputAttributeVariable("EUI","site.eui")));
  problem.pushResponse(response1);
  VariableVector vars;
  vars.push_back(OutputAttributeVariable("Heating Energy","heating.energy.gas"));
  vars.push_back(OutputAttributeVariable("Cooling Energy","cooling.energy.elec"));
  DoubleVector coeffs;
  coeffs.push_back(1.0); // approx. source factor
  coeffs.push_back(2.5); // approx. source factor
  LinearFunction response2("Approximate Source Energy",vars,coeffs);
  problem.pushResponse(response2);
  LinearFunction response3("North WWR",VariableVector(1u,wwrCV)); // input variable as output
  problem.pushResponse(response3);

  Analysis analysis("My Analysis",problem,FileReferenceType::OSM);

  if (state == PreRun) {
    // Add three DataPoints
    std::vector<QVariant> values;
    values.push_back(0);
    values.push_back(0.2);
    values.push_back(0.9);
    OptionalDataPoint dataPoint = problem.createDataPoint(values);
    analysis.addDataPoint(*dataPoint);
    values[0] = 1; values[1] = 0.21851789; values[2] = 1.1681938;
    dataPoint = problem.createDataPoint(values);
    analysis.addDataPoint(*dataPoint);
    values[0] = 2; values[1] = 0.0; values[2] = 0.581563892;
    dataPoint = problem.createDataPoint(values);
    analysis.addDataPoint(*dataPoint);
  }
  else {
    // state == PostRun
    // Add one complete DataPoint
    std::vector<QVariant> values;
    values.push_back(1);
    values.push_back(0.3);
    values.push_back(0.9);
    DoubleVector responseValues;
    responseValues.push_back(58.281967);
    responseValues.push_back(718952.281);
    responseValues.push_back(0.3);
    TagVector tags;
    tags.push_back(Tag("custom"));
    tags.push_back(Tag("faked"));
    // attributes
    AttributeVector attributes;
    attributes.push_back(Attribute("electricity.Cooling",281.281567,"kWh"));
    attributes.push_back(Attribute("electricity.Lighting",19206.291876,"kWh"));
    attributes.push_back(Attribute("electricity.Equipment",5112.125718,"kWh"));
    attributes = AttributeVector(1u,Attribute("enduses.electric",attributes));
    attributes.push_back(Attribute("eui",createQuantity(128.21689,"kBtu/ft^2").get()));
    // complete job
    // 1. get vector of work items
    std::vector<WorkItem> workItems;
    // 0
    workItems.push_back(problem.variables()[0].cast<MeasureGroup>().createWorkItem(QVariant(1),
                                                                                   toPath(rubyOpenStudioDir())));
    RubyContinuousVariable rcv = problem.variables()[1].cast<RubyContinuousVariable>();
    RubyMeasure rm = rcv.measure();
    OSArgument arg = rcv.argument().clone();
    arg.setValue(values[1].toDouble());
    rm.setArgument(arg);
    rcv = problem.variables()[2].cast<RubyContinuousVariable>();
    arg = rcv.argument().clone();
    arg.setValue(values[2].toDouble());
    rm.setArgument(arg);
    // 1
    workItems.push_back(rm.createWorkItem(toPath(rubyOpenStudioDir())));
    // 2
    workItems.push_back(WorkItem(JobType::ModelToIdf));
    // 3
    workItems.push_back(WorkItem(JobType::EnergyPlusPreProcess));
    // 4
    workItems.push_back(WorkItem(JobType::EnergyPlus));
    // 5
    workItems.push_back(WorkItem(JobType::OpenStudioPostProcess));
    // 2. step through work items and create jobs with results
    WorkItem wi = workItems[5];
    std::vector<FileInfo> inFiles = wi.files.files();
    inFiles.push_back(FileInfo("eplusout.sql",
                               DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,33,32)),
                               "",
                               toPath("myProject/fakeDataPoint/75-OpenStudioPostProcess-0/eplusout.sql")));                             
    Files inFilesObject(inFiles);
    std::vector<std::pair<ErrorType, std::string> > errors;
    errors.push_back(std::make_pair(ErrorType::Info,"Post-process completed successfully."));
    JobErrors errorsObject(OSResultValue::Success,errors);
    std::vector<FileInfo> outFiles;
    outFiles.push_back(FileInfo("report.xml",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,34,21)),
                                "",
                                toPath("myProject/fakeDataPoint/75-OpenStudioPostProcess-0/report.xml")));
    Files outFilesObject(outFiles);
    Job job = JobFactory::createJob(
          wi.type,
          wi.tools,
          wi.params,
          inFilesObject,
          std::vector<openstudio::URLSearchPath>(),
          false,
          createUUID(),
          JobState(
            DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,34,21)),
            errorsObject,
            outFilesObject,
            AdvancedStatus(),
            openstudio::path())

          ); // OpenStudioPostProcess

    Job jobLast = job;
    wi = workItems[4];
    inFiles = wi.files.files();
    inFiles.push_back(FileInfo("in.idf",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,23,05)),
                                "",
                                toPath("myProject/fakeDataPoint/74-EnergyPlus-0/in.idf")));
    inFilesObject = Files(inFiles);
    errors.clear();
    errors.push_back(std::make_pair(ErrorType::Warning,"ENERGYPLUS WARNING: ..."));
    errors.push_back(std::make_pair(ErrorType::Warning,"ENERGYPLUS WARNING: ..."));
    errors.push_back(std::make_pair(ErrorType::Warning,"ENERGYPLUS WARNING: ..."));
    errorsObject = JobErrors(OSResultValue::Success,errors);
    outFiles.clear();
    outFiles.push_back(FileInfo("eplusout.sql",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,33,32)),
                                "",
                                toPath("myProject/fakeDataPoint/74-EnergyPlus-0/eplusout.sql")));
    outFiles.push_back(FileInfo("eplusout.err",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,33,34)),
                                "",
                                toPath("myProject/fakeDataPoint/74-EnergyPlus-0/eplusout.err")));
    outFilesObject = Files(outFiles);
    job = JobFactory::createJob(
          wi.type,
          wi.tools,
          wi.params,
          inFilesObject,
          std::vector<openstudio::URLSearchPath>(),
          false,
          createUUID(),
          JobState(
            DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,33,42)),
            errorsObject,
            outFilesObject,
            AdvancedStatus(),
            openstudio::path())
          ); // EnergyPlus
    job.addChild(jobLast);

    jobLast = job;
    wi = workItems[3];
    inFiles = wi.files.files();
    inFiles.push_back(FileInfo("in.idf",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,22,30)),
                                "",
                                toPath("myProject/fakeDataPoint/73-EnergyPlusPreProcess-0/in.idf")));
    inFilesObject = Files(inFiles);
    errors.clear();
    errorsObject = JobErrors(OSResultValue::Success,errors);
    outFiles.clear();
    outFiles.push_back(FileInfo("out.idf",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,23,05)),
                                "",
                                toPath("myProject/fakeDataPoint/73-EnergyPlusPreProcess-0/out.idf")));
    outFilesObject = Files(outFiles);
    job = JobFactory::createJob(
          wi.type,
          wi.tools,
          wi.params,
          inFilesObject,
          std::vector<openstudio::URLSearchPath>(),
          false,
          createUUID(),
          JobState(
            DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,23,12)),
            errorsObject,
            outFilesObject,
            AdvancedStatus(),
            openstudio::path())
          ); // EnergyPlusPreProcess
    job.addChild(jobLast);

    jobLast = job;
    wi = workItems[2];
    inFiles = wi.files.files();
    inFiles.push_back(FileInfo("in.osm",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,22,01)),
                                "",
                                toPath("myProject/fakeDataPoint/72-ModelToIdf-0/in.osm")));
    inFilesObject = Files(inFiles);
    errors.clear();
    errors.push_back(std::make_pair(ErrorType::Info,"Did not find ScheduleTypeLimits for Schedule ..."));
    errors.push_back(std::make_pair(ErrorType::Warning,"Unexpectedly did not find a child object of a certain type, replaced with a default one."));
    errorsObject = JobErrors(OSResultValue::Success,errors);
    outFiles.clear();
    outFiles.push_back(FileInfo("out.idf",
                                DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,22,30)),
                                "",
                                toPath("myProject/fakeDataPoint/72-ModelToIdf-0/out.idf")));
    outFilesObject = Files(outFiles);
    job = JobFactory::createJob(
          wi.type,
          wi.tools,
          wi.params,
          inFilesObject,
          std::vector<openstudio::URLSearchPath>(),
          false,
          createUUID(),
          JobState(
            DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,22,32)),
            errorsObject,
            outFilesObject,
            AdvancedStatus(),
            openstudio::path())
          ); // ModelToIdf
    job.addChild(jobLast);

    jobLast = job;
    wi = workItems[1];
    errors.clear();
    errors.push_back(std::make_pair(ErrorType::InitialCondition,"Started with a window to wall ratio of ..."));
    errors.push_back(std::make_pair(ErrorType::FinalCondition,"Set the window to wall ratio ..."));
    errorsObject = JobErrors(OSResultValue::Success,errors);
    outFiles.clear();
    outFilesObject = Files(outFiles);
    wi.params.append("outdir","myProject/fakeDataPoint/");
    job = JobFactory::createJob(
          wi.type,
          wi.tools,
          wi.params,
          wi.files,
          std::vector<openstudio::URLSearchPath>(),
          false,
          createUUID(),
          JobState(
            DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,21,52)),
            errorsObject,
            outFilesObject,
            AdvancedStatus(),
            openstudio::path())
          ); // Variables 2 & 3
    job.addChild(jobLast);

    jobLast = job;
    wi = workItems[0];
    errors.clear();
    errors.push_back(std::make_pair(ErrorType::InitialCondition,"Started with a window to wall ratio of ..."));
    errors.push_back(std::make_pair(ErrorType::FinalCondition,"Set the window to wall ratio ..."));
    errorsObject = JobErrors(OSResultValue::Success,errors);
    outFiles.clear();
    outFilesObject = Files(outFiles);
    // add outdir job param
    JobParams params = wi.params;
    params.append("outdir","myProject/fakeDataPoint");
    job = JobFactory::createJob(
          wi.type,
          wi.tools,
          params,
          wi.files,
          std::vector<openstudio::URLSearchPath>(),
          false,
          createUUID(),
          JobState(
            DateTime(Date(MonthOfYear::Mar,21,2018),Time(0,8,21,10)),
            errorsObject,
            outFilesObject,
            AdvancedStatus(),
            openstudio::path())
          ); // Variable 1
    job.addChild(jobLast);

    DataPoint dataPoint(createUUID(),
                        createUUID(),
                        "fakeDataPoint",
                        "Fake Data Point",
                        "Demonstrating json serialization of complete DataPoint.",
                        problem,
                        true,
                        false,
                        true,
                        DataPointRunType::Local,
                        values,
                        responseValues,
                        toPath("myProject/fakeDataPoint/"),
                        FileReference(toPath("myProject/fakeDataPoint/71-Ruby-0/out.osm")),
                        FileReference(toPath("myProject/fakeDataPoint/72-ModelToIdf-0/out.idf")),
                        FileReference(toPath("myProject/fakeDataPoint/74-EnergyPlus-0/eplusout.sql")),
                        FileReferenceVector(1u,FileReference(toPath("myProject/fakeDataPoint/75-OpenStudioPostProcess-0/report.xml"))),
                        job,
                        std::vector<openstudio::path>(),
                        tags,
                        attributes);
    EXPECT_TRUE(analysis.addDataPoint(dataPoint));
  }

  return analysis;
}
TEST_F(ProjectFixture, ProblemRecord)
{
  //Logger::instance().logLevel(Debug);

  openstudio::path rubyLibDirPath = openstudio::toPath(rubyLibDir());
  openstudio::path perturbScript = rubyLibDirPath/openstudio::toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");

  // Workflow
  Workflow workflow;

  // Variables
  VariableVector variables;

  // Measures
  MeasureVector perturbations1;
  MeasureVector perturbations2;

  // Discrete Variable Records
  InputVariableRecordVector measureGroupRecords;

  {
    // Problem
    Problem problem("0 Variables",variables,workflow);

    // Project Database
    ProjectDatabase database = getCleanDatabase("ProblemRecord");

    // Problem Record
    ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problem,database);

    InputVariableRecordVector measureGroupRecords = problemRecord.inputVariableRecords();

    EXPECT_EQ(0u,measureGroupRecords.size());
  }

  {
    perturbations1.push_back(NullMeasure());

    variables.push_back(analysis::MeasureGroup("",perturbations1));

    // Problem
    Problem problem("perturbations1",variables,workflow);

    // Project Database
    ProjectDatabase database = getCleanDatabase("ProblemRecord");

    // Problem Record
    ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problem,database);

    measureGroupRecords = problemRecord.inputVariableRecords();

    EXPECT_EQ(1u,measureGroupRecords.size());

    // Discrete Variable Record
    MeasureGroupRecord measureGroupRecord = measureGroupRecords.at(0).cast<MeasureGroupRecord>();

    EXPECT_EQ(1u,measureGroupRecord.measureRecordIds(true).size());
    EXPECT_EQ(1u,measureGroupRecord.measureRecords(true).size());
  }

  {
    variables.push_back(MeasureGroup("Wall Construction",perturbations1));

    // Problem
    Problem problem("perturbations1",variables,workflow);

    // Project Database
    ProjectDatabase database = getCleanDatabase("ProblemRecord");

    // Problem Record
    ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problem,database);

    measureGroupRecords = problemRecord.inputVariableRecords();

    EXPECT_EQ(2u,measureGroupRecords.size());

    // Discrete Variable Record
    MeasureGroupRecord variable1 = measureGroupRecords.at(0).cast<MeasureGroupRecord>();

    EXPECT_EQ(static_cast<unsigned>(1), variable1.measureRecords(true).size());
    EXPECT_EQ(static_cast<unsigned>(1), variable1.measureRecords(false).size());
    EXPECT_EQ(static_cast<unsigned>(2), problemRecord.inputVariableRecords().size());
  }

  {
    perturbations2.push_back(NullMeasure());

    variables.push_back(MeasureGroup("Roof Construction",perturbations2));

    // Problem
    Problem problem("perturbations2",variables,workflow);

    // Project Database
    ProjectDatabase database = getCleanDatabase("ProblemRecord");

    // Problem Record
    ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problem,database);

    measureGroupRecords = problemRecord.inputVariableRecords();

    EXPECT_EQ(3u,measureGroupRecords.size());

    // Discrete Variable Record
    MeasureGroupRecord variable1 = measureGroupRecords.at(0).cast<MeasureGroupRecord>();
    MeasureGroupRecord variable2 = measureGroupRecords.at(0).cast<MeasureGroupRecord>();
    MeasureGroupRecord variable3 = measureGroupRecords.at(0).cast<MeasureGroupRecord>();

    EXPECT_EQ(static_cast<size_t>(1), variable1.measureRecords(true).size());
    EXPECT_EQ(static_cast<size_t>(1), variable1.measureRecords(false).size());
    EXPECT_EQ(static_cast<size_t>(3), problemRecord.inputVariableRecords().size());

    UUID uuid1;
    UUID versionUUID1;
    NullMeasure nullMeasure1(uuid1,versionUUID1,"","","",true);
    NullMeasureRecord nullMeasureRecord1(nullMeasure1,variable1,0);

    EXPECT_EQ(static_cast<unsigned>(2), variable1.measureRecords(true).size());
    EXPECT_EQ(static_cast<unsigned>(2), variable1.measureRecords(false).size());
    EXPECT_EQ(static_cast<unsigned>(2), variable2.measureRecords(true).size());
    EXPECT_EQ(static_cast<unsigned>(2), variable2.measureRecords(false).size());

    UUID uuid2;
    UUID versionUUID2;
    NullMeasure nullMeasure2(uuid2,versionUUID2,"","","",true);
    NullMeasureRecord nullMeasureRecord2(nullMeasure2,variable1,0);

    EXPECT_EQ(static_cast<unsigned>(2), variable1.measureRecords(true).size());
    EXPECT_EQ(static_cast<unsigned>(2), variable1.measureRecords(false).size());
    EXPECT_EQ(static_cast<unsigned>(2), variable2.measureRecords(true).size());
    EXPECT_EQ(static_cast<unsigned>(2), variable2.measureRecords(false).size());

    MeasureVector perturbations;
    MeasureGroup measureGroup("Wall Construction",perturbations);
    MeasureGroupRecord measureGroupRecord(measureGroup, problemRecord,0);

    openstudio::path rubyPath = resourcesPath() / openstudio::toPath("project/rubyscripts/*.rb");
    openstudio::path perturbScript = rubyPath/openstudio::toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
    RubyMeasure rubyMeasure(perturbScript,FileReferenceType::OSM,FileReferenceType::OSM);
    RubyMeasureRecord rubyMeasureRecord1(rubyMeasure,measureGroupRecord,0);

    EXPECT_EQ(static_cast<unsigned>(1), measureGroupRecord.numMeasures(true));
    EXPECT_EQ(static_cast<unsigned>(1), measureGroupRecord.measureRecordIds(true).size());
    EXPECT_EQ(static_cast<unsigned>(1), measureGroupRecord.measureRecords(true).size());

    RubyMeasureRecord rubyMeasureRecord(rubyMeasure,measureGroupRecord,0);

    EXPECT_EQ(static_cast<unsigned>(1), measureGroupRecord.numMeasures(true));
    EXPECT_EQ(static_cast<unsigned>(1), measureGroupRecord.measureRecordIds(true).size());
    EXPECT_EQ(rubyMeasureRecord.id(), measureGroupRecord.measureRecordIds(true)[0]);
    EXPECT_EQ(static_cast<unsigned>(1), measureGroupRecord.measureRecords(true).size());
    EXPECT_EQ(rubyMeasureRecord.handle(), measureGroupRecord.measureRecords(true)[0].handle());
  }
}
TEST_F(AnalysisFixture, Problem_Constructors) {
  VariableVector variables;
  MeasureVector measures;
  runmanager::Workflow workflow;

  // almost-default constructor
  Problem problem("Problem",variables,workflow);
  EXPECT_EQ(0,problem.numVariables());
  OptionalInt combinatorialSize = problem.combinatorialSize(true);
  ASSERT_TRUE(combinatorialSize);
  EXPECT_EQ(0,*combinatorialSize);

  // variables with consistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  openstudio::path rubyScriptPath = toPath(rubyLibDir()) /
                                    toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Variable 1",measures));
  measures.clear();
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 2",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 3",measures));
  problem = Problem("Problem",variables,workflow);
  EXPECT_EQ(3,problem.numVariables());
  EXPECT_EQ(6,problem.combinatorialSize(true).get());

  // variables with inconistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Variable 1",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 2",measures));
  EXPECT_THROW(Problem("Problem",variables,workflow),std::exception);

  // variables and non-null workflow with consistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 1",measures));
  workflow = runmanager::Workflow();
  workflow.addJob(openstudio::runmanager::JobType::EnergyPlus);
  problem = Problem("Problem",variables,workflow);
  problem.setDisplayName("Display Name");
  problem.setDescription("long winded description");
  EXPECT_EQ(1,problem.numVariables());
  EXPECT_EQ(2,problem.combinatorialSize(true).get());

  // deserialization
  Problem problemCopy(problem.uuid(),
                      problem.versionUUID(),
                      problem.name(),
                      problem.displayName(),
                      problem.description(),
                      problem.workflow(),
                      problem.responses());
  EXPECT_FALSE(problem == problemCopy); // different impls
  EXPECT_TRUE(problem.uuid() == problemCopy.uuid());
  EXPECT_TRUE(problem.versionUUID() == problemCopy.versionUUID());
  EXPECT_EQ(problem.name(),problemCopy.name());
  EXPECT_EQ(problem.displayName(),problemCopy.displayName());
  EXPECT_EQ(problem.description(),problemCopy.description());
  EXPECT_TRUE(problem.workflow() == problemCopy.workflow());

  // variables and non-null workflow with inconsistent file types
  workflow = runmanager::Workflow();
  workflow.addJob(openstudio::runmanager::JobType::ModelToIdf);
  workflow.addJob(openstudio::runmanager::JobType::EnergyPlus);
  EXPECT_THROW(Problem("Problem",variables,workflow),std::exception);
}
TEST_F(AnalysisFixture, Problem_FileTypesAreCompatible_ExistingMeasureGroup) {
  // expected behavior when discrete variable already contains measures
  // test with BCLMeasure first. verify with RubyMeasure.

  // create problem
  VariableVector variables;
  variables.push_back(
        MeasureGroup("Model Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("modelUserScript.rb"),
                                                                        FileReferenceType::OSM,
                                                                        FileReferenceType::OSM,
                                                                        true))));
  variables.back().cast<MeasureGroup>().insert(0,NullMeasure());
  // 2
  variables.push_back(
        MeasureGroup("Translation Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("translationUserScript.rb"),
                                                                        FileReferenceType::OSM,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  // 3
  variables.push_back(
        MeasureGroup("Workspace Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("workspaceUserScript.rb"),
                                                                        FileReferenceType::IDF,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  variables.back().cast<MeasureGroup>().insert(0,NullMeasure());
  runmanager::Workflow workflow;
  workflow.addJob(runmanager::JobType::EnergyPlus);
  Problem problem("Problem",variables,workflow);

  // create measure to be pushed onto a variable
  openstudio::path dir = resourcesPath() / toPath("/utilities/BCL/Measures/v2/SetWindowToWallRatioByFacade");
  BCLMeasure bclMeasure = BCLMeasure::load(dir).get();
  EXPECT_EQ(FileReferenceType(FileReferenceType::OSM),bclMeasure.inputFileType());
  EXPECT_EQ(FileReferenceType(FileReferenceType::OSM),bclMeasure.outputFileType());
  RubyMeasure measure(bclMeasure);

  ASSERT_EQ(3,problem.numVariables());

  // can be added to variable 0 (OSM)
  WorkflowStep step = problem.workflow()[0];
  InputVariable var = step.inputVariable();
  EXPECT_EQ(2u,var.cast<MeasureGroup>().numMeasures(false));
  EXPECT_TRUE(problem.fileTypesAreCompatible(step,
                                             measure.inputFileType(),
                                             measure.outputFileType()));
  EXPECT_TRUE(var.cast<MeasureGroup>().push(measure));
  EXPECT_EQ(3u,var.cast<MeasureGroup>().numMeasures(false));

  // cannot be added to variable 1 (translation)
  step = problem.workflow()[1];
  var = step.inputVariable();
  EXPECT_EQ(1u,var.cast<MeasureGroup>().numMeasures(false));
  EXPECT_FALSE(problem.fileTypesAreCompatible(step,
                                              measure.inputFileType(),
                                              measure.outputFileType()));
  EXPECT_FALSE(var.cast<MeasureGroup>().push(measure));
  EXPECT_EQ(1u,var.cast<MeasureGroup>().numMeasures(false));

  // cannot be added to variable 2 (IDF)
  step = problem.variables()[2];
  var = step.inputVariable();
  EXPECT_EQ(2u,var.cast<MeasureGroup>().numMeasures(false));
  EXPECT_FALSE(problem.fileTypesAreCompatible(step,
                                              measure.inputFileType(),
                                              measure.outputFileType()));
  EXPECT_FALSE(var.cast<MeasureGroup>().push(measure));
  EXPECT_EQ(2u,var.cast<MeasureGroup>().numMeasures(false));
}
TEST_F(AnalysisFixture, Problem_FileTypesAreCompatible_NewMeasureGroup) {
  // ok to insert null-only discrete variable anywhere in the chain
  // (between null-model, model-model, idf-idf, idf-workflow)
  VariableVector variables;
  // 0
  variables.push_back(
        MeasureGroup("Null Variable 1",
                     MeasureVector(1u,NullMeasure())));
  // 1
  variables.push_back(
        MeasureGroup("Model Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("modelUserScript.rb"),
                                                                        FileReferenceType::OSM,
                                                                        FileReferenceType::OSM,
                                                                        true))));
  // 2
  variables.push_back(
        MeasureGroup("Translation Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("translationUserScript.rb"),
                                                                        FileReferenceType::OSM,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  // 3
  variables.push_back(
        MeasureGroup("Workspace Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("workspaceUserScript.rb"),
                                                                        FileReferenceType::IDF,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  runmanager::Workflow workflow;
  workflow.addJob(runmanager::JobType::EnergyPlus);
  Problem problem("Problem 1",variables,workflow);
  MeasureGroup newVar("New Discrete Variable",
                          MeasureVector(1u,NullMeasure()));
  EXPECT_TRUE(problem.insert(4,newVar.clone().cast<InputVariable>()));
  ASSERT_EQ(5,problem.numVariables());
  EXPECT_EQ("New Discrete Variable",problem.variables()[4].name());
  EXPECT_TRUE(problem.insert(3,newVar.clone().cast<InputVariable>()));
  EXPECT_EQ(6,problem.numVariables());
  ASSERT_GE(problem.numVariables(),5);
  EXPECT_EQ("Translation Variable 1",problem.variables()[2].name());
  EXPECT_EQ("New Discrete Variable",problem.variables()[3].name());
  EXPECT_EQ("Workspace Variable 1",problem.variables()[4].name());
  EXPECT_TRUE(problem.insert(2,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(1,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(0,newVar.clone().cast<InputVariable>()));
  EXPECT_EQ(9,problem.numVariables());
  variables = castVector<Variable>(problem.variables());
  for (int i = 0, n = problem.numVariables(); i < n; ++i) {
    if (i % 2 == 0) {
      EXPECT_EQ("New Discrete Variable",variables[i].name());
    }
    else {
      EXPECT_NE("New Discrete Variable",variables[i].name());
    }
  }

  // (between null-idf, idf-idf, idf-workflow)
  variables.clear();
  // 0
  variables.push_back(
        MeasureGroup("Null Variable 1",
                         MeasureVector(1u,NullMeasure())));
  // 1
  variables.push_back(
        MeasureGroup("Workspace Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("workspaceUserScript.rb"),
                                                                        FileReferenceType::IDF,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  // 2
  variables.push_back(
        MeasureGroup("Workspace Variable 2",
                         MeasureVector(1u,RubyMeasure(toPath("workspaceUserScript.rb"),
                                                                        FileReferenceType::IDF,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  problem = Problem("Problem 2",variables,workflow);
  EXPECT_TRUE(problem.insert(3,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(2,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(1,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(0,newVar.clone().cast<InputVariable>()));
  EXPECT_EQ(7,problem.numVariables());
  variables = castVector<Variable>(problem.variables());
  for (int i = 0, n = problem.numVariables(); i < n; ++i) {
    if (i % 2 == 0) {
      EXPECT_EQ("New Discrete Variable",variables[i].name());
    }
    else {
      EXPECT_NE("New Discrete Variable",variables[i].name());
    }
  }

  // expected behavior for then adding first measure
  // test with BCLMeasure first. verify with RubyMeasure.
  openstudio::path dir = resourcesPath() / toPath("/utilities/BCL/Measures/v2/SetWindowToWallRatioByFacade");
  BCLMeasure bclMeasure = BCLMeasure::load(dir).get();
  EXPECT_EQ(FileReferenceType(FileReferenceType::OSM),bclMeasure.inputFileType());
  EXPECT_EQ(FileReferenceType(FileReferenceType::OSM),bclMeasure.outputFileType());
  RubyMeasure measure(bclMeasure);

  // have idf-only problem with two null variables at the top
  // try to add measures to first variable
  MeasureGroup firstVariable = problem.variables()[0].cast<MeasureGroup>();
  ASSERT_TRUE(firstVariable.parent());
  WorkflowStep step0 = problem.workflow()[0];
  EXPECT_TRUE(firstVariable.parent().get() == step0);
  EXPECT_EQ(1u,firstVariable.numMeasures(false));
  EXPECT_FALSE(problem.fileTypesAreCompatible(firstVariable,
                                              measure.inputFileType(),
                                              measure.outputFileType()));
  ASSERT_TRUE(firstVariable.parent());
  EXPECT_TRUE(firstVariable.parent().get() == problem.workflow()[0]);
  EXPECT_TRUE(problem.workflow()[0] == step0);
  EXPECT_FALSE(firstVariable.push(measure));
  EXPECT_EQ(1u,firstVariable.numMeasures(false));

  // make second variable a translation variable
  MeasureGroup secondVariable = problem.variables()[1].cast<MeasureGroup>();
  EXPECT_FALSE(secondVariable.push(RubyMeasure(toPath("myTranslationScript.rb"),
                                                    FileReferenceType::OSM,
                                                    FileReferenceType::IDF)));
  secondVariable.erase(secondVariable.measures(false)[0]);
  EXPECT_TRUE(secondVariable.push(RubyMeasure(toPath("myTranslationScript.rb"),
                                                   FileReferenceType::OSM,
                                                   FileReferenceType::IDF)));

  // now should be able to add OSM measure
  EXPECT_TRUE(problem.fileTypesAreCompatible(firstVariable,
                                             measure.inputFileType(),
                                             measure.outputFileType()));
  EXPECT_TRUE(firstVariable.push(measure));
  EXPECT_EQ(2u,firstVariable.numMeasures(false));
}
Example #9
0
void Stepper::unregisterProcess( Process* aProcess )
{ 
    ProcessVector::iterator ip( std::find( theProcessVector.begin(), 
                                    theProcessVector.end(),
                                    aProcess ) );
    
    if( ip == theProcessVector.end() )
    {
        THROW_EXCEPTION_INSIDE( NotFound,
                                asString() + ": Failed to dissociate [" +
                                aProcess->asString() + "] (no such Process is "
                                "associated to this stepper)" );
    }

    typedef std::set< Variable* > VariableSet;
    VariableSet aVarSet;
    Process::VariableReferenceVector const& aVarRefVector(
        aProcess->getVariableReferenceVector() );
    std::transform( aVarRefVector.begin(), aVarRefVector.end(),
                    inserter( aVarSet, aVarSet.begin() ),
                    std::mem_fun_ref( &VariableReference::getVariable ) );

    VariableVector aNewVector;
    VariableVector::size_type aReadWriteVariableOffset,
                             aReadOnlyVariableOffset;

    for( VariableVector::iterator i( theVariableVector.begin() ),
                                  e( theVariableVector.begin()
                                     + theReadWriteVariableOffset );
         i != e; ++i )
    {
        VariableSet::iterator j( aVarSet.find( *i ) );
        if ( j != aVarSet.end() )
            aVarSet.erase( j );
        else
            aNewVector.push_back( *i );
    }

    aReadWriteVariableOffset = aNewVector.size();

    for( VariableVector::iterator i( theVariableVector.begin()
                                     + theReadWriteVariableOffset ),
                                  e( theVariableVector.begin()
                                     + theReadOnlyVariableOffset );
         i != e; ++i )
    {
        VariableSet::iterator j( aVarSet.find( *i ) );
        if ( j != aVarSet.end() )
            aVarSet.erase( j );
        else
            aNewVector.push_back( *i );
    }

    aReadOnlyVariableOffset = aNewVector.size();

    for( VariableVector::iterator i( theVariableVector.begin()
                                     + theReadOnlyVariableOffset ),
                                  e( theVariableVector.end() );
         i != e; ++i )
    {
        VariableSet::iterator j( aVarSet.find( *i ) );
        if ( j != aVarSet.end() )
            aVarSet.erase( j );
        else
            aNewVector.push_back( *i );
    }

    theVariableVector.swap( aNewVector );
    theReadWriteVariableOffset = aReadWriteVariableOffset;
    theReadOnlyVariableOffset = aReadOnlyVariableOffset;

    theProcessVector.erase( ip );
}
TEST_F(AnalysisFixture, SequentialSearch) {
  // define dummy problem
  VariableVector variables;
  std::stringstream ss;
  for (int i = 0; i < 5; ++i) {
    MeasureVector measures;
    measures.push_back(NullMeasure());
    measures.push_back(RubyMeasure(toPath("in.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
    ss << "var " << i + 1;
    variables.push_back(MeasureGroup(ss.str(),measures));
    ss.str("");
  }
  FunctionVector functions;
  functions.push_back(LinearFunction("",VariableVector(1u,OutputAttributeContinuousVariable("f1","f1"))));
  functions.push_back(LinearFunction("",VariableVector(1u,OutputAttributeContinuousVariable("f2","f2"))));
  OptimizationProblem problem("By-Hand Problem",functions,variables,runmanager::Workflow());

  // solve dummy problem and check results
  SequentialSearch algorithm(SequentialSearchOptions(0));
  FileReference seed(toPath("in.osm"));
  Analysis analysis("By-Hand Analysis",problem,algorithm,seed);

  int numAdded = algorithm.createNextIteration(analysis);
  EXPECT_EQ(1,numAdded);
  OptimizationDataPointVector nextIteration =
      castVector<OptimizationDataPoint>(analysis.dataPointsToQueue());
  EXPECT_EQ(1u,nextIteration.size());
  while (!nextIteration.empty()) {
    int n = analysis.dataPoints().size();
    LOG(Debug,"Conducting iteration " << algorithm.iter() << " of Sequential Search.");
    OptimizationDataPointVector completeDataPoints =
        castVector<OptimizationDataPoint>(analysis.completeDataPoints());
    OptimizationDataPointVector currentDataPoints =
        castVector<OptimizationDataPoint>(analysis.getDataPoints("current"));
    EXPECT_EQ(1u,currentDataPoints.size());
    ASSERT_FALSE(currentDataPoints.empty());
    EXPECT_EQ(unsigned(algorithm.iter()),analysis.getDataPoints("explored").size());
    if (algorithm.iter() == 0) {
      EXPECT_EQ(1,n);
      EXPECT_EQ(0u,completeDataPoints.size());
    }
    else if (algorithm.iter() == 1) {
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(6,n);
      EXPECT_EQ(1u,completeDataPoints.size());
      EXPECT_TRUE(currentDataPoints[0] == completeDataPoints[0]);
      EXPECT_TRUE(completeDataPoints[0].isTag("curve0"));
      EXPECT_TRUE(completeDataPoints[0].isTag("current"));
      EXPECT_TRUE(completeDataPoints[0].isTag("explored"));
      EXPECT_EQ(5u,nextIteration.size());
    }
    else if (algorithm.iter() == 2) {
      EXPECT_DOUBLE_EQ(17.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(19.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(10,n);
      EXPECT_EQ(6u,completeDataPoints.size());
      EXPECT_EQ(2u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(4u,nextIteration.size());
    }
    else if (algorithm.iter() == 3) {
      EXPECT_DOUBLE_EQ(16.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(16.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(13,n);
      EXPECT_EQ(10u,completeDataPoints.size());
      EXPECT_EQ(3u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(3u,nextIteration.size());
    }
    else if (algorithm.iter() == 4) {
      // backtracking
      EXPECT_DOUBLE_EQ(15.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(19.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(16,n);
      EXPECT_EQ(13u,completeDataPoints.size());
      EXPECT_EQ(2u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(3u,nextIteration.size());
    }
    else if (algorithm.iter() == 5) {
      // backtracking
      EXPECT_DOUBLE_EQ(18.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(18,n);
      EXPECT_EQ(16u,completeDataPoints.size());
      EXPECT_EQ(2u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(2u,nextIteration.size());
    }
    else if (algorithm.iter() == 6) {
      EXPECT_DOUBLE_EQ(17.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(14.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(20,n);
      EXPECT_EQ(18u,completeDataPoints.size());
      EXPECT_EQ(5u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(2u,nextIteration.size());
    }
    else if (algorithm.iter() == 7) {
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(12.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(23,n);
      EXPECT_EQ(20u,completeDataPoints.size());
      EXPECT_EQ(6u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(3u,nextIteration.size());
    }
    EXPECT_EQ(static_cast<size_t>(n),completeDataPoints.size() + nextIteration.size());
    for (const OptimizationDataPoint& point : nextIteration) {
      std::vector<QVariant> values = point.variableValues();
      DoubleVector objectiveValues = getObjectiveValues(values);
      for (int i = 0; i < 5; ++i) {
        int value = values[i].toInt();
        if (value == 0) {
          ss << "  ";
        }
        else {
          EXPECT_EQ(1,value);
          ss << i + 1 << " ";
        }
      }
      ss << ": " << objectiveValues[0] << ", " << objectiveValues[1];
      LOG(Debug,ss.str()); ss.str("");

      completeDataPoints.push_back(OptimizationDataPoint(point.uuid(),
                                                         createUUID(),
                                                         "","","",
                                                         problem,
                                                         true,
                                                         false,
                                                         true,
                                                         DataPointRunType::Local,
                                                         values,
                                                         DoubleVector(),
                                                         objectiveValues,
                                                         openstudio::path(),
                                                         boost::none,
                                                         boost::none,
                                                         boost::none,
                                                         boost::none,
                                                         std::vector<openstudio::path>(),
                                                         point.tags(),
                                                         point.outputAttributes())); // DLM: Elaine is this ok?
    }
    EXPECT_EQ(static_cast<size_t>(n),completeDataPoints.size());
    analysis = Analysis(analysis.uuid(),
                        createUUID(),
                        analysis.name(),
                        analysis.displayName(),
                        analysis.description(),
                        problem,
                        algorithm,
                        seed,
                        boost::none,
                        castVector<DataPoint>(completeDataPoints),
                        false,
                        false);
    algorithm.createNextIteration(analysis);
    nextIteration = castVector<OptimizationDataPoint>(analysis.dataPointsToQueue());
  }

  EXPECT_EQ(23u,analysis.successfulDataPoints().size());
  OptimizationDataPointVector minimumCurve = algorithm.getMinimumCurve(0,analysis);
  ASSERT_EQ(6u,minimumCurve.size());
  DoubleVector values = minimumCurve[0].objectiveValues();
  EXPECT_DOUBLE_EQ(20.0,values[0]); EXPECT_DOUBLE_EQ(20.0,values[1]);
  values = minimumCurve[1].objectiveValues();
  EXPECT_DOUBLE_EQ(18.0,values[0]); EXPECT_DOUBLE_EQ(20.0,values[1]);
  values = minimumCurve[2].objectiveValues();
  EXPECT_DOUBLE_EQ(15.0,values[0]); EXPECT_DOUBLE_EQ(19.0,values[1]);
  values = minimumCurve[3].objectiveValues();
  EXPECT_DOUBLE_EQ(16.0,values[0]); EXPECT_DOUBLE_EQ(16.0,values[1]);
  values = minimumCurve[4].objectiveValues();
  EXPECT_DOUBLE_EQ(17.0,values[0]); EXPECT_DOUBLE_EQ(14.0,values[1]);
  values = minimumCurve[5].objectiveValues();
  EXPECT_DOUBLE_EQ(20.0,values[0]); EXPECT_DOUBLE_EQ(12.0,values[1]);
  OptimizationDataPointVector paretoFront = algorithm.getParetoFront(analysis);
  for (const OptimizationDataPoint& paretoPoint : paretoFront) {
    EXPECT_FALSE(std::find(minimumCurve.begin(),minimumCurve.end(),paretoPoint) == minimumCurve.end());
  }
  ASSERT_EQ(4u,paretoFront.size());
  // expect same order as curve0, although different method for calculating
  values = paretoFront[0].objectiveValues();
  EXPECT_DOUBLE_EQ(15.0,values[0]); EXPECT_DOUBLE_EQ(19.0,values[1]); // minimizes f0
  values = paretoFront[1].objectiveValues();
  EXPECT_DOUBLE_EQ(16.0,values[0]); EXPECT_DOUBLE_EQ(16.0,values[1]);
  values = paretoFront[2].objectiveValues();
  EXPECT_DOUBLE_EQ(17.0,values[0]); EXPECT_DOUBLE_EQ(14.0,values[1]);
  values = paretoFront[3].objectiveValues();
  EXPECT_DOUBLE_EQ(20.0,values[0]); EXPECT_DOUBLE_EQ(12.0,values[1]); // minimizes f1
}