Files
old-krovovi-kalkulator/test/calculators/calculator_test.py
2017-12-19 15:18:35 +01:00

565 lines
24 KiB
Python

import csv
import json
import unittest
import mockredis
from nose.tools import eq_
from numpy import array
from numpy.testing import assert_array_equal
import time
from helix.calculators.calculator import Calculator
from helix.constants.anchor_type import AnchorType
from helix.constants.exposure_category import ExposureCategory
from helix.constants.inverter_type import InverterType
from helix.constants.module_type import ModuleType
from helix.constants.panel_type import PanelType
from helix.constants.system_type import SystemType
from helix.models.coordinate import Coordinate
from helix.models.panel import Panel
from helix.models.sql.inverters import Inverter
from helix.models.sql.power_stations import PowerStation
from helix.models.sql.sites import Site
from helix.models.subarray import Subarray
from helix.store import Store
from helix.user_values import UserValues
from test.test_helpers import assert_array_is_close
class CalculatorTest(unittest.TestCase):
def setUp(self):
self.store = Store(mockredis.mock_redis_client(), "foo")
self.site = Site(
building_height=35,
building_width=450,
building_length=500,
parapet_height=5,
wind_speed=120,
exposure_category='B',
ballast_block_weight=14,
max_psf=10,
system_type=SystemType.dualTilt.value,
module_type=ModuleType.Cell96.value,
spectral_response=1.5,
anchor_type=AnchorType.OMG_PowerGrip_Plus.value,
seismic_importance_factor=1,
)
self.values = UserValues(self.store, self.site)
def test_compute_subarray_summary_dual_tilt(self):
self.site.system_type = SystemType.dualTilt.value
with open('test/fixtures/input_dual_tilt_csv_for_bom.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
expected = [
Subarray(subarray_number=1, origin=Coordinate(0, 0), required_seismic_anchors=436, weight=170698.330000,
start_row=0, size=863, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=130.5, column_count=98)
]
assert_array_is_close(self.subject.subarray_summary(), expected, decimal=0)
def test_compute_subarray_summary_single_tilt(self):
self.site.system_type = SystemType.singleTilt.value
with open('test/fixtures/input_single_tilt_csv_for_bom.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
expected = [
Subarray(subarray_number=1, origin=Coordinate(0, 0), required_seismic_anchors=11, weight=23804, start_row=0,
size=192, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=18, column_count=20),
Subarray(subarray_number=2, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=13271,
start_row=192, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=4, column_count=24),
Subarray(subarray_number=3, origin=Coordinate(0, 0), required_seismic_anchors=110, weight=40328,
start_row=288, size=312, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=25, column_count=29),
Subarray(subarray_number=4, origin=Coordinate(0, 0), required_seismic_anchors=71, weight=23324,
start_row=600, size=168, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=22, column_count=13),
Subarray(subarray_number=5, origin=Coordinate(0, 0), required_seismic_anchors=74, weight=35170,
start_row=768, size=234, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=26, column_count=14),
Subarray(subarray_number=6, origin=Coordinate(0, 0), required_seismic_anchors=1, weight=11461,
start_row=1002, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=10, column_count=12),
Subarray(subarray_number=7, origin=Coordinate(0, 0), required_seismic_anchors=28, weight=10865,
start_row=1098, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=16),
Subarray(subarray_number=8, origin=Coordinate(0, 0), required_seismic_anchors=12, weight=10780,
start_row=1194, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=15, column_count=17.5),
Subarray(subarray_number=9, origin=Coordinate(0, 0), required_seismic_anchors=0, weight= 10989,
start_row=1290, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=16),
Subarray(subarray_number=10, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=4901,
start_row=1386, size=48, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=6),
Subarray(subarray_number=11, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=10766,
start_row=1434, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=13, column_count=13),
Subarray(subarray_number=12, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=6350,
start_row=1530, size=54, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=8),
Subarray(subarray_number=13, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=4239,
start_row=1584, size=48, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=6, column_count=8)
]
assert_array_is_close(self.subject.subarray_summary(), expected, decimal=0)
def test_does_not_operate_on_panels_if_told_not_to(self):
with open('test/fixtures/input_dual_tilt_coordinates_seismic_anchors.txt', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values, calculate_panel_data=False)
for panel in self.subject.panels:
assert panel.seismic_anchors is None
assert self.subject.subarrays is None
def test_compute_summary_values(self):
self.site.building_width = 200
self.site.building_height = 30
self.site.building_length = 75.5
self.site.wind_speed = 130
self.site.exposure_category = ExposureCategory.C.value
self.site.ballast_block_weight = 13
self.site.parapet_height = 0
self.site.max_psf = 10
self.site.anchor_type = AnchorType.OMG_PowerGrip_Plus.value
self.site.spectral_response = 1.02
self.site.system_type = SystemType.singleTilt.value
with open('test/fixtures/input_single_tilt.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
received_values = self.subject.summary_values()
expected_values = array([
{'label': 'Total System Weight (lbs)', 'value': 2694},
{'label': 'Max PSF', 'value': 7.9},
{'label': 'Avg PSF', 'value': 5.41},
{'label': 'Total Anchors', 'value': 5},
{'label': 'Total Ballast', 'value': 135},
{'label': 'Max Possible System Weight', 'value': 2792.0},
{'label': 'Max System Weight Ballast Block', 'value': 17},
{'label': 'Seismic Anchor Max. Spacing', 'value': 12},
])
assert_array_equal(received_values, expected_values)
def test_compute_documentation_summary_values(self):
self.site.building_width = 200
self.site.building_height = 30
self.site.building_length = 75.5
self.site.wind_speed = 130
self.site.exposure_category = ExposureCategory.C.value
self.site.ballast_block_weight = 13
self.site.parapet_height = 0
self.site.max_psf = 10
self.site.anchor_type = AnchorType.OMG_PowerGrip_Plus.value
self.site.spectral_response = 1.02
self.site.system_type = SystemType.singleTilt.value
with open('test/fixtures/input_single_tilt.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
received_values = self.subject.documentation_summary_values()
expected_values = {
'total_system_weight': 2694,
'max_psf': 7.9,
'ave_psf': 5.41,
'total_anchors': 5,
'total_ballast': 135,
'max_possible_system_weight': 2792.0,
'max_system_weight_ballast_block': 17,
'seismic_anchor_max_spacing': 12
}
eq_(received_values, expected_values)
def test_recomputes_ballast_after_seismic_calculations(self):
self.site.building_width = 200
self.site.building_height = 30
self.site.building_length = 75.5
self.site.wind_speed = 110
self.site.exposure_category = ExposureCategory.C.value
self.site.ballast_block_weight = 13
self.site.parapet_height = 0
self.site.max_psf = 10
self.site.anchor_type = AnchorType.EcoFasten.value
self.site.spectral_response = 1.0
self.site.system_type = SystemType.dualTilt.value
with open('test/fixtures/input_dual_tilt_coordinates.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
required_seismic_anchors = sum(subarray.required_seismic_anchors for subarray in self.subject.subarrays)
eq_(required_seismic_anchors, 2)
self.site.spectral_response = 1.7
self.subject = Calculator(self.values)
required_seismic_anchors = sum(subarray.required_seismic_anchors for subarray in self.subject.subarrays)
eq_(required_seismic_anchors, 6)
def test_uses_user_provided_seismic_anchor_placement_if_available(self):
self.site.building_width = 200
self.site.building_height = 30
self.site.building_length = 75.5
self.site.wind_speed = 110
self.site.exposure_category = ExposureCategory.C.value
self.site.ballast_block_weight = 13
self.site.parapet_height = 0
self.site.max_psf = 10
self.site.anchor_type = AnchorType.EcoFasten.value
self.site.spectral_response = 1.0
self.site.system_type = SystemType.dualTilt.value
with open('test/fixtures/input_dual_tilt_coordinates.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.store.set('user_override_seismic_anchors', json.dumps([
{'panel_id': 1, 'seismic_anchors': 0},
{'panel_id': 2, 'seismic_anchors': 1},
{'panel_id': 3, 'seismic_anchors': 1},
{'panel_id': 4, 'seismic_anchors': 0},
{'panel_id': 5, 'seismic_anchors': 1},
{'panel_id': 6, 'seismic_anchors': 0},
{'panel_id': 7, 'seismic_anchors': 0},
{'panel_id': 8, 'seismic_anchors': 1},
{'panel_id': 9, 'seismic_anchors': 1},
{'panel_id': 10, 'seismic_anchors': 1},
{'panel_id': 11, 'seismic_anchors': 1},
{'panel_id': 12, 'seismic_anchors': 1},
{'panel_id': 13, 'seismic_anchors': 1},
{'panel_id': 14, 'seismic_anchors': 1},
{'panel_id': 15, 'seismic_anchors': 1},
{'panel_id': 16, 'seismic_anchors': 0},
]))
self.subject = Calculator(self.values)
eq_(sum(panel.seismic_anchors for panel in self.subject.panels), 11)
expected = [
Panel(seismic_anchors=0, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=0, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=0, ballast=10),
Panel(seismic_anchors=0, ballast=10),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=1, ballast=0),
Panel(seismic_anchors=0, ballast=0)
]
for idx, received_panel in enumerate(self.subject.panels):
received_seismic = received_panel.seismic_anchors
expected_seismic = expected[idx].seismic_anchors
eq_(received_seismic, expected_seismic,
"Panel at index %d had %d seismic anchors, expected %d" % (
idx, received_seismic, expected_seismic))
received_ballast = received_panel.ballast
expected_ballast = expected[idx].ballast
eq_(received_ballast, expected_ballast,
"Panel at index %d had %d ballast, expected %d" % (
idx, received_ballast, expected_ballast))
eq_(sum(subarray.required_seismic_anchors for subarray in self.subject.subarrays), 2)
def test_get_computed_csv_columns(self):
self.site.system_type = SystemType.dualTilt.value
self.subject = Calculator(self.values)
panels = [
Panel(panel_type=PanelType.Corner, link_tray=3),
Panel(panel_type=PanelType.NorthSouth, link_tray=3),
Panel(panel_type=PanelType.EastWest, link_tray=3),
Panel(panel_type=PanelType.Middle, link_tray=3),
]
self.subject.panels = panels
expected = [
Panel(panel_type=PanelType.Corner, link_tray=3, presented_link_tray=2),
Panel(panel_type=PanelType.NorthSouth, link_tray=3, presented_link_tray=2),
Panel(panel_type=PanelType.EastWest, link_tray=3, presented_link_tray=1),
Panel(panel_type=PanelType.Middle, link_tray=3, presented_link_tray=1),
]
eq_(self.subject.get_computed_csv_columns(), expected)
def test_computes_bom(self):
self.site.building_width = 200
self.site.building_height = 30
self.site.building_length = 75.5
self.site.wind_speed = 125
self.site.exposure_category = ExposureCategory.C.value
self.site.ballast_block_weight = 13
self.site.parapet_height = 0
self.site.max_psf = 10
self.site.anchor_type = AnchorType.OMG_PowerGrip_Plus.value
self.site.spectral_response = 1
self.site.system_type = SystemType.dualTilt.value
with open('test/fixtures/input_dual_tilt_csv_for_bom.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
with open('test/fixtures/expected_dual_tilt_ebom.csv', 'r', newline='') as expected_file:
expected_csv = expected_file.read()
reader = csv.reader(expected_csv.splitlines(), dialect='excel-tab')
expected = array([row for row in reader])
print("EXPECTED")
print(expected)
power_station_1 = PowerStation(
description='1',
quantity=1,
ac_run_length=55,
inverters=[
Inverter(
model=str(InverterType.SMA.MODEL_12KW.value),
strings_per_inverter=2
),
Inverter(
model=str(InverterType.SMA.MODEL_15KW.value),
strings_per_inverter=5
)
]
)
power_station_2 = PowerStation(
description='2',
quantity=3,
ac_run_length=89,
inverters=[
Inverter(
model=str(InverterType.SMA.MODEL_20KW.value),
strings_per_inverter=6
),
Inverter(
model=str(InverterType.SMA.MODEL_24KW.value),
strings_per_inverter=8
),
Inverter(
model=str(InverterType.SMA.MODEL_24KW.value),
strings_per_inverter=8
),
Inverter(
model=str(InverterType.SMA.MODEL_24KW.value),
strings_per_inverter=7
)
]
)
self.site.power_stations = [power_station_1, power_station_2]
print("COMPUTE")
print(self.subject.compute_bom())
assert_array_equal(self.subject.compute_bom(), expected)
def test_documentation_bom(self):
self.site.building_width = 200
self.site.building_height = 30
self.site.building_length = 75.5
self.site.wind_speed = 125
self.site.exposure_category = ExposureCategory.C.value
self.site.ballast_block_weight = 13
self.site.parapet_height = 0
self.site.max_psf = 10
self.site.anchor_type = AnchorType.OMG_PowerGrip_Plus.value
self.site.spectral_response = 1
self.site.power_stations = [
PowerStation(
quantity=1,
ac_run_length=10,
description='Test',
id=30,
inverters=[
Inverter(
model=str(InverterType.SMA.MODEL_12KW.value),
sunshade=True,
dc_switch=True,
strings_per_inverter=4
)
]
)
]
self.site.system_type = SystemType.dualTilt.value
with open('test/fixtures/input_dual_tilt_csv_for_bom.csv', 'r', newline='') as csv_file:
csv_content = csv_file.read()
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
expected = [('521794', 196),
('521795', 196),
('514056', 1000),
('modules', 1726),
('513843', 263),
('anchors', 263),
('518477', 275),
('513833', 670),
('513844', 214),
('ballast', 6777),
('515928', 261),
('517871', 139),
('514057', 1000),
('515063', 4000),
('512200', 3480),
('514265', 179),
('513303', 1),
('512660', 2),
('512661', 2),
('512662', 4),
('512663', 2),
('518331', 2),
('518058', 2),
('104813', 50),
('107551', 50),
('514865', 50),
('106925', 50),
('523921', 1),
('514438', 2),
('514437', 2),
('512910', 1),
('805615', 2),
('521031', 2),
('512575', 1),
('514698', 1),
('513007', 50),
('114961', 50),
('107549', 100),
('107586', 100),
('512021', 9),
('512199', 140),
('512511', 196),
('512510', 196),
('515929', 0),
('514477', 2),
('515059', 2),
('514697', 1),
('513831', 0),
('513836', 0),
('520301', 0),
('520302', 0),
('520303', 0),
('520306', 0),
('513832', 0),
('514435', 0),
('514436', 0),
('514439', 0),
('514440', 0),
('523922', 0),
('523923', 0),
('523924', 0),
('513299', 0),
('513301', 0),
('514478', 0),
('513300', 0),
('513302', 0),
('513304', 0),
('519008', 0),
('518059', 0),
('105317', 0),
('111147', 0),
('107538', 0),
('516045', 0),
('516043', 0),
('513586', 0),
('507985', 0),
('522020', 0),
('521798', 0),
('521797', 0),
('521363', 0),
('517463', 0)]
print("===")
print(self.subject.documentation_bom())
assert_array_equal(sorted(self.subject.documentation_bom()), sorted(expected))
# Performance Tests
def test_performance_with_1000ish_module_site(self):
store = Store(mockredis.mock_redis_client(), "foo")
site = Site()
values = UserValues(store, site)
site.building_width = 200
site.building_height = 30
site.building_length = 75.5
site.wind_speed = 110
site.exposure_category = ExposureCategory.C.value
site.ballast_block_weight = 13
site.parapet_height = 0
site.max_psf = 10
site.anchor_type = AnchorType.EcoFasten.value
site.spectral_response = 1.0
site.system_type = SystemType.dualTilt.value
site.module_type = ModuleType.Cell96.value
with open('test/fixtures/input_dual_tilt_coordinates_seismic_anchors.txt', 'r', newline='') as csv_file:
csv_content = csv_file.read()
site.cad_file = csv_content
run_count = 1
runtimes = []
for _ in range(run_count):
start = time.clock()
Calculator(values) # Do all calculations, including seismic anchor placement
end = time.clock()
runtimes.append(end - start)
print("%d runs took %f time on average" % (run_count, sum(runtimes) / run_count))
def test_performance_with_giant_module_site(self):
store = Store(mockredis.mock_redis_client(), "foo")
site = Site()
values = UserValues(store, site)
site.building_width = 200
site.building_height = 30
site.building_length = 75.5
site.wind_speed = 110
site.exposure_category = ExposureCategory.C.value
site.ballast_block_weight = 13
site.parapet_height = 0
site.max_psf = 10
site.anchor_type = AnchorType.EcoFasten.value
site.spectral_response = 1.0
site.system_type = SystemType.singleTilt.value
site.module_type = ModuleType.Cell96.value
with open('test/fixtures/input_single_tilt_96_cell_giant_site.txt', 'r', newline='') as csv_file:
csv_content = csv_file.read()
site.cad_file = csv_content
run_count = 1
runtimes = []
for _ in range(run_count):
start = time.clock()
Calculator(values) # Do all calculations, including seismic anchor placement
end = time.clock()
runtimes.append(end - start)
import sys
print("%d runs took %f time on average" % (run_count, sum(runtimes) / run_count), file=sys.stderr)