Skip to content

Commit

Permalink
Merge pull request #97 from NREL/measures-exist
Browse files Browse the repository at this point in the history
Validate that measures exist and arguments are correct
  • Loading branch information
nmerket authored Sep 20, 2019
2 parents fff6b4e + 926c6fa commit cb60f2e
Show file tree
Hide file tree
Showing 13 changed files with 5,941 additions and 3 deletions.
94 changes: 94 additions & 0 deletions buildstockbatch/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import zipfile
import csv
from collections import defaultdict
import xml.etree.ElementTree as ET

from buildstockbatch.__version__ import __schema_version__
from .workflow_generator import ResidentialDefaultWorkflowGenerator, CommercialDefaultWorkflowGenerator
Expand Down Expand Up @@ -287,6 +288,7 @@ def cleanup_sim_dir(sim_dir):
def validate_project(project_file):
assert(BuildStockBatchBase.validate_project_schema(project_file))
assert(BuildStockBatchBase.validate_xor_schema_keys(project_file))
assert(BuildStockBatchBase.validate_measures_and_arguments(project_file))
assert(BuildStockBatchBase.validate_options_lookup(project_file))
assert(BuildStockBatchBase.validate_measure_references(project_file))
assert(BuildStockBatchBase.validate_reference_scenario(project_file))
Expand Down Expand Up @@ -337,6 +339,98 @@ def validate_xor_schema_keys(project_file):
raise ValueError('Both/neither n_datapoints and buildstock_csv found in yaml baseline key')
return True

def validate_measures_and_arguments(project_file):
cfg = BuildStockBatchBase.get_project_configuration(project_file)
if cfg['stock_type'] != 'residential': # FIXME: add comstock logic
return True

buildstock_dir = os.path.join(os.path.dirname(project_file), cfg["buildstock_directory"])
measures_dir = f'{buildstock_dir}/measures'
type_map = {'Integer': int, 'Boolean': bool, 'String': str, 'Double': float}

measure_names = {
'ResidentialSimulationControls': 'residential_simulation_controls',
'BuildExistingModel': 'baseline',
'SimulationOutputReport': 'simulation_output_report',
'ServerDirectoryCleanup': None,
'ApplyUpgrade': 'upgrades',
'TimeseriesCSVExport': 'timeseries_csv_export'
}
if 'reporting_measures' in cfg.keys():
for reporting_measure in cfg['reporting_measures']:
measure_names[reporting_measure] = 'reporting_measures'

def get_measure_xml(xml_path):
tree = ET.parse(xml_path)
root = tree.getroot()
return root

error_msgs = ''
for measure_name in measure_names.keys():
measure_path = os.path.join(measures_dir, measure_name)

if measure_names[measure_name] in cfg.keys() or \
measure_names[measure_name] == 'residential_simulation_controls':
# if they exist in the cfg, make sure they exist in the buildstock checkout
if not os.path.exists(measure_path):
error_msgs += f"* {measure_name} does not exist in {buildstock_dir}. \n"

# check argument value types for residential simulation controls and timeseries csv export measures
if measure_name in ['ResidentialSimulationControls', 'TimeseriesCSVExport']:
root = get_measure_xml(os.path.join(measure_path, 'measure.xml'))

expected_arguments = {}
for argument in root.findall('./arguments/argument'):
for name in argument.findall('./name'):
expected_arguments[name.text] = []

if argument.find('./type').text == 'Choice':
for choice in argument.findall('./choices/choice'):
for value in choice.findall('./value'):
expected_arguments[name.text].append(value.text)
else:
expected_arguments[name.text].append(argument.find('./type').text)

# check only if that measure exists in cfg
if measure_names[measure_name] not in cfg.keys():
continue
for actual_argument_key in cfg[measure_names[measure_name]].keys():
if actual_argument_key not in expected_arguments.keys():
error_msgs += f"* Found unexpected argument key {actual_argument_key} for \
{measure_names[measure_name]} in yaml file. \n"

actual_argument_value = cfg[measure_names[measure_name]][actual_argument_key]

