Class: HeatingSystemCalculator::BaseElementFinder

Inherits:
BaseItemFinder show all
Defined in:
app/services/heating_system_calculator/base_element_finder.rb

Direct Known Subclasses

ElementFinderByArea, ElementFinderByZones

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseItemFinder

#get_number_of_poles_from_consolidated_elements, #get_total_from_solution

Constructor Details

#initialize(options) ⇒ BaseElementFinder

Returns a new instance of BaseElementFinder.



5
6
7
# File 'app/services/heating_system_calculator/base_element_finder.rb', line 5

def initialize(options)
  @element_set = options[:element_set]
end

Instance Attribute Details

#coverage_percentageObject (readonly)

Returns the value of attribute coverage_percentage.



3
4
5
# File 'app/services/heating_system_calculator/base_element_finder.rb', line 3

def coverage_percentage
  @coverage_percentage
end

#elementsObject (readonly)

Returns the value of attribute elements.



3
4
5
# File 'app/services/heating_system_calculator/base_element_finder.rb', line 3

def elements
  @elements
end

#errorObject (readonly)

Returns the value of attribute error.



3
4
5
# File 'app/services/heating_system_calculator/base_element_finder.rb', line 3

def error
  @error
end

#target_areaObject (readonly)

Returns the value of attribute target_area.



3
4
5
# File 'app/services/heating_system_calculator/base_element_finder.rb', line 3

def target_area
  @target_area
end

Instance Method Details

#consolidate_elements(solution) ⇒ Object



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

def consolidate_elements(solution)
  # set up consolidated elements list
  elements = []
  # loop over solution elements
  solution.each do |soln|
    # assume no duplicate products in the product set
    foundmatch = false
    # loop over our result set of unique elements
    elements.each do |elem|
      # test for duplicate product entries in the solution set
      next unless soln['sku'] == elem['sku']

      # match found , increment quantity in our result set, set flag and break
      foundmatch = true
      elem['qty'] = elem['qty'] + (soln['qty'] || 1)
      break
    end
    next if foundmatch

    # no match found, so this is a unique entry in our result set
    elemnt = soln.merge({})
    elemnt['qty'] ||= 1
    elements << elemnt
  end
  elements
end

#find_best_solution(solutionset, targetarea, return_index_only = false) ⇒ Object



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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'app/services/heating_system_calculator/base_element_finder.rb', line 36

def find_best_solution(solutionset, targetarea, return_index_only = false)
  # Priority: Prefer solutions that MEET OR EXCEED target over undersized ones
  # Among adequate solutions, prefer closest to target with lowest price
  threshold = 0.05

  # Separate solutions into adequate (>= target) and undersized (< target)
  adequate_solutions = []
  undersized_solutions = []

  solutionset.each_with_index do |solution, i|
    area = get_total_from_solution(solution, 'area')
    if area >= targetarea
      adequate_solutions << [i, solution, area]
    else
      undersized_solutions << [i, solution, area]
    end
  end

  # Use adequate solutions if available, otherwise fall back to undersized
  candidates = adequate_solutions.any? ? adequate_solutions : undersized_solutions

  bestdiffarea = 99_999_999.0
  bestprice = 99_999_999.0
  bestsolnsetind = -1
  best_length = 99_999

  candidates.each do |i, solution, area|
    diffarea = (targetarea - area).abs
    price = get_total_from_solution(solution, 'price')

    # Select best solution: closest to target, then cheapest, then fewest pieces
    is_better = (diffarea < bestdiffarea && bestdiffarea > threshold * targetarea) ||
                (diffarea < threshold * targetarea && price < bestprice) ||
                (diffarea < threshold * targetarea && (price - bestprice).abs < threshold * bestprice && solution.length < best_length)

    next unless is_better

    bestsolnsetind = i
    bestdiffarea = diffarea
    bestprice = price
    best_length = solution.length
  end

  res = solutionset[bestsolnsetind]
  res = bestsolnsetind if return_index_only
  res
end