CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-openstudio

Cross-platform collection of software tools to support whole building energy modeling using EnergyPlus and advanced daylight analysis using Radiance

Pending
Overview
Eval results
Files

measure.mddocs/

Measure Framework

Parametric analysis framework for automated model modifications and simulation workflows. The measure framework enables systematic exploration of design alternatives through scripted model modifications, making it ideal for sensitivity analysis, optimization, and automated design studies.

Capabilities

Base Measure Classes

Core measure framework providing the foundation for all measure types.

/**
 * Base class for all OpenStudio measures
 * Defines the interface for parametric model modifications
 */
class OSMeasure {
public:
  // Virtual destructor
  virtual ~OSMeasure() = default;
  
  // Core measure interface
  virtual std::string name() const = 0;
  virtual std::string description() const = 0;
  virtual std::string modeler_description() const = 0;
  
  // Argument definition - defines measure parameters
  virtual OSArgumentVector arguments() = 0;
  
  // Main execution method - implemented by derived classes
  virtual bool run(OSRunner& runner, const OSArgumentMap& user_arguments) = 0;
  
  // Measure metadata
  virtual std::vector<std::string> tags() const;
  virtual std::vector<std::string> outcomes() const;
  
  // File and resource handling
  virtual std::vector<FileReferenceType> fileReferenceTypes() const;
  virtual std::vector<OSAttribute> outputAttributes() const;
};

/**
 * Model-based measure - operates on OpenStudio models
 */
class ModelMeasure : public OSMeasure {
public:
  // Constructor
  ModelMeasure() = default;
  
  // Model-specific run method
  virtual bool run(Model& model, 
                   OSRunner& runner, 
                   const OSArgumentMap& user_arguments) = 0;
  
  // Final run implementation calls model-specific version
  virtual bool run(OSRunner& runner, const OSArgumentMap& user_arguments) override final;
};

/**
 * EnergyPlus-based measure - operates on IDF workspaces
 */
class EnergyPlusMeasure : public OSMeasure {
public:
  // Constructor
  EnergyPlusMeasure() = default;
  
  // Workspace-specific run method
  virtual bool run(Workspace& workspace,
                   OSRunner& runner,
                   const OSArgumentMap& user_arguments) = 0;
  
  // Final run implementation calls workspace-specific version
  virtual bool run(OSRunner& runner, const OSArgumentMap& user_arguments) override final;
};

/**
 * Reporting measure - processes simulation results
 */
class ReportingMeasure : public OSMeasure {
public:
  // Constructor
  ReportingMeasure() = default;
  
  // Reporting-specific run method
  virtual bool run(OSRunner& runner,
                   const OSArgumentMap& user_arguments) = 0;
};

Measure Arguments

Type-safe parameter system for measure configuration and user input.

/**
 * Measure argument definition and validation
 */
class OSArgument {
public:
  // Argument types
  enum class Type {
    Boolean,
    Double,
    Integer, 
    String,
    Choice,
    Path
  };
  
  // Static factory methods for different argument types
  static OSArgument makeBoolArgument(const std::string& name,
                                     bool required = true);
  
  static OSArgument makeDoubleArgument(const std::string& name,
                                       bool required = true);
  
  static OSArgument makeIntegerArgument(const std::string& name,
                                        bool required = true);
  
  static OSArgument makeStringArgument(const std::string& name,
                                       bool required = true);
  
  static OSArgument makeChoiceArgument(const std::string& name,
                                       const std::vector<std::string>& choices,
                                       bool required = true);
  
  static OSArgument makePathArgument(const std::string& name,
                                     bool isRead = true,
                                     const std::string& extension = std::string(),
                                     bool required = true);
  
  // Argument properties
  std::string name() const;
  Type type() const;
  bool required() const;
  
  // Display properties
  std::string displayName() const;
  void setDisplayName(const std::string& displayName);
  
  std::string description() const;
  void setDescription(const std::string& description);
  
  std::string units() const;
  void setUnits(const std::string& units);
  
  // Default values
  bool hasDefaultValue() const;
  boost::optional<bool> defaultValueAsBool() const;
  boost::optional<double> defaultValueAsDouble() const;
  boost::optional<int> defaultValueAsInteger() const;
  boost::optional<std::string> defaultValueAsString() const;
  
  void setDefaultValue(bool value);
  void setDefaultValue(double value);
  void setDefaultValue(int value);
  void setDefaultValue(const std::string& value);
  
