Repository: NREL/OpenStudio (language: python)

Issue:
Add native support for measure argument methods in OsLib_HelperMethods 
Enhancement Request
--------------
There are several methods in `OsLib_HelperMethods` in the extension gem that should be native to the `Measure` or `Measure::OSArgument` objects in OpenStudio.

## Detailed Description
These are the specific methods that **_could_** be implemented in OpenStudio:
```
- check_upstream_measure_for_arg(runner, arg_name)
- checkChoiceArgFromModelObjects(object, variableName, to_ObjectType, runner, user_arguments)
- checkDoubleAndIntegerArguments(runner, user_arguments, arg_check_hash)
- checkOptionalChoiceArgFromModelObjects(object, variableName, to_ObjectType, runner, user_arguments)
- createRunVariables(runner, model, user_arguments, arguments)
- populateChoiceArgFromModelObjects(model, modelObject_args_hash, includeBuilding)
```

## Possible Implementation
To be clear, I DO NOT think these methods should be adopted as is. Some are already partially or mostly addressed (checkDoubleAndIntegerArguments with #4551). @DavidGoldwasser - can you analyze which methods should live in OpenStudio, and which are best left to code in the measure, then comment below? @eringold may have thoughts as well.



Discussion / hints:
Another method:
```
- getAbsoluteAzimuthForSurface(surface, model)
```
(/openstudio-extension-gem/lib/openstudio/extension/core/os_lib_geometry.rb)

Fix patch:
diff --git a/src/measure/OSRunner.cpp b/src/measure/OSRunner.cpp
index a61cae15fa7..271eb92ffdd 100644
--- a/src/measure/OSRunner.cpp
+++ b/src/measure/OSRunner.cpp
@@ -784,6 +784,141 @@ namespace measure {
     return result;
   }
 
+  Json::Value OSRunner::getArgumentValues(std::vector<OSArgument>& script_arguments, const std::map<std::string, OSArgument>& user_arguments) {
+    Json::Value argument_values;
+    OSArgumentType type;
+    for (const OSArgument& script_argument : script_arguments) {
+      if (script_argument.required()) {
+        switch (script_argument.type().value()) {
+          case OSArgumentType::Boolean:
+            argument_values[script_argument.name()] = getBoolArgumentValue(script_argument.name(), user_arguments);
+            break;
+          case OSArgumentType::Double:
+            argument_values[script_argument.name()] = getDoubleArgumentValue(script_argument.name(), user_arguments);
+            break;
+          case OSArgumentType::Integer:
+            argument_values[script_argument.name()] = getIntegerArgumentValue(script_argument.name(), user_arguments);
+            break;
+          case OSArgumentType::String:
+            argument_values[script_argument.name()] = getStringArgumentValue(script_argument.name(), user_arguments);
+            break;
+          case OSArgumentType::Choice:
+            argument_values[script_argument.name()] = getStringArgumentValue(script_argument.name(), user_arguments);
+            break;
+          case OSArgumentType::Path:
+            argument_values[script_argument.name()] = getStringArgumentValue(script_argument.name(), user_arguments);
+            break;
+          default:
+            OS_ASSERT(false);
+        }
+      } else {
+        switch (script_argument.type().value()) {
+          case OSArgumentType::Boolean:
+            if (boost::optional<bool> optB_ = getOptionalBoolArgumentValue(script_argument.name(), user_arguments)) {
+              argument_values[script_argument.name()] = optB_.get();
+            } else {
+              argument_values[script_argument.name()] = Json::nullValue;
+            }
+            break;
+          case OSArgumentType::Double:
+            if (boost::optional<double> optD_ = getOptionalDoubleArgumentValue(script_argument.name(), user_arguments)) {
+              argument_values[script_argument.name()] = optD_.get();
+            } else {
+              argument_values[script_argument.name()] = Json::nullValue;
+            }
+            break;
+          case OSArgumentType::Integer:
+            if (boost::optional<int> optI_ = getOptionalIntegerArgumentValue(script_argument.name(), user_arguments)) {
+              argument_values[script_argument.name()] = optI_.get();
+            } else {
+              argument_values[script_argument.name()] = Json::nullValue;
+            }
+            break;
+          case OSArgumentType::String:
+            if (boost::optional<std::string> optS_ = getOptionalStringArgumentValue(script_argument.name(), user_arguments)) {
+              argument_values[script_argument.name()] = optS_.get();
+            } else {
+              argument_values[script_argument.name()] = Json::nullValue;
+            }
+            break;
+          case OSArgumentType::Choice:
+            if (boost::optional<std::string> optS_ = getOptionalStringArgumentValue(script_argument.name(), user_arguments)) {
+              argument_values[script_argument.name()] = optS_.get();
+            } else {
+              argument_values[script_argument.name()] = Json::nullValue;
+            }
+            break;
+          case OSArgumentType::Path:
+            if (boost::optional<std::string> optS_ = getOptionalStringArgumentValue(script_argument.name(), user_arguments)) {
+              argument_values[script_argument.name()] = optS_.get();
+            } else {
+              argument_values[script_argument.name()] = Json::nullValue;
+            }
+            break;
+          default:
+            OS_ASSERT(false);
+        }
+      }
+    }
+    return argument_values;
+  }
+
+  Json::Value OSRunner::getPastStepValuesForMeasure(const std::string& measure_name) {
+    Json::Value step_values;
+    WorkflowJSON workflow_ = workflow();
+    std::vector<WorkflowStep> workflow_steps = workflow_.workflowSteps();
+    for (const WorkflowStep& workflow_step : workflow_steps) {
+      if (boost::optional<WorkflowStepResult> workflow_step_result_ = workflow_step.result()) {
+        boost::optional<std::string> measure_name_ = workflow_step_result_.get().measureName();
+        std::vector<WorkflowStepValue> workflow_step_values = workflow_step_result_.get().stepValues();
+        if (workflow_step_result_.get().value() == StepResult::Success) {
+          if (istringEqual(measure_name, measure_name_.get())) {
+            for (const WorkflowStepValue& workflow_step_value : workflow_step_values) {
+              if (workflow_step_value.variantType() == VariantType::String) {
+                step_values[workflow_step_value.name()] = workflow_step_value.valueAsString();
+              } else if (workflow_step_value.variantType() == VariantType::Double) {
+                step_values[workflow_step_value.name()] = workflow_step_value.valueAsDouble();
+              } else if (workflow_step_value.variantType() == VariantType::Integer) {
+                step_values[workflow_step_value.name()] = workflow_step_value.valueAsInteger();
+              } else if (workflow_step_value.variantType() == VariantType::Boolean) {
+                step_values[workflow_step_value.name()] = workflow_step_value.valueAsBoolean();
+              }
+            }
+          }
+        }
+      }
+    }
+    return step_values;
+  }
+
+  Json::Value OSRunner::getPastStepValuesForName(const std::string& step_name) {
+    Json::Value step_values;
+    WorkflowJSON workflow_ = workflow();
+    std::vector<WorkflowStep> workflow_steps = workflow_.workflowSteps();
+    for (const WorkflowStep& workflow_step : workflow_steps) {
+      if (boost::optional<WorkflowStepResult> workflow_step_result_ = workflow_step.result()) {
+        boost::optional<std::string> measure_name_ = workflow_step_result_.get().measureName();
+        std::vector<WorkflowStepValue> workflow_step_values = workflow_step_result_.get().stepValues();
+        if (workflow_step_result_.get().value() == StepResult::Success) {
+          for (const WorkflowStepValue& workflow_step_value : workflow_step_values) {
+            if (istringEqual(step_name, workflow_step_value.name())) {
+              if (workflow_step_value.variantType() == VariantType::String) {
+                step_values[measure_name_.get()] = workflow_step_value.valueAsString();
+              } else if (workflow_step_value.variantType() == VariantType::Double) {
+                step_values[measure_name_.get()] = workflow_step_value.valueAsDouble();
+              } else if (workflow_step_value.variantType() == VariantType::Integer) {
+                step_values[measure_name_.get()] = workflow_step_value.valueAsInteger();
+              } else if (workflow_step_value.variantType() == VariantType::Boolean) {
+                step_values[measure_name_.get()] = workflow_step_value.valueAsBoolean();
+              }
+            }
+          }
+        }
+      }
+    }
+    return step_values;
+  }
+
   void OSRunner::setLastOpenStudioModel(const openstudio::model::Model& lastOpenStudioModel) {
     m_lastOpenStudioModel = lastOpenStudioModel;
     m_lastOpenStudioModelPath.reset();
diff --git a/src/measure/OSRunner.hpp b/src/measure/OSRunner.hpp
index 5113383dc48..81c7e5415af 100644
--- a/src/measure/OSRunner.hpp
+++ b/src/measure/OSRunner.hpp
@@ -231,6 +231,17 @@ namespace measure {
                                                                                        const std::map<std::string, OSArgument>& user_arguments,
                                                                                        const openstudio::Workspace& workspace);
 
+    /** Call this method to retrieve the values of OSArguments of all types. */
+    Json::Value getArgumentValues(std::vector<OSArgument>& script_arguments, const std::map<std::string, OSArgument>& user_arguments);
+
+    //
+    Json::Value getPastStepValuesForMeasure(const std::string& measure_name);
+    // {arg1 => val1, arg2 => val2, ...}
+
+    //
+    Json::Value getPastStepValuesForName(const std::string& step_name);
+    // {measure1 => val1, measure2 => val2, ...}
+
     //@}
 
     // reset the runner to re-run the workflow


Test patch:
diff --git a/src/measure/test/OSRunner_GTest.cpp b/src/measure/test/OSRunner_GTest.cpp
index 5ea4b8d7de9..c81e5f59602 100644
--- a/src/measure/test/OSRunner_GTest.cpp
+++ b/src/measure/test/OSRunner_GTest.cpp
@@ -102,3 +102,198 @@ TEST_F(MeasureFixture, OSRunner_getOptionalBoolArgumentValue) {
     }
   }
 }
