Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Rule22-42.md #1520

Open
wants to merge 8 commits into
base: feature/ashrae9012022
Choose a base branch
from
Open
Changes from 1 commit
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
98 changes: 98 additions & 0 deletions docs/section22/Rule22-42.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@

# CHW & CW - Rule 22-42

**Rule ID:** 22-42
**Rule Description:** The sets of performance curves specified in Table J-2 should be used to represent part-load performance of chillers in the baseline building design.
**Rule Assertion:** Baseline RMD = expected value
**Appendix G Section:** G3.2.2.1 Baseline

**Mandatory Rule:** True
**Evaluation Context:** Each Chiller
**Table Lookup:**
J-6
claperle marked this conversation as resolved.
Show resolved Hide resolved
**Function Call:**

## Applicability Check:
- look at each chiller - only water-cooled chillers are applicable (ie chillers with a condensing_loop): `for chiller in B_RMD.chillers:`
- if the chiller has a condensing_loop, and the chiller energy source is electricity, continue to rule logic: `if chiller.consensing_loop != NULL and chiller.energy_source_type == "ELECTRICITY": CONTINUE TO RULE LOGIC`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we include an applicability check for the baseline HVAC system type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. It overcomplicates things here, and if the user has issues with the HVAC system type, they'll be notified elsewhere. If we do a check here, it just means that if a user is using the RCT to validate a model then they have to do an extra iteration.


## Rule Logic:
- figure out the set of performance curves needed for the chiller - this should be V, X, Y, Z, AA or AB. Initialize a variable curve_set and set it to "NONE": `curve_set = "NONE"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weilixu I imagine that the actual implementation would use table lookups here. Is it clear enough from this RDS without using table lookups?

- get the chiller rated capacity: `rated_capacity = chiller.rated_capacity`
- convert the chiller rated capacity from watts to tons: `chiller_capacity_tons = rated_capacity * 0.000284345`
- check if the compressor type is CENTRIFUGAL: `if chiller.compressor_type == "CENTRIFUGAL":`
- if the capacity is less than 150 tons, the category is V: `if chiller_capacity_tons < 150: curve_set = "V"`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- else if the capacity is greater than or equal to 300 tons, the category is Y: `elif chiller_capacity_tons >= 300: curve_set = "Y"`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- otherwise, the capacity is between 150 and 300, so the curves set is X: `else: curve_set = "X"`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- otherwise, check if the compressor type is POSITIVE_DISPLACEMENT or SCROLL: `if chiller.compressor_type in ["POSITIVE_DISPLACEMENT","SCROLL"]:`
- if the capacity is less than 150 tons, the category is Z: `if chiller_capacity_tons < 150: curve_set = "Z"`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- else if the capacity is greater than or equal to 300 tons, the category is AA: `elif chiller_capacity_tons >= 300: curve_set = "AA"`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- otherwise, the capacity is between 150 and 300, so the curves set is AB: `else: curve_set = "AB"`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- continue if the curve set is not "NONE" - the curve set could be none if the chiller.compressor_type is not one of the recognized types for Appendix G: `if curve_set <> "NONE":`
- calculate the chiller rated power by dividing the chiller rated capacity by the chiller full load efficiency (cop): `rated_power = chiller.rated_capacity / chiller.full_load_efficiency`
- create a list of the expected capacity validation points - this is in 25% intervals, starting with 0.25 to 1: `expected_validation_plr = [0.25,0.5,0.75,1]`
- create a list of the expected temperatures for the chilled water temperature for the validation calculations - expected_chwt_temps - units are (F). The values are selected to capture the minimum, maximum, rating condition and some points in between: `expected_cwt_temps = [39,45,50,55]`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- create a list of the expected entering condenser temperatures for the condenser water temperature for the validation calculations - expected_ect_temps - units are (F). The values are selected to capture the minimum, maximum, rating condition and some points in between: `expected_ect_temps = [60,104,85,72.5,97.5]`
claperle marked this conversation as resolved.
Show resolved Hide resolved

- get the coefficients for the EIR-f-T curve (IP units) using a table lookup for table J-6. This assumes that the lookup function will return a list with the coefficients in order (C1,C2,...): `eir_f_t_coefficients = table_J_2_lookup(curve_set,"EIR-f-T")`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- get the coefficients for the CAP-f-T curve (IP units) using a table lookup for table J-6. This assumes that the lookup function will return a list with the coefficients in order (C1,C2,...): `cap_f_t_coefficients = table_J_2_lookup(curve_set,"Cap-f-T")`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weilixu do we need to create these lookup tables or do they exist for OS Standards?

Copy link
Collaborator

@weilixu weilixu Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@supriyagoel We do not have plan to develop 2022 in OS Standards in FY25.
In the case that if we are developing this feature, it would be a look up table.

- get the coefficients for the PLR curve (IP units) using a table lookup for table J-6. This assumes that the lookup function will return a list with the coefficients in order (C1,C2,...): `plr_coefficients = table_J_2_lookup(curve_set,"EIR-f-PLR")`

- create a variable (capacity_validation_status) to track whether the capacity validation passes and set it to "PASS" - if the capacity validation fails at any point, this variable will change to give the values at which the capacity validation failed: `capacity_validation_status = "PASS"`
- create a variable (power_validation_status) to track whether the power validation passes and set it to "PASS" - if the power validation fails at any point, this variable will change to give the values at which teh power validation failed: `power_validation_status = "PASS"

