Skip to content

Commit ec2ce40

Browse files
committed
Improve calculations for # series/parallel: 1) Fix inconsistent use of SAM and EnergyPlus defaults for default nominal cell voltage and 2) Fix use of unrounded vs rounded nominal voltage.
1 parent a4f7dfa commit ec2ce40

3 files changed

Lines changed: 41 additions & 47 deletions

File tree

HPXMLtoOpenStudio/measure.xml

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<schema_version>3.0</schema_version>
44
<name>hpxm_lto_openstudio</name>
55
<uid>b1543b30-9465-45ff-ba04-1d1f85e763bc</uid>
6-
<version_id>06ef5ca6-c15f-4393-ad64-c23976151d77</version_id>
7-
<version_modified>20211108T153003Z</version_modified>
6+
<version_id>d35bf77b-1694-4718-bb9f-28f48aea0cef</version_id>
7+
<version_modified>20211108T183458Z</version_modified>
88
<xml_checksum>D8922A73</xml_checksum>
99
<class_name>HPXMLtoOpenStudio</class_name>
1010
<display_name>HPXML to OpenStudio Translator</display_name>
@@ -16,31 +16,22 @@
1616
<display_name>HPXML File Path</display_name>
1717
<description>Absolute/relative path of the HPXML file.</description>
1818
<type>String</type>
19-
<units></units>
2019
<required>true</required>
2120
<model_dependent>false</model_dependent>
22-
<default_value></default_value>
23-
<min_value></min_value>
24-
<max_value></max_value>
2521
</argument>
2622
<argument>
2723
<name>output_dir</name>
2824
<display_name>Directory for Output Files</display_name>
2925
<description>Absolute/relative path for the output files directory.</description>
3026
<type>String</type>
31-
<units></units>
3227
<required>true</required>
3328
<model_dependent>false</model_dependent>
34-
<default_value></default_value>
35-
<min_value></min_value>
36-
<max_value></max_value>
3729
</argument>
3830
<argument>
3931
<name>debug</name>
4032
<display_name>Debug Mode?</display_name>
4133
<description>If true: 1) Writes in.osm file, 2) Generates additional log output, and 3) Creates all EnergyPlus output files.</description>
4234
<type>Boolean</type>
43-
<units></units>
4435
<required>false</required>
4536
<model_dependent>false</model_dependent>
4637
<default_value>false</default_value>
@@ -54,15 +45,12 @@
5445
<display_name>false</display_name>
5546
</choice>
5647
</choices>
57-
<min_value></min_value>
58-
<max_value></max_value>
5948
</argument>
6049
<argument>
6150
<name>add_component_loads</name>
6251
<display_name>Add component loads?</display_name>
6352
<description>If true, adds the calculation of heating/cooling component loads (not enabled by default for faster performance).</description>
6453
<type>Boolean</type>
65-
<units></units>
6654
<required>false</required>
6755
<model_dependent>false</model_dependent>
6856
<default_value>false</default_value>
@@ -76,15 +64,12 @@
7664
<display_name>false</display_name>
7765
</choice>
7866
</choices>
79-
<min_value></min_value>
80-
<max_value></max_value>
8167
</argument>
8268
<argument>
8369
<name>skip_validation</name>
8470
<display_name>Skip Validation?</display_name>
8571
<description>If true, bypasses HPXML input validation for faster performance. WARNING: This should only be used if the supplied HPXML file has already been validated against the Schema &amp; Schematron documents.</description>
8672
<type>Boolean</type>
87-
<units></units>
8873
<required>false</required>
8974
<model_dependent>false</model_dependent>
9075
<default_value>false</default_value>
@@ -98,20 +83,14 @@
9883
<display_name>false</display_name>
9984
</choice>
10085
</choices>
101-
<min_value></min_value>
102-
<max_value></max_value>
10386
</argument>
10487
<argument>
10588
<name>building_id</name>
10689
<display_name>BuildingID</display_name>
10790
<description>The ID of the HPXML Building. Only required if there are multiple Building elements in the HPXML file.</description>
10891
<type>String</type>
109-
<units></units>
11092
<required>false</required>
11193
<model_dependent>false</model_dependent>
112-
<default_value></default_value>
113-
<min_value></min_value>
114-
<max_value></max_value>
11594
</argument>
11695
</arguments>
11796
<outputs />
@@ -493,13 +472,13 @@
493472
<filename>test_battery.rb</filename>
494473
<filetype>rb</filetype>
495474
<usage_type>test</usage_type>
496-
<checksum>2D76F76D</checksum>
475+
<checksum>7C0EFC71</checksum>
497476
</file>
498477
<file>
499478
<filename>battery.rb</filename>
500479
<filetype>rb</filetype>
501480
<usage_type>resource</usage_type>
502-
<checksum>DA3AC0A2</checksum>
481+
<checksum>8AA18CFA</checksum>
503482
</file>
504483
</files>
505484
</measure>

