Skip to content

Commit

Permalink
Merge pull request #1375 from pnnl/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
weilixu authored May 29, 2024
2 parents 511e6c9 + e687b13 commit d3f6b7a
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 12 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ruleset-checking-tool"
version = "0.2.6"
version = "0.2.7"
description = "PNNL ruleset checking tool"
authors = ["Weili Xu <[email protected]>", "Charlie Holly <[email protected]>", "Juan Gonzalez <[email protected]>", "Yun Joon Jung <[email protected]>", "Jiarong Xie <[email protected]>"]
license = "MIT"
Expand Down
13 changes: 9 additions & 4 deletions rct229/rule_engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def get_available_rules():
return available_rules


def evaluate_all_rules_rpd(ruleset_project_descriptions):
def evaluate_all_rules_rpd(ruleset_project_descriptions, session_id=""):
# Get reference to rule functions in rules model
available_rule_definitions = rulesets.__getrules__()
ruleset_models = get_rmd_instance()
Expand All @@ -49,7 +49,7 @@ def evaluate_all_rules_rpd(ruleset_project_descriptions):

print("Processing rules...")
rules_list = [rule_def[1]() for rule_def in available_rule_definitions]
report = evaluate_rules(rules_list, ruleset_models)
report = evaluate_rules(rules_list, ruleset_models, session_id=session_id)
report["rpd_files"] = rpd_rmd_map_list

return report
Expand Down Expand Up @@ -135,7 +135,11 @@ def evaluate_rule(rule, rmrs, test=False):


def evaluate_rules(
rules_list: list, rmds: RuleSetModels, unit_system=UNIT_SYSTEM.IP, test=False
rules_list: list,
rmds: RuleSetModels,
unit_system=UNIT_SYSTEM.IP,
test=False,
session_id="",
):
"""Evaluates a list of rules against an RMDs
Expand All @@ -147,6 +151,7 @@ def evaluate_rules(
Object containing RPDs for ruleset evaluation
test: Boolean
Flag to indicate whether this run is for software testing workflow or not.
session_id: string
Returns
-------
Expand Down Expand Up @@ -227,7 +232,7 @@ def evaluate_rules(
rule_counter += 1
if rule_counter in counting_steps:
print(
f"Compliance evaluation progress: {round(rule_counter / total_num_rules * 100)}%"
f"Project Evaluation Session ID: #{session_id}# => Compliance evaluation progress: {round(rule_counter / total_num_rules * 100)}%"
)
print(f"Processing Rule {rule.id}")
outcome = rule.evaluate(copied_rmds)
Expand Down
4 changes: 2 additions & 2 deletions rct229/rulesets/ashrae9012019/section1/section1rule6.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ def __init__(self):
USER=False, BASELINE_0=True, PROPOSED=True
),
id="1-6",
description="temp",
description="The proposed design shall be the same as the baseline design for all data elements identified in the schema hosted at data.standards.ashrae {{https://github.com/open229/ruleset-model-description-schema/blob/main/docs229/ASHRAE229_extra.schema.json}}",
ruleset_section_title="Performance Calculation",
standard_section="a",
standard_section="Table G3.1(1) Baseline Building Performance (a)",
is_primary_rule=True,
rmd_context="",
)
Expand Down
4 changes: 2 additions & 2 deletions rct229/rulesets/ashrae9012019/section1/section1rule7.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def __init__(self):
USER=True, BASELINE_0=False, PROPOSED=True
),
id="1-7",
description="temp",
description="The proposed design shall be the same as the user design for all data elements identified in the schema hosted at data.standards.ashrae {{https://github.com/open229/ruleset-model-description-schema/blob/main/docs229/ASHRAE229_extra.schema.json}}",
ruleset_section_title="Performance Calculation",
standard_section="a",
standard_section="Table G3.1(1) Proposed Building Performance (a)",
is_primary_rule=True,
rmd_context="",
)
Expand Down
87 changes: 87 additions & 0 deletions rct229/utils/std_comparisons.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import operator
from pint import Quantity
from decimal import Decimal, ROUND_HALF_UP


"""
Global tolerance for equality comparison. Default allows 0.5% variations of generated baseline/proposed from the standard specify value.
"""
Expand Down Expand Up @@ -29,3 +34,85 @@ def std_equal(
True iff the val is within percent_tolerance of std_val
"""
return abs(std_val - val) <= (percent_tolerance / 100) * abs(std_val)


def std_equal_with_precision(
val: Quantity | float | int,
std_val: Quantity | float | int,
precision: Quantity | float | int,
) -> bool:
"""Determines whether the model value and standard value are equal with the specified precision. If any of the function inputs are a Quantity type, then all function inputs must be Quantity.
Parameters
----------
val: Quantity | float | int
value extracted from model
std_val : Quantity | float | int
standard value from code
precision: Quantity | float | int
number of decimal places or significant value to round to, and intended units of the comparison
Returns
-------
bool
True if the modeled value is equal to the standard value within the specified precision
"""
# Check if all or none of the arguments are Quantity types
are_quantities = [isinstance(arg, Quantity) for arg in [val, std_val, precision]]
if not (all(are_quantities) or not any(are_quantities)):
raise TypeError(
"Arguments must be consistent in type: all Quantity or all non-Quantity."
)

# Determine if the values are pint Quantities and handle accordingly
if (
isinstance(val, Quantity)
and isinstance(std_val, Quantity)
and isinstance(precision, Quantity)
):
units = precision.units
val = val.to(units)
std_val = std_val.to(units)
val_magnitude = Decimal(str(val.magnitude))
std_val_magnitude = Decimal(str(std_val.magnitude))
precision_magnitude = Decimal(str(precision.magnitude))
else:
val_magnitude = Decimal(str(val))
std_val_magnitude = Decimal(str(std_val))
precision_magnitude = Decimal(str(precision))