- convert the chiller.capacity_validation_points to a dictionary with the keys being a list of two values - [chilled_water_supply_temperature, condenser_temperature], and values being the capacity_validation_point itself. Sorting the validation points will allow the calculations that follow to be much simpler. Start by creating a dict capacity_validation_pts_dict: `capacity_validation_pts_dict = {}`
- look at each value in chiller.capacity_validation_points: `for capacity_validation_point in chiller.capacity_validation_points:`
- add the point to capacity_validation_pts_dict: `capacity_validation_pts_dict[[capacity_validation_point.chilled_water_supply_temperature, capacity_validation_point.condenser_temperature]] = capacity_validation_point`

claperle marked this conversation as resolved.
Show resolved Hide resolved
- do the same conversion with the chiller.power_validation_points and storing the results in power_validation_pts_dict. However in this case, the values are a list of points because we expect different loads at each point: `power_validation_pts_dict = {}`
- look at each value in chiller.power_validation_points: `for power_validation_point in chiller.power_validation_points:`
- set the default to a blank list: `power_validation_pts_dict.setdefault([[power_validation_point.chilled_water_supply_temperature, power_validation_point.condenser_temperature]], [])`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- append the power validation point to the list at this given: `power_validation_pts_dict[[power_validation_point.chilled_water_supply_temperature, power_validation_point.condenser_temperature]].append(power_validation_point)`
claperle marked this conversation as resolved.
Show resolved Hide resolved

- create a dictionary of the expected capacities for use in the power validation check: `given_capacities = {}`
- the following lines or logic do the capacity validation check:
- loop through the expected_cwt_temps: `for cwt in expected_cwt_temps:`
- loop through each expected_ect_temps: `for ect in expected_ect_temps:`
- look for the capacity validation point in capacity_validation_pts_dict: `if capacity_validation_pts_dict[[cwt, ect]]:`
- calculate the expected capacity by multiplying the result of the formula by the rated_capacity: `expected_capacity = (cap_f_t_coefficients[0] + cap_f_t_coefficients[1] * cwt + cap_f_t_coefficients[2] * cwt^2 + cap_f_t_coefficients[3] * ect + cap_f_t_coefficients[4] * ect^2 + cap_f_t_coefficients[5] * cwt * ect) * rated_capacity`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This formula looks correct cut recommend upsating ect and cwt nomenclature

Copy link
Collaborator Author

@KarenWGard KarenWGard Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everything has been updated to be ecwt and cwt

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typical abbreviation for chilled water is CHW so I recommend that any variable that pertains to chilled water use CHW and any variable that represents condensing water be CW. Looks like they both use CW regardless of whether they are chilled or condenser water. This is a suggestion - feel free to ignore but I found it was a little confusing with CW for both. Maybe use the nomenclature in 90.1 with CHWT and ECT.

- get the given capacity: `given_capacity = capacity_validation_pts_dict[[cwt, ect]].result`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- if the expected capacity matches the given capacity, add the expected capacity to the expected_capacities dictionary. This should not be an exact match, but with a margin of error (see notes at the bottom for suggested margins of errors): `if expected_capacity == given_capacity: given_capacities[[cwt,ect]] = given_capacity`
- otherwise, if the expected capacity doesn't match the given capacity, then change capacity_validation_status and go to Rule Assertion: `else: capacity_validation_status = "Expected capacity does not match given capacity at CHWT: " + cwt + " & ECT: " + ect; GO TO RULE ASSERTION`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- otherwise this value doesn't exist, set the capacity_validation_status and go to rule assertion: `else: capacity_validation_status = "Required capacity validation point is missing. Expected CHWT: " + cwt + ", expected ECT: " + ect; GO TO RULE ASSERTION`

- the following lines or logic do the power validation check:
- loop through the expected_cwt_temps: `for cwt in expected_cwt_temps:`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on above on line 34 I think this is supposed to be for chwt in expected_chwt_temps"

Copy link
Collaborator Author

@KarenWGard KarenWGard Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everything has been updated to be ecwt and cwt

Copy link
Collaborator

@claperle claperle Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typical abbreviation for chilled water is CHW so I recommend that any variable that pertains to chilled water use CHW and any variable that represents condensing water be CW. Looks like they both use CW regardless of whether they are chilled or condenser water. This is a suggestion - feel free to ignore but I found it was a little confusing with CW for both. Maybe use the nomenclature in 90.1 with CHWT and ECT.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I see what you're looking for now. Using CHWT and ECT now