HPXMLtoOpenStudio/resources/battery.rb

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ class Battery
44
def self.apply(runner, model, battery)
55
obj_name = battery.id
66

7-
power = battery.rated_power_output # W
8-
voltage = battery.nominal_voltage # V
7+
rated_power_output = battery.rated_power_output # W
8+
nominal_voltage = battery.nominal_voltage # V
99
if not battery.nominal_capacity_kwh.nil?
10-
capacity = battery.nominal_capacity_kwh # kWh
10+
nominal_capacity_kwh = battery.nominal_capacity_kwh # kWh
1111
else
12-
capacity = get_kWh_from_Ah(battery.nominal_capacity_ah, voltage) # kWh
12+
nominal_capacity_kwh = get_kWh_from_Ah(battery.nominal_capacity_ah, nominal_voltage) # kWh
1313
end
1414

15-
return if power <= 0 || capacity <= 0 || voltage <= 0
15+
return if rated_power_output <= 0 || nominal_capacity_kwh <= 0 || nominal_voltage <= 0
1616

1717
is_outside = (battery.location == HPXML::LocationOutside)
1818
if not is_outside
@@ -21,11 +21,13 @@ def self.apply(runner, model, battery)
2121
frac_sens = 0.0
2222
end
2323

24-
# The following calculations are from Rohit C.
25-
number_of_cells_in_series = Integer((voltage / 3.6).round)
26-
number_of_strings_in_parallel = Integer(((capacity * 1000.0) / (voltage * 3.2)).round)
27-
battery_mass = (capacity / 10.0) * 99.0 # kg
28-
battery_surface_area = 0.306 * (capacity**(2.0 / 3.0)) # m^2
24+
default_nominal_cell_voltage = 3.342 # V, EnergyPlus default
25+
default_cell_capacity = 3.2 # Ah, EnergyPlus default
26+
27+
number_of_cells_in_series = Integer((nominal_voltage / default_nominal_cell_voltage).round)
28+
number_of_strings_in_parallel = Integer(((nominal_capacity_kwh * 1000.0) / ((default_nominal_cell_voltage * number_of_cells_in_series) * default_cell_capacity)).round)
29+
battery_mass = (nominal_capacity_kwh / 10.0) * 99.0 # kg
30+
battery_surface_area = 0.306 * (nominal_capacity_kwh**(2.0 / 3.0)) # m^2
2931

3032
minimum_storage_state_of_charge_fraction = 0.15 # from SAM
3133
maximum_storage_state_of_charge_fraction = 0.95 # from SAM
@@ -45,6 +47,9 @@ def self.apply(runner, model, battery)
4547
elcs.setInitialFractionalStateofCharge(initial_fractional_state_of_charge)
4648
elcs.setBatteryMass(battery_mass)
4749
elcs.setBatterySurfaceArea(battery_surface_area)
50+
elcs.setDefaultNominalCellVoltage(default_nominal_cell_voltage)
51+
elcs.setCellVoltageatEndofNominalZone(default_nominal_cell_voltage)
52+
elcs.setFullyChargedCellCapacity(default_cell_capacity)
4853