if actual_argument_key in expected_arguments.keys():
expected_argument_type = expected_arguments[actual_argument_key]

try:
if type(actual_argument_value) != list:
if not isinstance(actual_argument_value, type_map[expected_argument_type[0]]):
error_msgs += f"* Wrong argument value type for {actual_argument_key} for \
{measure_names[measure_name]} in yaml file. \n"
else:
for actual_argument_val in actual_argument_value:
if not isinstance(actual_argument_val, type_map[expected_argument_type[0]]):
error_msgs += f"* Wrong argument value type for {actual_argument_key} for \
{measure_names[measure_name]} in yaml file. \n"

except KeyError:
if len(expected_argument_type) > 1: # Choice
if actual_argument_value not in expected_argument_type:
error_msgs += f"* Found unexpected argument value {actual_argument_value} for \
{measure_names[measure_name]} in yaml file. \n"
else:
print(f"Found an unexpected argument value type: {expected_argument_type[0]}.")

if not error_msgs:
return True
else:
logger.error(error_msgs)
raise ValueError(error_msgs)

return True

@staticmethod
def validate_options_lookup(project_file):
"""
Expand Down
2 changes: 1 addition & 1 deletion buildstockbatch/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def _basic_residential_project_file(update_args={}):
},
'timeseries_csv_export': {
'reporting_frequency': 'Hourly',
'include_enduse_subcategories': 'true'
'include_enduse_subcategories': True
},
'eagle': {
'sampling': {
Expand Down
5 changes: 4 additions & 1 deletion buildstockbatch/test/test_eagle.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
from buildstockbatch.eagle import user_cli, EagleBatch


@patch('buildstockbatch.base.BuildStockBatchBase.validate_measures_and_arguments')
@patch('buildstockbatch.base.BuildStockBatchBase.validate_options_lookup')
@patch('buildstockbatch.eagle.subprocess')
def test_user_cli(mock_subprocess, mock_validate_options, basic_residential_project_file, monkeypatch):
def test_user_cli(mock_subprocess, mock_validate_options, mock_validate_measures, basic_residential_project_file,
monkeypatch):
mock_validate_measures.return_value = True
mock_validate_options.return_value = True

project_filename, results_dir = basic_residential_project_file()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
stock_type: residential
buildstock_directory: test_openstudio_buildstock
project_directory: project_singlefamilydetached
baseline:
n_datapoints: 30
n_buildings_represented: 81221016
residential_simulation_controls:
timesteps_per_hr: 4
begin_month: 1
begin_day_of_month: 1.5
end_month: 12
end_day_of_month: 31
upgrades:
- upgrade_name: good upgrade
options:
- option: Vintage|<1940
apply_logic:
- or:
- Insulation Slab|Good Option
- Insulation Slab|None
- not: Insulation Wall|Good Option
- and:
- Vintage|1960s||Vintage|1960s
- Vintage|1980s
- option: Insulation Finished Basement|Good Option
apply_logic:
- Insulation Unfinished Basement|Extra Argument
timeseries_csv_export:
reporting_frequency: Huorly
include_enduse_subcategories: true
output_variable:
- Zone Mean Air Temperature
reporting_measures:
- ReportingMeasure2
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
stock_type: residential
buildstock_directory: test_openstudio_buildstock
project_directory: project_singlefamilydetached
baseline:
n_datapoints: 30
n_buildings_represented: 81221016
upgrades:
- upgrade_name: good upgrade
options:
- option: Vintage|<1940
apply_logic:
- or:
- Insulation Slab|Good Option
- Insulation Slab|None
- not: Insulation Wall|Good Option
- and:
- Vintage|1960s||Vintage|1960s
- Vintage|1980s
- option: Insulation Finished Basement|Good Option
apply_logic:
- Insulation Unfinished Basement|Extra Argument
package_apply_logic: Vintage|1960s||Vintage|1940s
timeseries_csv_export:
reporting_frequency: Timestep
include_enduse_subcategories: true
output_variables:
- Zone Mean Air Temperature
reporting_measures:
- ReportingMeasure1
Loading

0 comments on commit cb60f2e

Please sign in to comment.