  // Value constraints
  bool hasDomainType() const;
  void setMinValue(double minValue);
  void setMaxValue(double maxValue);
  boost::optional<double> minValue() const;
  boost::optional<double> maxValue() const;
  
  // Choice-specific methods
  std::vector<std::string> choiceValues() const;
  std::vector<std::string> choiceDisplayNames() const;
  void setChoiceDisplayName(const std::string& choice, const std::string& displayName);
};

/**
 * Collection of arguments with type-safe value access
 */
using OSArgumentVector = std::vector<OSArgument>;

class OSArgumentMap {
public:
  // Constructor
  OSArgumentMap();
  OSArgumentMap(const OSArgumentVector& arguments);
  
  // Argument management
  void insert(const std::string& name, const OSArgument& argument);
  bool hasArgument(const std::string& name) const;
  OSArgument getArgument(const std::string& name) const;
  
  // Value access with type safety
  boost::optional<bool> getArgumentValue(const std::string& name) const;
  bool getArgumentValueAsBool(const std::string& name, bool defaultValue = false) const;
  double getArgumentValueAsDouble(const std::string& name, double defaultValue = 0.0) const;
  int getArgumentValueAsInteger(const std::string& name, int defaultValue = 0) const;
  std::string getArgumentValueAsString(const std::string& name, 
                                       const std::string& defaultValue = std::string()) const;
  Path getArgumentValueAsPath(const std::string& name) const;
  
  // Validation
  bool isValid(const OSArgumentVector& argumentVector) const;
  std::vector<std::string> validate(const OSArgumentVector& argumentVector) const;
};

Measure Execution Environment

Runtime environment for measure execution with logging and progress tracking.

/**
 * Measure execution environment and logging
 */
class OSRunner {
public:
  // Constructor
  OSRunner();
  OSRunner(const WorkflowJSON& workflow);
  
  // Logging methods
  void registerInfo(const std::string& message);
  void registerWarning(const std::string& message);
  void registerError(const std::string& message);
  void registerAsNotApplicable(const std::string& message);
  void registerFinalCondition(const std::string& message);
  
  // Progress tracking
  void registerInitialCondition(const std::string& message);
  void registerProgress(double percentComplete, const std::string& message = std::string());
  
  // Value registration for reporting
  void registerValue(const std::string& name, double value, const std::string& units = std::string());
  void registerValue(const std::string& name, int value, const std::string& units = std::string());
  void registerValue(const std::string& name, const std::string& value);
  
  // File handling
  bool registerFileForUpload(const Path& path, const std::string& filename = std::string());
  Path workflow() const;
  
  // Result queries
  bool result() const;
  std::vector<LogMessage> errors() const;
  std::vector<LogMessage> warnings() const;
  std::vector<LogMessage> info() const;
  
  // Halt execution
  void haltWorkflow(const std::string& completedStatus = std::string());
  
  // Workflow step information
  boost::optional<WorkflowStep> currentStep() const;
  void setCurrentStep(const WorkflowStep& step);
  
  // Model and workspace access during workflow
  boost::optional<Model> lastOpenStudioModel() const;
  void setLastOpenStudioModel(const Model& model);
  
  boost::optional<Workspace> lastEnergyPlusWorkspace() const;
  void setLastEnergyPlusWorkspace(const Workspace& workspace);
  
  boost::optional<SqlFile> lastEnergyPlusSqlFile() const;
  void setLastEnergyPlusSqlFile(const SqlFile& sqlFile);
};

Building Component Library Integration

Integration with online measure and component libraries.

/**
 * Building Component Library measure access
 */
class BCLMeasure {
public:
  // Constructor from local directory
  BCLMeasure(const Path& directory);
  
  // Metadata access
  std::string uid() const;
  std::string name() const;
  std::string displayName() const;
  std::string className() const;
  std::string description() const;
  std::string modelerDescription() const;
  
  // Version and provenance
  VersionString version() const;
  std::string xmlChecksum() const;
  
  // Taxonomy and tagging
  std::vector<std::string> tags() const;
  std::vector<BCLMeasureArgument> arguments() const;
  std::vector<BCLMeasureOutput> outputs() const;
  
  // File access
  Path directory() const;
  std::vector<BCLFileReference> files() const;
  
  // Measure type
  MeasureType measureType() const;
  
  // Validation
  bool isValid() const;
  std::vector<std::string> errors() const;
  
  // Updates from BCL
  bool checkForUpdates();
  bool update();
};

/**
 * BCL measure argument metadata
 */
