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, leading_tray 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