# Determine rounding precision based on whether precision is a whole number or a decimal
if precision_magnitude.as_tuple().exponent < 0:
# Decimal places (e.g., 0.01)
precision_decimal_places = abs(precision_magnitude.as_tuple().exponent)
rounding_precision = f"1E-{str(precision_decimal_places)}"
else:
# Whole number (e.g., 10, 100)
rounding_precision = f"1E+{str(int(precision_magnitude.log10()))}"

# Round both values to the specified precision
val_rounded = val_magnitude.quantize(
Decimal(rounding_precision), rounding=ROUND_HALF_UP
)
std_val_rounded = std_val_magnitude.quantize(
Decimal(rounding_precision), rounding=ROUND_HALF_UP
)

# Compare the rounded values
return val_rounded == std_val_rounded


def std_conservative_outcome(
val: Quantity, std_val: Quantity, conservative_operator_wrt_std: operator
):
"""Determines if the model value has a conservative outcome compared to the standard value.
Parameters
----------
val: Quantity
value extracted from model
std_val : Quantity
standard value from code
conservative_operator_wrt_std: operator that results in a conservative outcome compared to the standard value
"""
return conservative_operator_wrt_std(val, std_val)
76 changes: 75 additions & 1 deletion rct229/utils/std_comparisons_test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import operator
import pytest

from rct229.schema.config import ureg
from rct229.utils.compare_standard_val import (
compare_standard_val,
compare_standard_val_strict,
)
from rct229.utils.std_comparisons import std_equal
from rct229.utils.std_comparisons import (
std_equal,
std_equal_with_precision,
std_conservative_outcome,
)

_M2 = ureg("m2")

Expand Down Expand Up @@ -150,3 +155,72 @@ def test__compare_standard_val_strict_gt__false_with_units():
std_val=1.0101 * _M2,
operator=operator.gt,
)


def test__std_equal_with_precision__false_types_vary():
with pytest.raises(TypeError):
std_equal_with_precision(1.05 * _M2, 1.1, 0.1)


def test__std_equal_with_precision__true_with_units():
assert std_equal_with_precision(1.05 * _M2, 1.1 * _M2, 0.1 * _M2)


def test__std_equal_with_precision__true_without_units():
assert std_equal_with_precision(1.05, 1.1, 0.1)


def test__std_equal_with_precision__false_with_units():
assert not std_equal_with_precision(1.15 * _M2, 1.1 * _M2, 0.1 * _M2)


def test__std_equal_with_precision__false_without_units():
assert not std_equal_with_precision(1.15, 1.1, 0.1)


def test__std_equal_with_precision__10_true_with_units():
assert std_equal_with_precision(145 * _M2, 150 * _M2, 10 * _M2)


def test__std_equal_with_precision__10_true_without_units():
assert std_equal_with_precision(145, 150, 10)


def test__std_equal_with_precision__10_false_with_units():
assert not std_equal_with_precision(155 * _M2, 150 * _M2, 10 * _M2)


def test__std_equal_with_precision__10_false_without_units():
assert not std_equal_with_precision(155, 150, 10)


def test__std_conservative_outcome__true_with_units_gt():
assert std_conservative_outcome(1.1 * _M2, 1.05 * _M2, operator.gt)


def test__std_conservative_outcome__true_with_units_lt():
assert std_conservative_outcome(1.05 * _M2, 1.1 * _M2, operator.lt)


def test__std_conservative_outcome__false_with_units_gt():
assert not std_conservative_outcome(1.09999 * _M2, 1.1 * _M2, operator.gt)


def test__std_conservative_outcome__false_with_units_lt():
assert not std_conservative_outcome(1.05001 * _M2, 1.05 * _M2, operator.lt)


def test__std_conservative_outcome__true_without_units_gt():
assert std_conservative_outcome(1.1, 1.05, operator.gt)


def test__std_conservative_outcome__true_without_units_lt():
assert std_conservative_outcome(1.05, 1.1, operator.lt)


def test__std_conservative_outcome__false_without_units_gt():
assert not std_conservative_outcome(1.09999, 1.1, operator.gt)


def test__std_conservative_outcome__false_without_units_lt():
assert not std_conservative_outcome(1.05001, 1.05, operator.lt)
7 changes: 5 additions & 2 deletions rct229/web_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ def run_software_test(ruleset, section=None, saving_dir="./"):
return report_dir


def run_project_evaluation(rpds, ruleset, reports=["RAW_OUTPUT"], saving_dir="./"):
def run_project_evaluation(
rpds, ruleset, reports=["RAW_OUTPUT"], saving_dir="./", session_id=""
):
"""
Parameters
Expand All @@ -130,6 +132,7 @@ def run_project_evaluation(rpds, ruleset, reports=["RAW_OUTPUT"], saving_dir="./
ruleset: str ruleset key
reports: list[str] list of strings and each string is the enum value of a report
saving_dir: directory to save report.
session_id: a string representing a calculation session
Returns
-------
Expand Down Expand Up @@ -161,7 +164,7 @@ def run_project_evaluation(rpds, ruleset, reports=["RAW_OUTPUT"], saving_dir="./

print("Test implementation of rule engine for ASHRAE Std 229 RCT.")
print("")
report = evaluate_all_rules_rpd(rpds)
report = evaluate_all_rules_rpd(rpds, session_id)

print(f"Saving reports to: {saving_dir}......")
report_path_list = []
Expand Down
Loading

0 comments on commit d3f6b7a

Please sign in to comment.