class BCLMeasureArgument {
public:
  // Properties
  std::string name() const;
  std::string displayName() const;
  std::string description() const;
  std::string type() const;
  std::string units() const;
  bool required() const;
  
  // Default value
  boost::optional<std::string> defaultValue() const;
  
  // Choice values for choice arguments
  std::vector<std::string> choiceValues() const;
  std::vector<std::string> choiceDisplayNames() const;
  
  // Numeric constraints
  boost::optional<double> minValue() const;
  boost::optional<double> maxValue() const;
};

/**
 * BCL measure output metadata  
 */
class BCLMeasureOutput {
public:
  std::string name() const;
  std::string displayName() const;
  std::string shortName() const;
  std::string description() const;
  std::string type() const;
  std::string units() const;
  std::string modelDependent() const;
};

// Measure type enumeration
enum class MeasureType {
  ModelMeasure,
  EnergyPlusMeasure,
  ReportingMeasure
};

Measure Utilities

Helper classes and utilities for common measure operations.

/**
 * Utilities for common measure operations
 */
namespace MeasureUtils {
  
  // Model object utilities
  template<typename T>
  std::vector<T> getObjectsByType(const Model& model);
  
  template<typename T>
  boost::optional<T> getObjectByName(const Model& model, const std::string& name);
  
  // Space and zone utilities
  std::vector<Space> getSpacesByType(const Model& model, 
                                     const boost::optional<SpaceType>& spaceType = boost::none);
  std::vector<ThermalZone> getZonesByName(const Model& model, const std::string& namePattern);
  
  // Schedule utilities
  boost::optional<Schedule> getScheduleByName(const Model& model, const std::string& name);
  Schedule createConstantSchedule(Model& model, const std::string& name, double value);
  
  // Construction and material utilities
  std::vector<Construction> getConstructionsByType(const Model& model, const std::string& type);
  boost::optional<Construction> cloneConstruction(Model& model, 
                                                  const Construction& originalConstruction,
                                                  const std::string& newName);
  
  // Load utilities
  double getTotalFloorArea(const Model& model);
  double getTotalConditionedFloorArea(const Model& model);
  std::map<SpaceType, double> getFloorAreaBySpaceType(const Model& model);
  
  // Cost utilities
  double getTotalCost(const std::vector<LifeCycleCost>& costs);
  boost::optional<LifeCycleCost> addCostToObject(ModelObject& object,
                                                 const std::string& name,
                                                 const std::string& category,
                                                 double cost);
  
  // Validation utilities
  bool validateModel(const Model& model, OSRunner& runner);
  std::vector<std::string> getModelWarnings(const Model& model);
  std::vector<std::string> getModelErrors(const Model& model);
}

Common Usage Patterns

Creating a Model Measure

#include <openstudio/measure/ModelMeasure.hpp>
#include <openstudio/model/Model.hpp>
#include <openstudio/model/Space.hpp>

using namespace openstudio;
using namespace openstudio::model;

/**
 * Example measure that adds daylighting controls to spaces
 */
class AddDaylightingControls : public ModelMeasure {
public:
  // Measure metadata
  virtual std::string name() const override {
    return "Add Daylighting Controls";
  }
  
  virtual std::string description() const override {
    return "This measure adds daylighting controls to specified space types.";
  }
  
  virtual std::string modeler_description() const override {
    return "Adds DaylightingControl objects to spaces with the specified space type.";
  }
  
  // Define measure arguments
  virtual OSArgumentVector arguments() override {
    OSArgumentVector args;
    
    // Space type selection
    OSArgument spaceTypeName = OSArgument::makeStringArgument("space_type_name", true);
    spaceTypeName.setDisplayName("Space Type Name");
    spaceTypeName.setDescription("Name of space type to add daylighting controls to");
    args.push_back(spaceTypeName);
    
    // Control illuminance setpoint
    OSArgument illuminanceSetpoint = OSArgument::makeDoubleArgument("illuminance_setpoint", true);
    illuminanceSetpoint.setDisplayName("Illuminance Setpoint");
    illuminanceSetpoint.setDescription("Illuminance setpoint for daylighting control");
    illuminanceSetpoint.setUnits("lux");
    illuminanceSetpoint.setDefaultValue(300.0);
    illuminanceSetpoint.setMinValue(100.0);
    illuminanceSetpoint.setMaxValue(1000.0);
    args.push_back(illuminanceSetpoint);
    
    return args;
  }
  
