Class: HeatingSystemCalculator::ElementFinderByZones

Inherits:
BaseElementFinder show all
Defined in:
app/services/heating_system_calculator/element_finder_by_zones.rb

Instance Attribute Summary

Attributes inherited from BaseElementFinder

#coverage_percentage, #elements, #error, #target_area

Instance Method Summary collapse

Methods inherited from BaseElementFinder

#consolidate_elements, #find_best_solution

Methods inherited from BaseItemFinder

#get_number_of_poles_from_consolidated_elements, #get_total_from_solution

Constructor Details

#initialize(options) ⇒ ElementFinderByZones

Returns a new instance of ElementFinderByZones.



3
4
5
6
7
8
9
# File 'app/services/heating_system_calculator/element_finder_by_zones.rb', line 3

def initialize(options)
  @element_set = options[:element_set]
  @expansion_joint_spacing = options[:expansion_joint_spacing]
  @skip_expansion_joint = options[:skip_expansion_joint]
  @cable_spacing = options[:cable_spacing]
  @element_type = options[:element_type]
end

Instance Method Details

#find_cable_fitting_solution_using(cables, target_dims) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'app/services/heating_system_calculator/element_finder_by_zones.rb', line 96

def find_cable_fitting_solution_using(cables, target_dims)
  solution = []
  # first do a check on target_dims size to trivially calculate the number of the largest cable size we have rather than the very slow method of using the "mat fit" method to do this. Convention is last cable in element set is the biggest.
  # the closest integral length of cable required to fill the target_dims length by width this is:
  # CL = (W*L + W*CS(0.5708))/CS
  num_of_longest_cables = 0
  biggest_target_dim = (((target_dims.max * target_dims.min) + (target_dims.min * @cable_spacing * 0.5708 / 12.0)) * 144.0 / @cable_spacing).floor.to_f
  biggest_cable_length = cables.last[1].to_f
  num_of_longest_cables = (biggest_target_dim / biggest_cable_length).floor
  biggest_target_dim_remnant = biggest_target_dim - (num_of_longest_cables.to_f * biggest_cable_length)
  if (biggest_target_dim_remnant.to_f / biggest_target_dim.to_f) > 0.25 and cables.length > 1
    # This means we can't cover the zone. Try again with longest cables removed
    cables.pop
    find_cable_fitting_solution_using(cables, target_dims)
  else
    # use a modified version of mat fitter to get the right set of cables by setting 'length' of target_dims to the closest integral length of cable required to fill the target_dims length by width, where we use the remnant after using the trivially calculated num_of_longest_cables
    cable_target_dims = [1, biggest_target_dim_remnant.floor]
    cable_fitter = MatFitter.new(*cable_target_dims)
    result = cable_fitter.fit_mats(cables)
    fitted_cables = result[:items]
    # here we push the trivially calculated number of the last cables into the result for proper image rendering etc.
    num_of_longest_cables.times do |_i|
      fitted_cables.push(cables.last)
    end
    fitted_cables.each do |cable|
      elem = @element_set.find { |e| e['sku'] == cable[2] }
      solution << elem
    end
    solution
  end
end

#find_mat_fitting_solution(target_dims) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/services/heating_system_calculator/element_finder_by_zones.rb', line 82

def find_mat_fitting_solution(target_dims)
  all_mats = @element_set.map { |e| [(e['width']).ceil, (e['length']).ceil, e['sku']] }
  target_dims = [target_dims[0] * 12, target_dims[1] * 12] # convert to inches
  mat_fitter = MatFitter.new(*target_dims)
  result = mat_fitter.fit_mats(all_mats)
  fitted_mats = result[:items]
  solution = []
  fitted_mats.each do |mat|
    elem = @element_set.detect { |e| e['sku'] == mat[2] }
    solution << elem
  end
  solution
end

#fit_elements_into_heated_area_zones(zones, heated_area_sqft) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'app/services/heating_system_calculator/element_finder_by_zones.rb', line 11

