149 lines
6.8 KiB
Python
149 lines
6.8 KiB
Python
from math import ceil, floor
|
|
|
|
from helix.calculators.bom_helper import add_parts_to_list, apply_fudge_factors, \
|
|
get_panel_type_counts
|
|
from helix.calculators.subarray_helper import extract_subarray
|
|
from helix.constants.module_type import ModuleType
|
|
from helix.constants.panel_type import PanelType
|
|
from helix.constants.parts import link_tray, cross_tray, ballast, cross_tray_1_1
|
|
from helix.constants.system_type import SystemType
|
|
|
|
|
|
class MechanicalBomCalculator(object):
|
|
def __init__(self, values, panels, subarrays):
|
|
self.values = values
|
|
self.panels = panels
|
|
self.subarrays = subarrays
|
|
|
|
def mechanical_bom(self):
|
|
module_type = self.values.module_type()
|
|
system_type = self.values.system_type()
|
|
system_parts = system_type.parts(module_type)
|
|
combined_parts_list = {}
|
|
|
|
for subarray in self.subarrays:
|
|
panels = extract_subarray(self.panels, subarray.subarray_number)
|
|
|
|
ballast_count = sum(panel.ballast for panel in panels)
|
|
cross_count = sum(panel.cross_tray for panel in panels)
|
|
assigned_seismic_anchors_count = sum(panel.seismic_anchors for panel in panels)
|
|
required_seismic_anchors_count = subarray.required_seismic_anchors
|
|
seismic_anchors_count = max(assigned_seismic_anchors_count, required_seismic_anchors_count)
|
|
required_wind_anchors_count = sum(panel.wind_anchors for panel in panels)
|
|
anchor_count = required_wind_anchors_count + seismic_anchors_count
|
|
panel_type_counts = get_panel_type_counts(panels)
|
|
|
|
subarray_parts_list = {}
|
|
|
|
for index, panel_type_parts in enumerate(system_parts.parts_per_panel_type()):
|
|
add_parts_to_list(subarray_parts_list, panel_type_parts, panel_type_counts[PanelType.from_index(index)])
|
|
|
|
add_parts_to_list(subarray_parts_list, self.values.anchor_type().parts().parts, anchor_count)
|
|
|
|
link_count = self.link_count(panel_type_counts, panels, subarray)
|
|
|
|
add_parts_to_list(subarray_parts_list, {link_tray: 1}, link_count)
|
|
cross_tray_parts = cross_tray if self.values.module_type() == ModuleType.Cell96 else cross_tray_1_1
|
|
add_parts_to_list(subarray_parts_list, {cross_tray_parts: 1}, cross_count)
|
|
add_parts_to_list(subarray_parts_list, {ballast: 1}, ballast_count)
|
|
|
|
add_parts_to_list(subarray_parts_list, system_parts.row_parts(module_type), subarray.row_count)
|
|
add_parts_to_list(subarray_parts_list, system_parts.column_parts(module_type), subarray.column_count)
|
|
|
|
add_parts_to_list(subarray_parts_list, system_parts.sub_array_parts, 1)
|
|
|
|
apply_fudge_factors(subarray_parts_list, system_parts.fudge_factors(not subarray.row_counted_geometrically))
|
|
|
|
dependent_parts_list = {}
|
|
for part, quantity in subarray_parts_list.items():
|
|
dependent_parts = system_parts.dependent_parts(module_type).get(part)
|
|
if dependent_parts:
|
|
add_parts_to_list(dependent_parts_list, dependent_parts, quantity)
|
|
|
|
subarray_parts_list.update(dependent_parts_list)
|
|
add_parts_to_list(combined_parts_list, subarray_parts_list)
|
|
|
|
return combined_parts_list
|
|
|
|
def link_count(self, panel_type_counts, panels, subarray):
|
|
if self.values.system_type() == SystemType.dualTilt:
|
|
|
|
# check if info about position of panels is available
|
|
coordinates_available = all(p.coordinate for p in panels) \
|
|
and not all(p.coordinate.x == 0 and p.coordinate.y == 0 for p in panels)
|
|
|
|
if coordinates_available:
|
|
# initially every C, NS, EW panels has 2 link trays attached
|
|
panel_types = [PanelType.Corner, PanelType.NorthSouth, PanelType.EastWest]
|
|
layout = dict([((p.coordinate.x, p.coordinate.y), 2) for p in panels if p.panel_type in panel_types])
|
|
|
|
row_count = ceil(subarray.row_count)
|
|
column_count = ceil(subarray.column_count)
|
|
|
|
# reduce number of link trays between every two vertically adjoining panels
|
|
for y in range(row_count):
|
|
for x in range(column_count):
|
|
|
|
if (x, y) not in layout:
|
|
continue
|
|
|
|
if (x, y + 1) in layout:
|
|
layout[(x, y + 1)] = 1
|
|
|
|
# count link trays located on perimeter
|
|
link_count = sum([layout[p] for p in layout])
|
|
|
|
# subtract places reserved for leading trays
|
|
link_count -= subarray.row_count + 1
|
|
|
|
# add link trays for panels of type Middle
|
|
link_count += sum([1 for panel in panels if panel.link_tray != 0 and panel.panel_type == PanelType.Middle])
|
|
|
|
return max(link_count, 0)
|
|
else:
|
|
total_possible_link_trays = len(panels) + subarray.column_count
|
|
link_count = total_possible_link_trays
|
|
|
|
for panel in panels:
|
|
if panel.link_tray == 0 and panel.panel_type == PanelType.Middle:
|
|
link_count -= 1
|
|
|
|
link_count -= floor(subarray.row_count)
|
|
|
|
return link_count
|
|
else:
|
|
return sum([self.compute_link_count_single_tilt(panel_type, panel_type_counts, panels) for panel_type in PanelType.all()])
|
|
|
|
def compute_link_count_single_tilt(self, panel_type, panel_type_counts, panels):
|
|
if panel_type == PanelType.Corner:
|
|
return 0
|
|
elif panel_type == PanelType.NorthSouth:
|
|
return 0
|
|
elif panel_type == PanelType.EastWest:
|
|
return panel_type_counts[panel_type] * 2
|
|
elif panel_type == PanelType.Middle:
|
|
return self.get_panel_type_middle_link_trays_single_tilt(panels)
|
|
else:
|
|
return 0
|
|
|
|
def get_panel_type_middle_link_trays_single_tilt(self, panels):
|
|
wind_zones = self.values.system_type().system_constants().wind_zones
|
|
|
|
middle_panels_per_wind_zone = [0 for _ in wind_zones]
|
|
middle_link_trays_per_wind_zone = [0 for _ in wind_zones]
|
|
|
|
for panel in panels:
|
|
if panel.panel_type != PanelType.Middle:
|
|
continue
|
|
|
|
wind_zone = panel.wind_zone
|
|
middle_panels_per_wind_zone[wind_zone] += 1
|
|
middle_link_trays_per_wind_zone[wind_zone] += panel.link_tray # use calculated number of link trays to see if it is non-zero
|
|
|
|
total_link_trays_required = 0
|
|
for wind_zone, panel_count in enumerate(middle_panels_per_wind_zone):
|
|
if middle_link_trays_per_wind_zone[wind_zone] > 0: # if any middle panels in this wind zone need link trays
|
|
total_link_trays_required += ceil(panel_count * 1.05)
|
|
return total_link_trays_required
|
|
|