170 lines
8.0 KiB
Python
170 lines
8.0 KiB
Python
from math import ceil, floor
|
|
import copy
|
|
from helix.Repositories.graph_repository import GraphRepository
|
|
from helix.calculators.ballast_calculator import BallastCalculator
|
|
from helix.calculators.bom_calculator import BomCalculator
|
|
from helix.calculators.coordinates_calculator import CoordinatesCalculator
|
|
from helix.calculators.pressure_coefficient_calculator import PressureCoefficientCalculator
|
|
from helix.calculators.seismic_calculator import SeismicCalculator
|
|
from helix.calculators.subarray_helper import get_subarray_sizes_and_rows, extract_subarray
|
|
from helix.calculators.summary_values_calculator import SummaryValuesCalculator
|
|
from helix.calculators.wind_pressure_calculator import WindPressureCalculator
|
|
|
|
|
|
class Calculator(object):
|
|
def __init__(self, user_values, calculate_panel_data=True):
|
|
self.values = user_values
|
|
self._q_z = None
|
|
self._c_p_matrix = None
|
|
self._L_B = None
|
|
self._K_z = None
|
|
self.subarrays = None
|
|
self.buildings = self.values.buildings_polygons()
|
|
self.buildings_for_drawing = []
|
|
|
|
self.panels = self.values.csv()
|
|
if calculate_panel_data and self.panels is not None:
|
|
for idx, panel in enumerate(self.panels):
|
|
panel.id = idx + 1
|
|
self.panels.sort(key=lambda x: x.subarray)
|
|
self.subarrays = get_subarray_sizes_and_rows(self.panels)
|
|
|
|
self.__compute_ballast()
|
|
_,_,self.buildings_for_drawing = self.__transform_coordinates()
|
|
self.graph_repository = GraphRepository(self.panels, self.subarrays, self.values.system_type())
|
|
if self.values.user_override_seismic_anchors():
|
|
user_provided_panels = self.values.get_user_provided_seismic_anchors()
|
|
for user_panel in user_provided_panels:
|
|
panel = [panel for panel in self.panels if panel.id == user_panel.id][0]
|
|
panel.seismic_anchors = user_panel.seismic_anchors
|
|
self.__compute_seismic_anchors(self.panels) # Update subarrays to include required seismic anchors
|
|
self.__update_ballast(self.panels)
|
|
else:
|
|
# Update subarrays *and panels* to include required seismic anchors
|
|
self.panels = self.__compute_seismic_anchors(self.panels)
|
|
|
|
def k_z(self):
|
|
if self._K_z is None:
|
|
self._K_z = WindPressureCalculator(self.values).K_z()
|
|
return self._K_z
|
|
|
|
def L_B(self):
|
|
if self._L_B is None:
|
|
self._L_B = PressureCoefficientCalculator(self.values).L_B()
|
|
return self._L_B
|
|
|
|
def summary_table(self):
|
|
return BallastCalculator(self.values).summary_table(self.__c_p_matrix(), self.q_z())
|
|
|
|
def minimum_array_sizes(self):
|
|
return PressureCoefficientCalculator(self.values).minimum_array_size(self.L_B())
|
|
|
|
# Used in the array summary page - is the table of weight, psf, anchors, ballast, etc. for the entire system
|
|
def summary_values(self):
|
|
seismic_anchors = self.subarray_summary()
|
|
ballast_calculator = BallastCalculator(self.values)
|
|
seismic_interval = SeismicCalculator(self.values, self.graph_repository).seismic_anchor_interval()
|
|
|
|
return SummaryValuesCalculator(self.values).summary_values(self.panels, seismic_anchors, self.__c_p_matrix(),
|
|
self.q_z(), seismic_interval, ballast_calculator)
|
|
|
|
def documentation_summary_values(self):
|
|
seismic_anchors = self.subarray_summary()
|
|
ballast_calculator = BallastCalculator(self.values)
|
|
seismic_interval = SeismicCalculator(self.values, self.graph_repository).seismic_anchor_interval()
|
|
|
|
return SummaryValuesCalculator(self.values).documentation_summary_values(self.panels, seismic_anchors, self.__c_p_matrix(),
|
|
self.q_z(), seismic_interval, ballast_calculator)
|
|
|
|
# used in the array visualization - is parsed into json and displayed using the fancy canvas
|
|
def get_computed_csv_columns(self):
|
|
return BallastCalculator(self.values).show_presented_link_trays(self.panels)
|
|
|
|
def compute_bom(self):
|
|
required_seismic_anchors = self.subarray_summary()
|
|
return BomCalculator(self.values, self.panels, required_seismic_anchors, self.graph_repository).compute_bom()
|
|
|
|
def documentation_bom(self):
|
|
required_seismic_anchors = self.subarray_summary()
|
|
return BomCalculator(self.values, self.panels, required_seismic_anchors, self.graph_repository).documentation_bom()
|
|
|
|
# used in the array summary page - is part of the fancy scrolling table of summing up each subarray
|
|
def subarray_summary(self):
|
|
summary_values_calculator = SummaryValuesCalculator(self.values)
|
|
for subarray in self.subarrays:
|
|
panels_for_subarray = extract_subarray(self.panels, subarray.subarray_number)
|
|
weight, _ = summary_values_calculator.system_weight_and_pressure(panels_for_subarray)
|
|
subarray.weight = weight
|
|
|
|
return self.subarrays
|
|
|
|
def q_z(self):
|
|
if self._q_z is None:
|
|
self._q_z = WindPressureCalculator(self.values).q_z(self.k_z())
|
|
return self._q_z
|
|
|
|
def __c_p_matrix(self):
|
|
if self._c_p_matrix is None:
|
|
self._c_p_matrix = PressureCoefficientCalculator(self.values).c_p_matrix(self.L_B())
|
|
return self._c_p_matrix
|
|
|
|
def __compute_seismic_anchors(self, panels):
|
|
panels = copy.deepcopy(panels)
|
|
seismic_calculator = SeismicCalculator(self.values, self.graph_repository)
|
|
for subarray in self.subarrays:
|
|
if subarray.required_seismic_anchors is None:
|
|
subarray.required_seismic_anchors = 0
|
|
panels = self.__seismic_anchors_for_subarray(panels, subarray, seismic_calculator)
|
|
return panels
|
|
|
|
def __seismic_anchors_for_subarray(self, panels, subarray, seismic_calculator):
|
|
# do first estimation to obtain upper bound
|
|
required_seismic = seismic_calculator.required_force_seismic_anchors(subarray.subarray_number, panels)
|
|
|
|
test_value = required_seismic
|
|
tried_acceptable_values = []
|
|
|
|
def assign_seismic_anchors(count):
|
|
subarray.required_seismic_anchors = count
|
|
seismic_calculator.assign_seismic_anchors(subarray, panels)
|
|
self.__update_ballast(panels)
|
|
|
|
assigned = sum([panel.seismic_anchors for panel in panels if panel.seismic_anchors is not None])
|
|
|
|
return assigned
|
|
|
|
step = max(1, test_value // 2)
|
|
|
|
while True:
|
|
seismic_anchors_assigned = assign_seismic_anchors(test_value)
|
|
|
|
provided_force = seismic_calculator.compute_provided_lateral_capacity(subarray.subarray_number, panels)
|
|
required_seismic_force = seismic_calculator.required_force_seismic_demand(subarray.subarray_number, panels)
|
|
|
|
if seismic_anchors_assigned == 0:
|
|
# anchors were not assigned propably because self.graph_repository.subarray_graph(subarray.subarray_number) is empty
|
|
# which may be because of test construction
|
|
return panels
|
|
|
|
if provided_force < required_seismic_force:
|
|
test_value += step
|
|
else:
|
|
if (test_value in tried_acceptable_values) or (required_seismic_force == 0):
|
|
return panels
|
|
|
|
tried_acceptable_values.append(test_value)
|
|
test_value = max(0, test_value - step)
|
|
|
|
step = max(1, step // 2)
|
|
|
|
def __transform_coordinates(self):
|
|
return CoordinatesCalculator(self.values).transform_coordinates(self.panels, self.subarrays, self.buildings)
|
|
|
|
def __compute_ballast(self):
|
|
ballast_calculator = BallastCalculator(self.values)
|
|
changed_panels = ballast_calculator.ballast_and_trays_matrix(self.__c_p_matrix(), self.q_z(), self.panels)
|
|
self.panels = [panel.merge(changed_panels[idx]) for idx, panel in enumerate(self.panels)]
|
|
|
|
def __update_ballast(self, panels):
|
|
BallastCalculator(self.values).update_ballast(self.__c_p_matrix(), self.q_z(), panels)
|