+
+TEST_F(MeasureFixture, OSRunner_getArgumentValues) {
+  WorkflowJSON workflow;
+  OSRunner runner(workflow);
+
+  std::vector<OSArgument> argumentVector;
+
+  OSArgument requiredBoolArgument = OSArgument::makeBoolArgument("required_bool", true);
+  EXPECT_TRUE(requiredBoolArgument.setValue(true));
+  argumentVector.push_back(requiredBoolArgument);
+
+  OSArgument requiredBoolArgument2 = OSArgument::makeBoolArgument("required_bool2", true);
+  EXPECT_TRUE(requiredBoolArgument2.setValue(false));
+  argumentVector.push_back(requiredBoolArgument2);
+
+  OSArgument optionalBoolArgument = OSArgument::makeBoolArgument("optional_bool", false);
+  argumentVector.push_back(optionalBoolArgument);
+
+  OSArgument requiredDoubleArgument = OSArgument::makeDoubleArgument("required_double", true);
+  EXPECT_TRUE(requiredDoubleArgument.setValue(1.0));
+  argumentVector.push_back(requiredDoubleArgument);
+
+  OSArgument requiredDoubleArgument2 = OSArgument::makeDoubleArgument("required_double2", true);
+  EXPECT_TRUE(requiredDoubleArgument2.setValue(234892384234.39485923845834534));
+  argumentVector.push_back(requiredDoubleArgument2);
+
+  OSArgument optionalDoubleArgument = OSArgument::makeDoubleArgument("optional_double", false);
+  argumentVector.push_back(optionalDoubleArgument);
+
+  OSArgument optionalDoubleArgument2 = OSArgument::makeDoubleArgument("optional_double2", false);
+  EXPECT_TRUE(optionalDoubleArgument2.setValue(10.5));
+  argumentVector.push_back(optionalDoubleArgument2);
+
+  OSArgument requiredIntegerArgument = OSArgument::makeIntegerArgument("required_integer", true);
+  EXPECT_TRUE(requiredIntegerArgument.setValue(2));
+  argumentVector.push_back(requiredIntegerArgument);
+
+  OSArgument optionalIntegerArgument = OSArgument::makeDoubleArgument("optional_integer", false);
+  argumentVector.push_back(optionalIntegerArgument);
+
+  OSArgument requiredStringArgument = OSArgument::makeStringArgument("required_string", true);
+  requiredStringArgument.setValue("Value");
+  argumentVector.push_back(requiredStringArgument);
+
+  OSArgument optionalStringArgument = OSArgument::makeStringArgument("optional_string", false);
+  argumentVector.push_back(optionalStringArgument);
+
+  std::vector<std::string> choices;
+  choices.push_back("On");
+  choices.push_back("Off");
+
+  OSArgument requiredChoiceArgument = OSArgument::makeChoiceArgument("required_choice", choices, true);
+  requiredChoiceArgument.setValue("Off");
+  argumentVector.push_back(requiredChoiceArgument);
+
+  OSArgument optionalChoiceArgument = OSArgument::makeChoiceArgument("optional_choice", choices, false);
+  argumentVector.push_back(optionalChoiceArgument);
+
+  std::map<std::string, OSArgument> argumentMap = convertOSArgumentVectorToMap(argumentVector);
+
+  bool b = runner.getBoolArgumentValue(requiredBoolArgument.name(), argumentMap);
+  EXPECT_TRUE(b);
+
+  bool b2 = runner.getBoolArgumentValue(requiredBoolArgument2.name(), argumentMap);
+  EXPECT_FALSE(b2);
+
+  double d = runner.getDoubleArgumentValue(requiredDoubleArgument.name(), argumentMap);
+  EXPECT_EQ(1.0, d);
+
+  double d2 = runner.getDoubleArgumentValue(requiredDoubleArgument2.name(), argumentMap);
+  EXPECT_EQ(234892384234.39485923845834534, d2);
+
+  int i = runner.getIntegerArgumentValue(requiredIntegerArgument.name(), argumentMap);
+  EXPECT_EQ(2, i);
+
+  std::string s = runner.getStringArgumentValue(requiredStringArgument.name(), argumentMap);
+  EXPECT_EQ("Value", s);
+
+  std::string c = runner.getStringArgumentValue(requiredChoiceArgument.name(), argumentMap);
+  EXPECT_EQ("Off", c);
+
+  Json::Value argumentValues = runner.getArgumentValues(argumentVector, argumentMap);
+
+  EXPECT_FALSE(argumentValues["required_bool"].isNull());
+  EXPECT_TRUE(argumentValues["required_bool"].asBool());
+  EXPECT_FALSE(argumentValues["required_bool2"].isNull());
+  EXPECT_FALSE(argumentValues["required_bool2"].asBool());
+  EXPECT_TRUE(argumentValues["optional_bool"].isNull());
+
+  EXPECT_EQ(1.0, argumentValues["required_double"].asDouble());
+  EXPECT_EQ(234892384234.39485923845834534, argumentValues["required_double2"].asDouble());
+  EXPECT_TRUE(argumentValues["optional_double"].isNull());
+  EXPECT_FALSE(argumentValues["optional_double2"].isNull());
+  EXPECT_EQ(10.5, argumentValues["optional_double2"].asDouble());
+
+  EXPECT_EQ(2, argumentValues["required_integer"].asInt());
+  EXPECT_TRUE(argumentValues["optional_integer"].isNull());
+
+  EXPECT_EQ("Value", argumentValues["required_string"].asString());
+  EXPECT_TRUE(argumentValues["optional_string"].isNull());
+
+  EXPECT_EQ("Off", argumentValues["required_choice"].asString());
+  EXPECT_TRUE(argumentValues["optional_choice"].isNull());
+}
+
+TEST_F(MeasureFixture, OSRunner_getPastStepValues) {
+  WorkflowJSON workflow;
+  OSRunner runner(workflow);
+
+  std::vector<WorkflowStep> workflow_steps;
+
+  MeasureStep step1("Step1");
+  step1.setArgument("Argument1", 100);
+  step1.setArgument("Argument2", 200);
+  workflow_steps.push_back(step1);
+  WorkflowStepResult workflow_step_result1;
+  EXPECT_FALSE(workflow_step_result1.measureName());
+  workflow_step_result1.setMeasureName("MeasureName1");
+  ASSERT_TRUE(workflow_step_result1.measureName());
+  EXPECT_EQ("MeasureName1", workflow_step_result1.measureName().get());
+  WorkflowStepValue stepValue1("StepValue1", true);
+  workflow_step_result1.addStepValue(stepValue1);
+  WorkflowStepValue stepValue2("StepValue2", 5);
+  workflow_step_result1.addStepValue(stepValue2);
+
+  MeasureStep step2("Step2");
+  step2.setArgument("Argument3", 300);
+  step2.setArgument("Argument4", 400);
+  workflow_steps.push_back(step2);
+  WorkflowStepResult workflow_step_result2;
+  EXPECT_FALSE(workflow_step_result2.measureName());
+  workflow_step_result2.setMeasureName("MeasureName2");
+  ASSERT_TRUE(workflow_step_result2.measureName());
+  EXPECT_EQ("MeasureName2", workflow_step_result2.measureName().get());
+  WorkflowStepValue stepValue3("StepValue3", 342.3);
+  workflow_step_result2.addStepValue(stepValue3);
+  EXPECT_EQ(342.3, stepValue3.valueAsDouble());
+  WorkflowStepValue stepValue4("StepValue4", false);
+  workflow_step_result2.addStepValue(stepValue4);
+  EXPECT_FALSE(stepValue4.valueAsBoolean());
+  EXPECT_EQ(2, workflow_step_result2.stepValues().size());
+  EXPECT_TRUE(workflow.setWorkflowSteps(workflow_steps));
+  EXPECT_EQ(2, workflow.workflowSteps().size());
+  EXPECT_EQ(workflow.string(), runner.workflow().string());
+  workflow_step_result2.setStepResult(StepResult::Success);
+  step2.setResult(workflow_step_result2);
+  ASSERT_TRUE(step2.result());
+  WorkflowStepResult workflow_step_result2_ = step2.result().get();
+  ASSERT_TRUE(workflow_step_result2_.stepResult());
+  EXPECT_EQ(workflow_step_result2.string(), workflow_step_result2_.string());
+  ASSERT_TRUE(workflow_step_result2.stepResult());
+  EXPECT_EQ(StepResult::Success, workflow_step_result2.stepResult().get());
+
+  Json::Value stepValues;
+
+  stepValues = runner.getPastStepValuesForMeasure("MeasureName1");
+  EXPECT_EQ(0, stepValues.size());  // did not set step result
+
+  stepValues = runner.getPastStepValuesForMeasure("MeasureName2");
+  EXPECT_EQ(2, stepValues.size());
+
+  EXPECT_TRUE(stepValues["Argument3"].isNull());
+  EXPECT_TRUE(stepValues["Argument4"].isNull());
+  EXPECT_TRUE(stepValues["StepValue1"].isNull());
+  EXPECT_TRUE(stepValues["StepValue2"].isNull());
+  EXPECT_FALSE(stepValues["StepValue3"].isNull());
+  EXPECT_EQ(342.3, stepValues["StepValue3"].asDouble());
+  EXPECT_FALSE(stepValues["StepValue4"].isNull());
+  EXPECT_FALSE(stepValues["StepValue4"].asBool());
+
+  MeasureStep step3("Step3");
+  workflow_steps.push_back(step3);
+  WorkflowStepResult workflow_step_result3;
+  workflow_step_result3.setMeasureName("MeasureName3");
+  workflow_step_result3.setStepResult(StepResult::Success);
+  step3.setResult(workflow_step_result3);
+  WorkflowStepValue stepValue5("StepValue3", 20);
+  workflow_step_result3.addStepValue(stepValue5);
+  EXPECT_TRUE(workflow.setWorkflowSteps(workflow_steps));
+  EXPECT_EQ(3, workflow.workflowSteps().size());
+
+  stepValues = runner.getPastStepValuesForName("StepValue1");
+  EXPECT_EQ(0, stepValues.size());  // did not set step result
+
+  stepValues = runner.getPastStepValuesForName("StepValue3");
+  EXPECT_EQ(2, stepValues.size());
+
+  EXPECT_TRUE(stepValues["Step1"].isNull());
+  EXPECT_TRUE(stepValues["Step2"].isNull());
+  EXPECT_TRUE(stepValues["MeasureName1"].isNull());
+  EXPECT_FALSE(stepValues["MeasureName2"].isNull());
+  EXPECT_FALSE(stepValues["MeasureName3"].isNull());
+  EXPECT_EQ(342.3, stepValues["MeasureName2"].asDouble());
+  EXPECT_EQ(20, stepValues["MeasureName3"].asInt());
+}