  // Implement measure logic
  virtual bool run(Model& model, OSRunner& runner, const OSArgumentMap& user_arguments) override {
    
    // Get arguments
    std::string spaceTypeName = user_arguments.getArgumentValueAsString("space_type_name");
    double illuminanceSetpoint = user_arguments.getArgumentValueAsDouble("illuminance_setpoint", 300.0);
    
    runner.registerInitialCondition("Starting daylighting controls addition");
    
    // Find space type
    boost::optional<SpaceType> spaceType;
    for (const auto& st : model.getConcreteModelObjects<SpaceType>()) {
      if (st.name() && *st.name() == spaceTypeName) {
        spaceType = st;
        break;
      }
    }
    
    if (!spaceType) {
      runner.registerError("Space type '" + spaceTypeName + "' not found in model");
      return false;
    }
    
    // Add daylighting controls to spaces with this space type
    int controlsAdded = 0;
    std::vector<Space> spaces = model.getConcreteModelObjects<Space>();
    
    for (auto& space : spaces) {
      if (space.spaceType() && space.spaceType().get() == spaceType.get()) {
        
        // Create daylighting control
        DaylightingControl daylightingControl(model);
        daylightingControl.setSpace(space);
        daylightingControl.setIlluminanceSetpoint(illuminanceSetpoint);
        
        // Position at space centroid
        boost::optional<Point3d> centroid = space.transformation() * space.floorPrint().centroid();
        if (centroid) {
          daylightingControl.setPositionXCoordinate(centroid->x());
          daylightingControl.setPositionYCoordinate(centroid->y());
          daylightingControl.setPositionZCoordinate(centroid->z() + 0.8); // 0.8m above floor
        }
        
        controlsAdded++;
        runner.registerInfo("Added daylighting control to space: " + space.name().get_value_or("Unnamed"));
      }
    }
    
    if (controlsAdded == 0) {
      runner.registerAsNotApplicable("No spaces found with space type '" + spaceTypeName + "'");
      return true;
    }
    
    runner.registerFinalCondition("Added " + std::to_string(controlsAdded) + " daylighting controls");
    runner.registerValue("daylighting_controls_added", controlsAdded);
    
    return true;
  }
};

Creating an EnergyPlus Measure

#include <openstudio/measure/EnergyPlusMeasure.hpp>
#include <openstudio/utilities/idf/Workspace.hpp>

using namespace openstudio;

/**
 * Example measure that modifies EnergyPlus simulation parameters
 */
class ModifySimulationParameters : public EnergyPlusMeasure {
public:
  virtual std::string name() const override {
    return "Modify Simulation Parameters";
  }
  
  virtual std::string description() const override {
    return "This measure modifies EnergyPlus simulation control parameters.";
  }
  
  virtual OSArgumentVector arguments() override {
    OSArgumentVector args;
    
    // Timestep selection
    std::vector<std::string> timestepChoices{"1", "2", "4", "6", "10", "15", "20", "30", "60"};
    OSArgument timesteps = OSArgument::makeChoiceArgument("timesteps_per_hour", timestepChoices, true);
    timesteps.setDisplayName("Timesteps per Hour");
    timesteps.setDefaultValue("6");
    args.push_back(timesteps);
    
    // Solar distribution
    std::vector<std::string> solarChoices{"MinimalShadowing", "FullExterior", "FullInteriorAndExterior"};
    OSArgument solarDistribution = OSArgument::makeChoiceArgument("solar_distribution", solarChoices, true);
    solarDistribution.setDisplayName("Solar Distribution Algorithm");
    solarDistribution.setDefaultValue("FullExterior");
    args.push_back(solarDistribution);
    
    return args;
  }
  
  virtual bool run(Workspace& workspace, OSRunner& runner, const OSArgumentMap& user_arguments) override {
    
    int timestepsPerHour = user_arguments.getArgumentValueAsInteger("timesteps_per_hour", 6);
    std::string solarDistribution = user_arguments.getArgumentValueAsString("solar_distribution", "FullExterior");
    
    runner.registerInitialCondition("Modifying EnergyPlus simulation parameters");
    
    // Modify timestep
    std::vector<WorkspaceObject> timestepObjects = workspace.getObjectsByType(IddObjectType::Timestep);
    if (!timestepObjects.empty()) {
      WorkspaceObject& timestepObj = timestepObjects[0];
      timestepObj.setInt(0, timestepsPerHour);
      runner.registerInfo("Set timesteps per hour to: " + std::to_string(timestepsPerHour));
    } else {
      // Create new timestep object
      IdfObject newTimestep(IddObjectType::Timestep);
      newTimestep.setInt(0, timestepsPerHour);
      workspace.addObject(newTimestep);
      runner.registerInfo("Created new timestep object with " + std::to_string(timestepsPerHour) + " timesteps per hour");
    }
    
    // Modify building object for solar distribution
    std::vector<WorkspaceObject> buildingObjects = workspace.getObjectsByType(IddObjectType::Building);
    if (!buildingObjects.empty()) {
      WorkspaceObject& building = buildingObjects[0];
      building.setString(2, solarDistribution); // Solar Distribution field
      runner.registerInfo("Set solar distribution to: " + solarDistribution);
    }
    
    runner.registerFinalCondition("Successfully modified simulation parameters");
    return true;
  }
};