def fit_elements_into_heated_area_zones(zones, heated_area_sqft)
  if @skip_expansion_joint != true && @expansion_joint_spacing && (@expansion_joint_spacing < RoomConfigurationConstants::MIN_CONCRETE_EXPANSION_JOINT_SPACING or @expansion_joint_spacing > RoomConfigurationConstants::MAX_CONCRETE_EXPANSION_JOINT_SPACING)
    @error = { error_status: :expansion_joint_spacing_invalid,
               error_message: "Expansion joint spacing is invalid, please choose a value between #{RoomConfigurationConstants::MIN_CONCRETE_EXPANSION_JOINT_SPACING} and #{RoomConfigurationConstants::MAX_CONCRETE_EXPANSION_JOINT_SPACING} feet." }
    return self
  elsif zones.any? and zones.any? { |z| !z[:length].present? and !z[:width].present? }
    @error = { error_status: :no_solutions_found, error_message: "We can't calculate a solution for this request unless you enter rectangular heated areas." }
    return self
  end
  element_results = []
  zones.each do |zone|
    element_results << get_elements_for_zone(zone)
  end
  if element_results.empty?
    @error = { error_status: :no_solutions_found, error_message: 'No matching heating elements found.' }
  else
    @elements = consolidate_elements(element_results.flatten)
    @coverage_percentage = [(100.0 * get_total_from_solution(@elements, 'area').to_f / heated_area_sqft).round, 100].min
    @target_area = heated_area_sqft
  end
  self
end

#get_elements_for_zone(zone) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/services/heating_system_calculator/element_finder_by_zones.rb', line 34

def get_elements_for_zone(zone)
  target_area_options = get_target_area_sub_zones_from_zone(zone)
  num_sub_zones = target_area_options[:num_sub_zones]
  target_dims = [target_area_options[:sub_zone_length], target_area_options[:sub_zone_width]]
  min_area = @element_set[0]['area']

  # Check if we have too small an area to even proceed
  return [] if target_area_options[:sub_zone_target_area] < min_area

  if @element_type == :cable
    cables_to_use = @element_set.map { |e| [1, e['length'].to_f.ceil, e['sku']] }
    solution = find_cable_fitting_solution_using(cables_to_use, target_dims.reverse)
  else
    # here we compare two solutions one with target_dims rotated, and choose the one with the closest area match and least cost
    solution1 = find_mat_fitting_solution(target_dims)
    solution2 = find_mat_fitting_solution(target_dims.reverse)
    ind = find_best_solution([solution1, solution2], target_area_options[:sub_zone_target_area], return_index_only = true)
    solution = [solution1, solution2][ind]
  end

  # get consolidated elements list
  elements = consolidate_elements(solution)

  # multiply out the elements needed for all sub zones
  elements.each do |elem|
    elem['qty'] = num_sub_zones * elem['qty']
  end

  elements
end

#get_target_area_sub_zones_from_zone(zone) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'app/services/heating_system_calculator/element_finder_by_zones.rb', line 65

def get_target_area_sub_zones_from_zone(zone)
  # handle exp joint spacing if an issue
  if @skip_expansion_joint != true && @expansion_joint_spacing
    num_sub_zones_length = (zone[:length].to_f / @expansion_joint_spacing).ceil
    num_sub_zones_width = (zone[:width] / @expansion_joint_spacing).ceil
  else
    num_sub_zones_length = 1
    num_sub_zones_width = 1
  end
  sub_zone_length = (zone[:length] / num_sub_zones_length)
  sub_zone_width = (zone[:width] / num_sub_zones_width)
  sub_zone_target_area = sub_zone_width * sub_zone_length
  num_sub_zones = num_sub_zones_width * num_sub_zones_length
  { num_sub_zones:, sub_zone_target_area:, sub_zone_width:,
    sub_zone_length: }
end