4954
model.getElectricLoadCenterDistributions.each do |elcd|
5055
next unless elcd.inverter.is_initialized
@@ -55,8 +60,8 @@ def self.apply(runner, model, battery)
5560
elcd.setStorageOperationScheme('TrackFacilityElectricDemandStoreExcessOnSite')
5661
elcd.setElectricalStorage(elcs)
5762
runner.registerWarning("Due to an OpenStudio bug, the battery's rated power output will not be honored; the simulation will proceed without a maximum charge/discharge limit.")
58-
elcd.setDesignStorageControlDischargePower(power)
59-
elcd.setDesignStorageControlChargePower(power)
63+
elcd.setDesignStorageControlDischargePower(rated_power_output)
64+
elcd.setDesignStorageControlChargePower(rated_power_output)
6065
end
6166
end
6267

@@ -68,11 +73,11 @@ def self.get_battery_default_values()
6873
nominal_voltage: 50.0 }
6974
end
7075

71-
def self.get_Ah_from_kWh(capacity, voltage)
72-
return capacity * 1000.0 / voltage
76+
def self.get_Ah_from_kWh(nominal_capacity_kwh, nominal_voltage)
77+
return nominal_capacity_kwh * 1000.0 / nominal_voltage
7378
end
7479

75-
def self.get_kWh_from_Ah(capacity, voltage)
76-
return capacity * voltage / 1000.0
80+
def self.get_kWh_from_Ah(nominal_capacity_ah, nominal_voltage)
81+
return nominal_capacity_ah * nominal_voltage / 1000.0
7782
end
7883
end

HPXMLtoOpenStudio/tests/test_battery.rb

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ def get_battery(model, name)
2020
end
2121
end
2222

23+
def calc_nom_capacity(battery)
24+
return (battery.numberofCellsinSeries * battery.numberofStringsinParallel *
25+
battery.cellVoltageatEndofNominalZone * battery.fullyChargedCellCapacity)
26+
end
27+
2328
def test_battery_default
2429
args_hash = {}
2530
args_hash['hpxml_path'] = File.absolute_path(File.join(sample_files_dir, 'base-misc-defaults.xml'))
@@ -32,11 +37,12 @@ def test_battery_default
3237
assert(!battery.thermalZone.is_initialized)
3338
assert_equal(0, battery.radiativeFraction)
3439
assert_equal(HPXML::BatteryLifetimeModelNone, battery.lifetimeModel)
35-
assert_in_epsilon(14, battery.numberofCellsinSeries, 0.01)
36-
assert_in_epsilon(63, battery.numberofStringsinParallel, 0.01)
40+
assert_in_epsilon(15, battery.numberofCellsinSeries, 0.01)
41+
assert_in_epsilon(62, battery.numberofStringsinParallel, 0.01)
3742
assert_in_epsilon(0.5, battery.initialFractionalStateofCharge, 0.01)
3843
assert_in_epsilon(99.0, battery.batteryMass, 0.01)
3944
assert_in_epsilon(1.42, battery.batterySurfaceArea, 0.01)
45+
assert_in_epsilon(10000, calc_nom_capacity(battery), 0.01)
4046