- loop through each expected_ect_temps: `for ect in expected_ect_temps:`
- look for the power validation points in power_validation_pts_dict: `if power_validation_pts_dict[[cwt, ect]]:`
- we are expecting to see multiple validation points aligning with the expected_validation_plr. Create a list of part load ratios that are given. Later we'll compare this with the expected list to make sure that all points are given: `given_plrs = []`
- look at each power validation point in the list: `for power_validation_point in power_validation_pts_dict[[cwt, ect]]:`
- get the load: `load = power_validation_point.load`
Copy link
Collaborator

@claperle claperle Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may just be tired but I am not following how this load is getting retrieved from this. I don't think you are looping through power_validation_point objects at this point. Looks like you are looping through a list of power validation point results. I am thinking the loads also need to be included in the dictionary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load is a property of ChillerPowerValidationPoint

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay but I do not see how you are getting to this with the current logic. How are you able to reference the power_validation_point, you are not looping through them anywehre and there can be multiple associated with the chiller. power_validation_point is an object so I am not following how you are able to reference it here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right - I made changes also on line 50, so that power_validation_pts_dict[dict_key] references power_validation_points, not a list of power_validation_point results. Then we can get both the result and load on lines 75 and 76

- get the given power: `given_power = power_validation_point.result`
claperle marked this conversation as resolved.
Show resolved Hide resolved
- calculate the PLR by dividing the load by the given capacity at these operating conditions: `plr = load / given_capacities[[cwt, ect]]`
- check whether the plr is one of the plrs that we need to check: `if plr in expected_validation_plr:`
Copy link
Collaborator

@claperle claperle Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we allow some deviation? I can imagine the plr not being exactly 0.25 for example.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. I added a note to the dev team to accept a deviation of 0.01 - which is based on the understanding that if a number is given to two decimal places, there is a deviation of 0.01 allowed (PLRs are 0.25, etc)

- add the plr to the list of plrs provided: `given_plrs.append(plr)`
- calculate eir_plr using the coefficients given: `eir_plr = plr_coefficients[0] + plr_coefficients[1] * plr + plr_coefficients[2] * plr^2`
- calculate the eir_ft using the coefficients given: `eir_ft = eir_f_t_coefficients[0] + eir_f_t_coefficients[1] * cwt + eir_f_t_coefficients[2] * cwt^2 + eir_f_t_coefficients[3] * ect + eir_f_t_coefficients[4] * ect^2 + eir_f_t_coefficients[5] * cwt * ect
claperle marked this conversation as resolved.
Show resolved Hide resolved
- calculate the expected power using the formula:
- Chiller operating power = Rated Capacity × CAP-f-T × EIR-f-T × EIR-f-PLR × Chiller Input Power at Rated Conditions/Chiller Capacity at Rated Conditions
claperle marked this conversation as resolved.
Show resolved Hide resolved
- in this case, the given capacity under these operating conditions is the rated capacity * cap_ft, so the modified formula is:
- Chiller operating power = given_capacity[[cwt, ect]] × EIR-f-T × EIR-f-PLR × Chiller Input Power at Rated Conditions/Chiller Capacity at Rated Conditions
claperle marked this conversation as resolved.
Show resolved Hide resolved

- `expected_power = given_capacity[[cwt, ect]] * eir_ft * eir_plr * rated_power/rated_capacity`
- check whether the expected power and the given power are equal. This should not be an exact match, but with a margin of error (see notes at the bottom for suggested margins of errors). If the expected power and given power do not match, update power_validation_status and GO TO RULE ASSERTION: `if expected_power != given_power: power_validation_status = "Expected power does not match given power at CHWT: " + cwt + ", ECT: " + ect + " & " + plr".; GO TO RULE ASSERTION`
- make sure that all of the expected plrs are covered by
claperle marked this conversation as resolved.
Show resolved Hide resolved

**Rule Assertion:**
- Case 1: both power_validation_status and capacity_validation_status are PASS: PASS: `if power_validation_status == capacity_validation_status == "PASS"; PASS`
- Case 2: capacity validation status is not PASS, return fail and raise warning with the capacity_validation_status as an additional message: `if capacity_validation_status != "PASS": FAIL; raise_warning: capacity_validation_status`
- Case 3: power validation status is not PASS, return fail and raise warning with the power_validation_status as an additional message: `if power_validation_status != "PASS": FAIL; raise_warning: power_validation_status`


**Notes:**
1. on precision - my understanding is that the general rule for the RCT is that if a value is given in the standard and has four decimal places, the precision is to the nearest .0001. Minimum efficiency for chillers has values ranging between 0.5495 IPLV.IP and 0.7903 FL, which means that given the precision for chillers is about ~ 0.01% (1-). Suggest that for the capacity calculations, this precision is used, but for the power calculation, the square root of 0.01% (0.1%) is used, as this calculation is the result of two separate calculations multiplied together


**[Back](../_toc.md)**


Loading