Cross-platform collection of software tools to support whole building energy modeling using EnergyPlus and advanced daylight analysis using Radiance
—
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.
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;
};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;
};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);
};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
};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);
}#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;
}
};#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;
}
};#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;
}#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