0
# Measure Framework
1
2
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.
3
4
## Capabilities
5
6
### Base Measure Classes
7
8
Core measure framework providing the foundation for all measure types.
9
10
```cpp { .api }
11
/**
12
* Base class for all OpenStudio measures
13
* Defines the interface for parametric model modifications
14
*/
15
class OSMeasure {
16
public:
17
// Virtual destructor
18
virtual ~OSMeasure() = default;
19
20
// Core measure interface
21
virtual std::string name() const = 0;
22
virtual std::string description() const = 0;
23
virtual std::string modeler_description() const = 0;
24
25
// Argument definition - defines measure parameters
26
virtual OSArgumentVector arguments() = 0;
27
28
// Main execution method - implemented by derived classes
29
virtual bool run(OSRunner& runner, const OSArgumentMap& user_arguments) = 0;
30
31
// Measure metadata
32
virtual std::vector<std::string> tags() const;
33
virtual std::vector<std::string> outcomes() const;
34
35
// File and resource handling
36
virtual std::vector<FileReferenceType> fileReferenceTypes() const;
37
virtual std::vector<OSAttribute> outputAttributes() const;
38
};
39
40
/**
41
* Model-based measure - operates on OpenStudio models
42
*/
43
class ModelMeasure : public OSMeasure {
44
public:
45
// Constructor
46
ModelMeasure() = default;
47
48
// Model-specific run method
49
virtual bool run(Model& model,
50
OSRunner& runner,
51
const OSArgumentMap& user_arguments) = 0;
52
53
// Final run implementation calls model-specific version
54
virtual bool run(OSRunner& runner, const OSArgumentMap& user_arguments) override final;
55
};
56
57
/**
58
* EnergyPlus-based measure - operates on IDF workspaces
59
*/
60
class EnergyPlusMeasure : public OSMeasure {
61
public:
62
// Constructor
63
EnergyPlusMeasure() = default;
64
65
// Workspace-specific run method
66
virtual bool run(Workspace& workspace,
67
OSRunner& runner,
68
const OSArgumentMap& user_arguments) = 0;
69
70
// Final run implementation calls workspace-specific version
71
virtual bool run(OSRunner& runner, const OSArgumentMap& user_arguments) override final;
72
};
73
74
/**
75
* Reporting measure - processes simulation results
76
*/
77
class ReportingMeasure : public OSMeasure {
78
public:
79
// Constructor
80
ReportingMeasure() = default;
81
82
// Reporting-specific run method
83
virtual bool run(OSRunner& runner,
84
const OSArgumentMap& user_arguments) = 0;
85
};
86
```
87
88
### Measure Arguments
89
90
Type-safe parameter system for measure configuration and user input.
91
92
```cpp { .api }
93
/**
94
* Measure argument definition and validation
95
*/
96
class OSArgument {
97
public:
98
// Argument types
99
enum class Type {
100
Boolean,
101
Double,
102
Integer,
103
String,
104
Choice,
105
Path
106
};
107
108
// Static factory methods for different argument types
109
static OSArgument makeBoolArgument(const std::string& name,
110
bool required = true);
111
112
static OSArgument makeDoubleArgument(const std::string& name,
113
bool required = true);
114
115
static OSArgument makeIntegerArgument(const std::string& name,
116
bool required = true);
117
118
static OSArgument makeStringArgument(const std::string& name,
119
bool required = true);
120
121
static OSArgument makeChoiceArgument(const std::string& name,
122
const std::vector<std::string>& choices,
123
bool required = true);
124
125
static OSArgument makePathArgument(const std::string& name,
126
bool isRead = true,
127
const std::string& extension = std::string(),
128
bool required = true);
129
130
// Argument properties
131
std::string name() const;
132
Type type() const;
133
bool required() const;
134
135
// Display properties
136
std::string displayName() const;
137
void setDisplayName(const std::string& displayName);
138
139
std::string description() const;
140
void setDescription(const std::string& description);
141
142
std::string units() const;
143
void setUnits(const std::string& units);
144
145
// Default values
146
bool hasDefaultValue() const;
147
boost::optional<bool> defaultValueAsBool() const;
148
boost::optional<double> defaultValueAsDouble() const;
149
boost::optional<int> defaultValueAsInteger() const;
150
boost::optional<std::string> defaultValueAsString() const;
151
152
void setDefaultValue(bool value);
153
void setDefaultValue(double value);
154
void setDefaultValue(int value);
155
void setDefaultValue(const std::string& value);
156
157
// Value constraints
158
bool hasDomainType() const;
159
void setMinValue(double minValue);
160
void setMaxValue(double maxValue);
161
boost::optional<double> minValue() const;
162
boost::optional<double> maxValue() const;
163
164
// Choice-specific methods
165
std::vector<std::string> choiceValues() const;
166
std::vector<std::string> choiceDisplayNames() const;
167
void setChoiceDisplayName(const std::string& choice, const std::string& displayName);
168
};
169
170
/**
171
* Collection of arguments with type-safe value access
172
*/
173
using OSArgumentVector = std::vector<OSArgument>;
174
175
class OSArgumentMap {
176
public:
177
// Constructor
178
OSArgumentMap();
179
OSArgumentMap(const OSArgumentVector& arguments);
180
181
// Argument management
182
void insert(const std::string& name, const OSArgument& argument);
183
bool hasArgument(const std::string& name) const;
184
OSArgument getArgument(const std::string& name) const;
185
186
// Value access with type safety
187
boost::optional<bool> getArgumentValue(const std::string& name) const;
188
bool getArgumentValueAsBool(const std::string& name, bool defaultValue = false) const;
189
double getArgumentValueAsDouble(const std::string& name, double defaultValue = 0.0) const;
190
int getArgumentValueAsInteger(const std::string& name, int defaultValue = 0) const;
191
std::string getArgumentValueAsString(const std::string& name,
192
const std::string& defaultValue = std::string()) const;
193
Path getArgumentValueAsPath(const std::string& name) const;
194
195
// Validation
196
bool isValid(const OSArgumentVector& argumentVector) const;
197
std::vector<std::string> validate(const OSArgumentVector& argumentVector) const;
198
};
199
```
200
201
### Measure Execution Environment
202
203
Runtime environment for measure execution with logging and progress tracking.
204
205
```cpp { .api }
206
/**
207
* Measure execution environment and logging
208
*/
209
class OSRunner {
210
public:
211
// Constructor
212
OSRunner();
213
OSRunner(const WorkflowJSON& workflow);
214
215
// Logging methods
216
void registerInfo(const std::string& message);
217
void registerWarning(const std::string& message);
218
void registerError(const std::string& message);
219
void registerAsNotApplicable(const std::string& message);
220
void registerFinalCondition(const std::string& message);
221
222
// Progress tracking
223
void registerInitialCondition(const std::string& message);
224
void registerProgress(double percentComplete, const std::string& message = std::string());
225
226
// Value registration for reporting
227
void registerValue(const std::string& name, double value, const std::string& units = std::string());
228
void registerValue(const std::string& name, int value, const std::string& units = std::string());
229
void registerValue(const std::string& name, const std::string& value);
230
231
// File handling
232
bool registerFileForUpload(const Path& path, const std::string& filename = std::string());
233
Path workflow() const;
234
235
// Result queries
236
bool result() const;
237
std::vector<LogMessage> errors() const;
238
std::vector<LogMessage> warnings() const;
239
std::vector<LogMessage> info() const;
240
241
// Halt execution
242
void haltWorkflow(const std::string& completedStatus = std::string());
243
244
// Workflow step information
245
boost::optional<WorkflowStep> currentStep() const;
246
void setCurrentStep(const WorkflowStep& step);
247
248
// Model and workspace access during workflow
249
boost::optional<Model> lastOpenStudioModel() const;
250
void setLastOpenStudioModel(const Model& model);
251
252
boost::optional<Workspace> lastEnergyPlusWorkspace() const;
253
void setLastEnergyPlusWorkspace(const Workspace& workspace);
254
255
boost::optional<SqlFile> lastEnergyPlusSqlFile() const;
256
void setLastEnergyPlusSqlFile(const SqlFile& sqlFile);
257
};
258
```
259
260
### Building Component Library Integration
261
262
Integration with online measure and component libraries.
263
264
```cpp { .api }
265
/**
266
* Building Component Library measure access
267
*/
268
class BCLMeasure {
269
public:
270
// Constructor from local directory
271
BCLMeasure(const Path& directory);
272
273
// Metadata access
274
std::string uid() const;
275
std::string name() const;
276
std::string displayName() const;
277
std::string className() const;
278
std::string description() const;
279
std::string modelerDescription() const;
280
281
// Version and provenance
282
VersionString version() const;
283
std::string xmlChecksum() const;
284
285
// Taxonomy and tagging
286
std::vector<std::string> tags() const;
287
std::vector<BCLMeasureArgument> arguments() const;
288
std::vector<BCLMeasureOutput> outputs() const;
289
290
// File access
291
Path directory() const;
292
std::vector<BCLFileReference> files() const;
293
294
// Measure type
295
MeasureType measureType() const;
296
297
// Validation
298
bool isValid() const;
299
std::vector<std::string> errors() const;
300
301
// Updates from BCL
302
bool checkForUpdates();
303
bool update();
304
};
305
306
/**
307
* BCL measure argument metadata
308
*/
309
class BCLMeasureArgument {
310
public:
311
// Properties
312
std::string name() const;
313
std::string displayName() const;
314
std::string description() const;
315
std::string type() const;
316
std::string units() const;
317
bool required() const;
318
319
// Default value
320
boost::optional<std::string> defaultValue() const;
321
322
// Choice values for choice arguments
323
std::vector<std::string> choiceValues() const;
324
std::vector<std::string> choiceDisplayNames() const;
325
326
// Numeric constraints
327
boost::optional<double> minValue() const;
328
boost::optional<double> maxValue() const;
329
};
330
331
/**
332
* BCL measure output metadata
333
*/
334
class BCLMeasureOutput {
335
public:
336
std::string name() const;
337
std::string displayName() const;
338
std::string shortName() const;
339
std::string description() const;
340
std::string type() const;
341
std::string units() const;
342
std::string modelDependent() const;
343
};
344
345
// Measure type enumeration
346
enum class MeasureType {
347
ModelMeasure,
348
EnergyPlusMeasure,
349
ReportingMeasure
350
};
351
```
352
353
### Measure Utilities
354
355
Helper classes and utilities for common measure operations.
356
357
```cpp { .api }
358
/**
359
* Utilities for common measure operations
360
*/
361
namespace MeasureUtils {
362
363
// Model object utilities
364
template<typename T>
365
std::vector<T> getObjectsByType(const Model& model);
366
367
template<typename T>
368
boost::optional<T> getObjectByName(const Model& model, const std::string& name);
369
370
// Space and zone utilities
371
std::vector<Space> getSpacesByType(const Model& model,
372
const boost::optional<SpaceType>& spaceType = boost::none);
373
std::vector<ThermalZone> getZonesByName(const Model& model, const std::string& namePattern);
374
375
// Schedule utilities
376
boost::optional<Schedule> getScheduleByName(const Model& model, const std::string& name);
377
Schedule createConstantSchedule(Model& model, const std::string& name, double value);
378
379
// Construction and material utilities
380
std::vector<Construction> getConstructionsByType(const Model& model, const std::string& type);
381
boost::optional<Construction> cloneConstruction(Model& model,
382
const Construction& originalConstruction,
383
const std::string& newName);
384
385
// Load utilities
386
double getTotalFloorArea(const Model& model);
387
double getTotalConditionedFloorArea(const Model& model);
388
std::map<SpaceType, double> getFloorAreaBySpaceType(const Model& model);
389
390
// Cost utilities
391
double getTotalCost(const std::vector<LifeCycleCost>& costs);
392
boost::optional<LifeCycleCost> addCostToObject(ModelObject& object,
393
const std::string& name,
394
const std::string& category,
395
double cost);
396
397
// Validation utilities
398
bool validateModel(const Model& model, OSRunner& runner);
399
std::vector<std::string> getModelWarnings(const Model& model);
400
std::vector<std::string> getModelErrors(const Model& model);
401
}
402
```
403
404
## Common Usage Patterns
405
406
### Creating a Model Measure
407
408
```cpp
409
#include <openstudio/measure/ModelMeasure.hpp>
410
#include <openstudio/model/Model.hpp>
411
#include <openstudio/model/Space.hpp>
412
413
using namespace openstudio;
414
using namespace openstudio::model;
415
416
/**
417
* Example measure that adds daylighting controls to spaces
418
*/
419
class AddDaylightingControls : public ModelMeasure {
420
public:
421
// Measure metadata
422
virtual std::string name() const override {
423
return "Add Daylighting Controls";
424
}
425
426
virtual std::string description() const override {
427
return "This measure adds daylighting controls to specified space types.";
428
}
429
430
virtual std::string modeler_description() const override {
431
return "Adds DaylightingControl objects to spaces with the specified space type.";
432
}
433
434
// Define measure arguments
435
virtual OSArgumentVector arguments() override {
436
OSArgumentVector args;
437
438
// Space type selection
439
OSArgument spaceTypeName = OSArgument::makeStringArgument("space_type_name", true);
440
spaceTypeName.setDisplayName("Space Type Name");
441
spaceTypeName.setDescription("Name of space type to add daylighting controls to");
442
args.push_back(spaceTypeName);
443
444
// Control illuminance setpoint
445
OSArgument illuminanceSetpoint = OSArgument::makeDoubleArgument("illuminance_setpoint", true);
446
illuminanceSetpoint.setDisplayName("Illuminance Setpoint");
447
illuminanceSetpoint.setDescription("Illuminance setpoint for daylighting control");
448
illuminanceSetpoint.setUnits("lux");
449
illuminanceSetpoint.setDefaultValue(300.0);
450
illuminanceSetpoint.setMinValue(100.0);
451
illuminanceSetpoint.setMaxValue(1000.0);
452
args.push_back(illuminanceSetpoint);
453
454
return args;
455
}
456
457
// Implement measure logic
458
virtual bool run(Model& model, OSRunner& runner, const OSArgumentMap& user_arguments) override {
459
460
// Get arguments
461
std::string spaceTypeName = user_arguments.getArgumentValueAsString("space_type_name");
462
double illuminanceSetpoint = user_arguments.getArgumentValueAsDouble("illuminance_setpoint", 300.0);
463
464
runner.registerInitialCondition("Starting daylighting controls addition");
465
466
// Find space type
467
boost::optional<SpaceType> spaceType;
468
for (const auto& st : model.getConcreteModelObjects<SpaceType>()) {
469
if (st.name() && *st.name() == spaceTypeName) {
470
spaceType = st;
471
break;
472
}
473
}
474
475
if (!spaceType) {
476
runner.registerError("Space type '" + spaceTypeName + "' not found in model");
477
return false;
478
}
479
480
// Add daylighting controls to spaces with this space type
481
int controlsAdded = 0;
482
std::vector<Space> spaces = model.getConcreteModelObjects<Space>();
483
484
for (auto& space : spaces) {
485
if (space.spaceType() && space.spaceType().get() == spaceType.get()) {
486
487
// Create daylighting control
488
DaylightingControl daylightingControl(model);
489
daylightingControl.setSpace(space);
490
daylightingControl.setIlluminanceSetpoint(illuminanceSetpoint);
491
492
// Position at space centroid
493
boost::optional<Point3d> centroid = space.transformation() * space.floorPrint().centroid();
494
if (centroid) {
495
daylightingControl.setPositionXCoordinate(centroid->x());
496
daylightingControl.setPositionYCoordinate(centroid->y());
497
daylightingControl.setPositionZCoordinate(centroid->z() + 0.8); // 0.8m above floor
498
}
499
500
controlsAdded++;
501
runner.registerInfo("Added daylighting control to space: " + space.name().get_value_or("Unnamed"));
502
}
503
}
504
505
if (controlsAdded == 0) {
506
runner.registerAsNotApplicable("No spaces found with space type '" + spaceTypeName + "'");
507
return true;
508
}
509
510
runner.registerFinalCondition("Added " + std::to_string(controlsAdded) + " daylighting controls");
511
runner.registerValue("daylighting_controls_added", controlsAdded);
512
513
return true;
514
}
515
};
516
```
517
518
### Creating an EnergyPlus Measure
519
520
```cpp
521
#include <openstudio/measure/EnergyPlusMeasure.hpp>
522
#include <openstudio/utilities/idf/Workspace.hpp>
523
524
using namespace openstudio;
525
526
/**
527
* Example measure that modifies EnergyPlus simulation parameters
528
*/
529
class ModifySimulationParameters : public EnergyPlusMeasure {
530
public:
531
virtual std::string name() const override {
532
return "Modify Simulation Parameters";
533
}
534
535
virtual std::string description() const override {
536
return "This measure modifies EnergyPlus simulation control parameters.";
537
}
538
539
virtual OSArgumentVector arguments() override {
540
OSArgumentVector args;
541
542
// Timestep selection
543
std::vector<std::string> timestepChoices{"1", "2", "4", "6", "10", "15", "20", "30", "60"};
544
OSArgument timesteps = OSArgument::makeChoiceArgument("timesteps_per_hour", timestepChoices, true);
545
timesteps.setDisplayName("Timesteps per Hour");
546
timesteps.setDefaultValue("6");
547
args.push_back(timesteps);
548
549
// Solar distribution
550
std::vector<std::string> solarChoices{"MinimalShadowing", "FullExterior", "FullInteriorAndExterior"};
551
OSArgument solarDistribution = OSArgument::makeChoiceArgument("solar_distribution", solarChoices, true);
552
solarDistribution.setDisplayName("Solar Distribution Algorithm");
553
solarDistribution.setDefaultValue("FullExterior");
554
args.push_back(solarDistribution);
555
556
return args;
557
}
558
559
virtual bool run(Workspace& workspace, OSRunner& runner, const OSArgumentMap& user_arguments) override {
560
561
int timestepsPerHour = user_arguments.getArgumentValueAsInteger("timesteps_per_hour", 6);
562
std::string solarDistribution = user_arguments.getArgumentValueAsString("solar_distribution", "FullExterior");
563
564
runner.registerInitialCondition("Modifying EnergyPlus simulation parameters");
565
566
// Modify timestep
567
std::vector<WorkspaceObject> timestepObjects = workspace.getObjectsByType(IddObjectType::Timestep);
568
if (!timestepObjects.empty()) {
569
WorkspaceObject& timestepObj = timestepObjects[0];
570
timestepObj.setInt(0, timestepsPerHour);
571
runner.registerInfo("Set timesteps per hour to: " + std::to_string(timestepsPerHour));
572
} else {
573
// Create new timestep object
574
IdfObject newTimestep(IddObjectType::Timestep);
575
newTimestep.setInt(0, timestepsPerHour);
576
workspace.addObject(newTimestep);
577
runner.registerInfo("Created new timestep object with " + std::to_string(timestepsPerHour) + " timesteps per hour");
578
}
579
580
// Modify building object for solar distribution
581
std::vector<WorkspaceObject> buildingObjects = workspace.getObjectsByType(IddObjectType::Building);
582
if (!buildingObjects.empty()) {
583
WorkspaceObject& building = buildingObjects[0];
584
building.setString(2, solarDistribution); // Solar Distribution field
585
runner.registerInfo("Set solar distribution to: " + solarDistribution);
586
}
587
588
runner.registerFinalCondition("Successfully modified simulation parameters");
589
return true;
590
}
591
};
592
```
593
594
### Using Measures in Applications
595
596
```cpp
597
#include <openstudio/measure/OSRunner.hpp>
598
#include <openstudio/utilities/bcl/BCLMeasure.hpp>
599
600
using namespace openstudio;
601
602
// Function to run a measure on a model
603
bool runMeasureOnModel(Model& model, const BCLMeasure& measure, const OSArgumentMap& arguments) {
604
605
// Create runner
606
OSRunner runner;
607
608
// Load measure class (this would typically involve dynamic loading)
609
// For this example, assume we have the measure instance
610
std::unique_ptr<ModelMeasure> measureInstance = loadMeasureFromBCL(measure);
611
612
if (!measureInstance) {
613
std::cout << "Failed to load measure: " << measure.name() << std::endl;
614
return false;
615
}
616
617
// Run the measure
618
bool success = measureInstance->run(model, runner, arguments);
619
620
// Handle results
621
std::vector<LogMessage> errors = runner.errors();
622
std::vector<LogMessage> warnings = runner.warnings();
623
std::vector<LogMessage> info = runner.info();
624
625
// Log all messages
626
for (const auto& msg : errors) {
627
std::cout << "ERROR: " << msg.logMessage() << std::endl;
628
}
629
630
for (const auto& msg : warnings) {
631
std::cout << "WARNING: " << msg.logMessage() << std::endl;
632
}
633
634
for (const auto& msg : info) {
635
std::cout << "INFO: " << msg.logMessage() << std::endl;
636
}
637
638
return success && errors.empty();
639
}
640
641
// Function to apply multiple measures in sequence
642
bool runMeasureSequence(Model& model, const std::vector<std::pair<BCLMeasure, OSArgumentMap>>& measures) {
643
644
for (const auto& measurePair : measures) {
645
const BCLMeasure& measure = measurePair.first;
646
const OSArgumentMap& arguments = measurePair.second;
647
648
std::cout << "Running measure: " << measure.displayName() << std::endl;
649
650
bool success = runMeasureOnModel(model, measure, arguments);
651
if (!success) {
652
std::cout << "Measure failed: " << measure.displayName() << std::endl;
653
return false;
654
}
655
}
656
657
return true;
658
}
659
```
660
661
### Argument Validation and Error Handling
662
663
```cpp
664
#include <openstudio/measure/OSArgument.hpp>
665
666
using namespace openstudio;
667
668
// Function to create and validate measure arguments
669
OSArgumentMap createValidatedArguments(const OSArgumentVector& argumentDefinitions) {
670
OSArgumentMap argumentMap;
671
672
for (const auto& argDef : argumentDefinitions) {
673
OSArgument arg = argDef; // Copy the definition
674
675
// Set values based on argument type and validation rules
676
if (argDef.type() == OSArgument::Type::Double) {
677
double value = 25.0; // Example value
678
679
// Check constraints
680
if (argDef.minValue() && value < *argDef.minValue()) {
681
value = *argDef.minValue();
682
std::cout << "Adjusted " << argDef.name() << " to minimum value: " << value << std::endl;
683
}
684
685
if (argDef.maxValue() && value > *argDef.maxValue()) {
686
value = *argDef.maxValue();
687
std::cout << "Adjusted " << argDef.name() << " to maximum value: " << value << std::endl;
688
}
689
690
arg.setDefaultValue(value);
691
}
692
693
else if (argDef.type() == OSArgument::Type::Choice) {
694
std::vector<std::string> choices = argDef.choiceValues();
695
if (!choices.empty()) {
696
arg.setDefaultValue(choices[0]); // Use first choice as default
697
}
698
}
699
700
argumentMap.insert(argDef.name(), arg);
701
}
702
703
// Validate the complete argument map
704
std::vector<std::string> validationErrors = argumentMap.validate(argumentDefinitions);
705
if (!validationErrors.empty()) {
706
std::cout << "Validation errors:" << std::endl;
707
for (const auto& error : validationErrors) {
708
std::cout << " " << error << std::endl;
709
}
710
}
711
712
return argumentMap;
713
}
714
```