Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,5 @@ jobs:
gem install bundler -v 2.4.10
bundle config set --local path "$GITHUB_WORKSPACE/.bundle/install"
bundle install
- name: Run Integration Tests
run: bundle exec rspec spec/tests/integration/write_and_run_osws_spec.rb
if: always()
- name: Run Unit Tests
run: bundle exec rspec spec/tests/unit/buildingsync_reader_spec.rb
if: always()
- name: Run Tests
run: bundle exec rspec
7 changes: 7 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-checkstyle_formatter (0.6.0)
rubocop (>= 1.14.0)
rubocop-performance (1.20.0)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-ole (1.2.13.1)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
Expand Down Expand Up @@ -191,6 +196,8 @@ DEPENDENCIES
rake (~> 13.0)
rspec (~> 3.13)
rubocop (= 1.50)
rubocop-checkstyle_formatter (= 0.6.0)
rubocop-performance (= 1.20.0)

BUNDLED WITH
2.4.10
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ BuildingSync OpenStudio Simulator (BOSS) takes in BuildingSync files, creates Op
bundle install
```

4. Install external non-gem measure repositories declared in the shipped manifest.

```bash
bundle exec rake measures:install_external
```

5. (Optional) List resolved external measure roots.

```bash
bundle exec rake measures:list_external
```

The shipped manifest lives at [config/external_measure_repos.yml](config/external_measure_repos.yml) and is preconfigured with:

- local measure roots: `lib/measures` (first in precedence)
- external repository: comstock (`measures` and `resources/measures`)

Measure path precedence is:

1. local measure roots from [config/external_measure_repos.yml](config/external_measure_repos.yml)
2. gem-provided measure directories
3. external non-gem repository measure roots from [config/external_measure_repos.yml](config/external_measure_repos.yml)

The generated `in.osw` will include these paths in order under `measure_paths`.

## OpenStudio Compatibility Version

Expand Down Expand Up @@ -97,6 +121,11 @@ bundle exec boss write_baseline_osw BUILDINGSYNC_FILE -o OUTPUT_PATH [options]
bundle exec boss run_osw BUILDINGSYNC_FILE -o OUTPUT_PATH [options]
```

To get help text, type the following:
```bash
bundle exec boss help [COMMAND]
```

### Options

- `-o`, `--output_path` (required): directory where BOSS writes output (for example, use an XML-specific folder such as `output/v2.7.0/L100_Audit-1.0.0`)
Expand Down Expand Up @@ -131,7 +160,6 @@ Use this when `output/v2.7.0/L100_Audit-1.0.0/baseline/in.osw` already exists:

```bash
bundle exec boss run_osw \
"spec/files/v2.7.0/L100_Audit-1.0.0.xml" \
-o output/v2.7.0/L100_Audit-1.0.0
```

Expand Down
29 changes: 28 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,37 @@

require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
require_relative './lib/BOSS/constants'
require_relative './lib/BOSS/external_measure_repo_manager'

RSpec::Core::RakeTask.new(:spec)

require 'rubocop/rake_task'
RuboCop::RakeTask.new

task default: :spec
task default: :spec

namespace :measures do
desc 'Clone/fetch external non-gem measure repositories declared in config/external_measure_repos.yml'
task :install_external do
manager = BOSS::ExternalMeasureRepoManager.new

if !manager.manifest_exists?
puts "No external measure manifest found at #{EXTERNAL_MEASURE_REPOS_MANIFEST_PATH}"
next
end

dirs = manager.install_all
puts "Installed external measure roots (#{dirs.length}):"
dirs.each { |dir| puts " - #{dir}" }
end

desc 'List resolved external non-gem measure directories'
task :list_external do
manager = BOSS::ExternalMeasureRepoManager.new
dirs = manager.resolved_measure_directories

puts "External measure roots (#{dirs.length}):"
dirs.each { |dir| puts " - #{dir}" }
end
end
27 changes: 27 additions & 0 deletions config/external_measure_repos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# External non-gem measure repositories.
#
# Precedence in workflow measure lookup is:
# 1) local_measure_roots below
# 2) gem-provided measure dirs
# 3) external repos listed here
#
# local_measure_roots are resolved relative to this file.
# The default shipped configuration keeps this repository's local measures first.
local_measure_roots:
- ../lib/measures

# For each repo:
# - name: local checkout folder name under vendor/external_measures
# - url: git repository URL
# - ref: pinned tag/branch/commit to checkout
# - measure_roots: one or more paths inside the repo that contain measure directories
#

# The below measures are needed for the BOSS L200 workflow.
repos:
- name: comstock
url: https://github.com/NatLabRockies/comstock.git
ref: main
measure_roots:
- measures
- resources/measures
50 changes: 48 additions & 2 deletions lib/BOSS/boss.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
require 'fileutils'
require 'BOSS/buildingsync_reader/buildingsync_reader'
require 'BOSS/osw_arg_populator'
require 'BOSS/external_measure_repo_manager'