4147
elcds = model.getElectricLoadCenterDistributions
4248
assert_equal(1, elcds.size)
@@ -78,11 +84,12 @@ def test_pv_battery_outside
7884
assert(!battery.thermalZone.is_initialized)
7985
assert_equal(0, battery.radiativeFraction)
8086
assert_equal(HPXML::BatteryLifetimeModelNone, battery.lifetimeModel)
81-
assert_in_epsilon(14, battery.numberofCellsinSeries, 0.01)
87+
assert_in_epsilon(15, battery.numberofCellsinSeries, 0.01)
8288
assert_in_epsilon(125, battery.numberofStringsinParallel, 0.01)
8389
assert_in_epsilon(0.5, battery.initialFractionalStateofCharge, 0.01)
8490
assert_in_epsilon(198.0, battery.batteryMass, 0.01)
8591
assert_in_epsilon(2.25, battery.batterySurfaceArea, 0.01)
92+
assert_in_epsilon(20000, calc_nom_capacity(battery), 0.01)
8693

8794
elcds = model.getElectricLoadCenterDistributions
8895
assert_equal(1, elcds.size)
@@ -109,11 +116,12 @@ def test_pv_battery_outside_degrades
109116
assert(!battery.thermalZone.is_initialized)
110117
assert_equal(0, battery.radiativeFraction)
111118
assert_equal(HPXML::BatteryLifetimeModelKandlerSmith, battery.lifetimeModel)
112-
assert_in_epsilon(14, battery.numberofCellsinSeries, 0.01)
119+
assert_in_epsilon(15, battery.numberofCellsinSeries, 0.01)
113120
assert_in_epsilon(125, battery.numberofStringsinParallel, 0.01)
114121
assert_in_epsilon(0.5, battery.initialFractionalStateofCharge, 0.01)
115122
assert_in_epsilon(198.0, battery.batteryMass, 0.01)
116123
assert_in_epsilon(2.25, battery.batterySurfaceArea, 0.01)
124+
assert_in_epsilon(20000, calc_nom_capacity(battery), 0.01)
117125

118126
elcds = model.getElectricLoadCenterDistributions
119127
assert_equal(1, elcds.size)
@@ -141,11 +149,12 @@ def test_pv_battery_garage
141149
assert_equal(HPXML::LocationGarage, battery.thermalZone.get.name.to_s)
142150
assert_equal(0.9, battery.radiativeFraction)
143151
assert_equal(HPXML::BatteryLifetimeModelNone, battery.lifetimeModel)
144-
assert_in_epsilon(14, battery.numberofCellsinSeries, 0.01)
152+
assert_in_epsilon(15, battery.numberofCellsinSeries, 0.01)
145153
assert_in_epsilon(125, battery.numberofStringsinParallel, 0.01)
146154
assert_in_epsilon(0.5, battery.initialFractionalStateofCharge, 0.01)
147155
assert_in_epsilon(198.0, battery.batteryMass, 0.01)
148156
assert_in_epsilon(2.25, battery.batterySurfaceArea, 0.01)
157+
assert_in_epsilon(20000, calc_nom_capacity(battery), 0.01)
149158

150159
elcds = model.getElectricLoadCenterDistributions
151160
assert_equal(1, elcds.size)
@@ -172,11 +181,12 @@ def test_pv_battery_ah
172181
assert(!battery.thermalZone.is_initialized)
173182
assert_equal(0, battery.radiativeFraction)
174183
assert_equal(HPXML::BatteryLifetimeModelNone, battery.lifetimeModel)
175-
assert_in_epsilon(14, battery.numberofCellsinSeries, 0.01)
184+
assert_in_epsilon(15, battery.numberofCellsinSeries, 0.01)
176185
assert_in_epsilon(125, battery.numberofStringsinParallel, 0.01)
177186
assert_in_epsilon(0.5, battery.initialFractionalStateofCharge, 0.01)
178187
assert_in_epsilon(198.0, battery.batteryMass, 0.01)
179188
assert_in_epsilon(2.25, battery.batterySurfaceArea, 0.01)
189+
assert_in_epsilon(20000, calc_nom_capacity(battery), 0.01)
180190

181191
elcds = model.getElectricLoadCenterDistributions
182192
assert_equal(1, elcds.size)

0 commit comments

Comments
 (0)