Using Measures in Applications

#include <openstudio/measure/OSRunner.hpp>
#include <openstudio/utilities/bcl/BCLMeasure.hpp>

using namespace openstudio;

// Function to run a measure on a model
bool runMeasureOnModel(Model& model, const BCLMeasure& measure, const OSArgumentMap& arguments) {
  
  // Create runner
  OSRunner runner;
  
  // Load measure class (this would typically involve dynamic loading)
  // For this example, assume we have the measure instance
  std::unique_ptr<ModelMeasure> measureInstance = loadMeasureFromBCL(measure);
  
  if (!measureInstance) {
    std::cout << "Failed to load measure: " << measure.name() << std::endl;
    return false;
  }
  
  // Run the measure
  bool success = measureInstance->run(model, runner, arguments);
  
  // Handle results
  std::vector<LogMessage> errors = runner.errors();
  std::vector<LogMessage> warnings = runner.warnings();
  std::vector<LogMessage> info = runner.info();
  
  // Log all messages
  for (const auto& msg : errors) {
    std::cout << "ERROR: " << msg.logMessage() << std::endl;
  }
  
  for (const auto& msg : warnings) {
    std::cout << "WARNING: " << msg.logMessage() << std::endl;
  }
  
  for (const auto& msg : info) {
    std::cout << "INFO: " << msg.logMessage() << std::endl;
  }
  
  return success && errors.empty();
}

// Function to apply multiple measures in sequence
bool runMeasureSequence(Model& model, const std::vector<std::pair<BCLMeasure, OSArgumentMap>>& measures) {
  
  for (const auto& measurePair : measures) {
    const BCLMeasure& measure = measurePair.first;
    const OSArgumentMap& arguments = measurePair.second;
    
    std::cout << "Running measure: " << measure.displayName() << std::endl;
    
    bool success = runMeasureOnModel(model, measure, arguments);
    if (!success) {
      std::cout << "Measure failed: " << measure.displayName() << std::endl;
      return false;
    }
  }
  
  return true;
}

Argument Validation and Error Handling

#include <openstudio/measure/OSArgument.hpp>

using namespace openstudio;

// Function to create and validate measure arguments
OSArgumentMap createValidatedArguments(const OSArgumentVector& argumentDefinitions) {
  OSArgumentMap argumentMap;
  
  for (const auto& argDef : argumentDefinitions) {
    OSArgument arg = argDef; // Copy the definition
    
    // Set values based on argument type and validation rules
    if (argDef.type() == OSArgument::Type::Double) {
      double value = 25.0; // Example value
      
      // Check constraints
      if (argDef.minValue() && value < *argDef.minValue()) {
        value = *argDef.minValue();
        std::cout << "Adjusted " << argDef.name() << " to minimum value: " << value << std::endl;
      }
      
      if (argDef.maxValue() && value > *argDef.maxValue()) {
        value = *argDef.maxValue();
        std::cout << "Adjusted " << argDef.name() << " to maximum value: " << value << std::endl;
      }
      
      arg.setDefaultValue(value);
    }
    
    else if (argDef.type() == OSArgument::Type::Choice) {
      std::vector<std::string> choices = argDef.choiceValues();
      if (!choices.empty()) {
        arg.setDefaultValue(choices[0]); // Use first choice as default
      }
    }
    
    argumentMap.insert(argDef.name(), arg);
  }
  
  // Validate the complete argument map
  std::vector<std::string> validationErrors = argumentMap.validate(argumentDefinitions);
  if (!validationErrors.empty()) {
    std::cout << "Validation errors:" << std::endl;
    for (const auto& error : validationErrors) {
      std::cout << "  " << error << std::endl;
    }
  }
  
  return argumentMap;
}

Install with Tessl CLI

npx tessl i tessl/pypi-openstudio

docs

energyplus.md

index.md

measure.md

model.md

radiance.md

utilities.md

workflow.md

tile.json