require 'openstudio/common_measures'
require 'openstudio/model_articulation'
Expand All @@ -20,7 +21,8 @@ class Boss
# @param output_dir [String]
# @param epw_file_path [String] if provided, full/path/to/my.epw
# @param standard_to_be_used [String]
def self.write_baseline_osw(xml_file_path, output_dir, epw_file_path, standard_to_be_used)
class << self
def write_baseline_osw(xml_file_path, output_dir, epw_file_path, standard_to_be_used)
# check file exists
if !File.exist?(xml_file_path)
message = "File '#{xml_file_path}' does not exist"
Expand Down Expand Up @@ -48,6 +50,31 @@ def self.write_baseline_osw(xml_file_path, output_dir, epw_file_path, standard_t
OSWArgPopulator::populate_set_electric_equipment_loads_by_epd_args(baseline_osw, bsync_reader)
OSWArgPopulator::populate_openstudio_results_args(baseline_osw, bsync_reader)

# Gather extension-derived paths (typically gem-based measures/files) from ObjectSpace.
OpenStudio::Extension.configure_osw(baseline_osw)

# Force ordering: local BOSS measures, extension-discovered (gems), then external repos.
manager = ExternalMeasureRepoManager.new
local_measure_dirs = manager.local_measure_directories
external_measure_dirs = manager.resolved_measure_directories
discovered_measure_dirs = baseline_osw[:measure_paths] || []

discovered_nonlocal_nonexternal = discovered_measure_dirs.reject do |path|
local_measure_dirs.include?(path) || external_measure_dirs.include?(path)
end

baseline_osw[:measure_paths] = ordered_unique(local_measure_dirs + discovered_nonlocal_nonexternal + external_measure_dirs)

local_file_paths = local_measure_dirs.map { |path| measure_path_to_files_path(path) }
external_file_paths = external_measure_dirs.map { |path| measure_path_to_files_path(path) }
discovered_file_paths = baseline_osw[:file_paths] || []

discovered_file_nonlocal_nonexternal = discovered_file_paths.reject do |path|
local_file_paths.include?(path) || external_file_paths.include?(path)
end

baseline_osw[:file_paths] = ordered_unique(local_file_paths + discovered_file_nonlocal_nonexternal + external_file_paths)

# write to file
workflow_dir = File.join(output_dir, 'baseline')
FileUtils.mkdir_p(workflow_dir)
Expand All @@ -59,7 +86,7 @@ def self.write_baseline_osw(xml_file_path, output_dir, epw_file_path, standard_t
end

# @param osw_dir [String] path to osws
def self.run_baseline_osw(osw_dir)
def run_baseline_osw(osw_dir)
# assert we have a baseline osm
baseline_osw_path = File.join(osw_dir, 'baseline', 'in.osw')

Expand All @@ -78,5 +105,24 @@ def self.run_baseline_osw(osw_dir)
# run the baseline osm
return runner.run_osws([baseline_osw_path])
end

private

def measure_path_to_files_path(measure_path)
path = measure_path.to_s
replaced = path.sub(%r{/measures/?$}, '/files')
return replaced if replaced != path

File.expand_path(File.join(path, '..', 'files'))
end

def ordered_unique(paths)
(paths || []).each_with_object([]) do |path, acc|
next if path.nil? || path.empty? || acc.include?(path)

acc << path
end
end
end
end
end
5 changes: 3 additions & 2 deletions lib/BOSS/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
SCHEMA_2_7_0_URL = 'https://raw.githubusercontent.com/BuildingSync/schema/v2.7.0/BuildingSync.xsd'
EMPTY_BASELINE_OSW_PATH = File.expand_path(File.join(__dir__, 'empty_baseline.osw'))
BUILDING_TYPES_BY_OCCUPANCY_CLASSIFICATION_PATH = File.expand_path(File.join(__dir__, 'buildingsync_reader/building_types_by_occupancy_classification.json'))

LOCAL_MEASURES_DIR = File.expand_path(File.join(__dir__, '..', 'measures'))
WEATHER_DIR = File.expand_path(File.join(__dir__, '../../weather'))

EXTERNAL_MEASURE_REPOS_MANIFEST_PATH = File.expand_path(File.join(__dir__, '..', '..', 'config', 'external_measure_repos.yml'))
EXTERNAL_MEASURE_REPOS_INSTALL_DIR = File.expand_path(File.join(__dir__, '..', '..', 'vendor', 'external_measures'))

# Standards strings
ASHRAE90_1 = 'ASHRAE90.1'
Expand Down
Loading
Loading