tbd 3.1.1 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
  #
3
- # Copyright (c) 2020-2022 Denis Bourgeois & Dan Macumber
3
+ # Copyright (c) 2020-2023 Denis Bourgeois & Dan Macumber
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the "Software"), to deal
@@ -913,6 +913,7 @@ module TBD
913
913
  return mismatch("argh", argh, Hash, mth, DBG, tbd) unless argh.is_a?(Hash)
914
914
 
915
915
  argh = {} if argh.empty?
916
+ argh[:sub_tol ] = TBD::TOL unless argh.key?(:sub_tol )
916
917
  argh[:option ] = "" unless argh.key?(:option )
917
918
  argh[:io_path ] = nil unless argh.key?(:io_path )
918
919
  argh[:schema_path ] = nil unless argh.key?(:schema_path )
@@ -974,8 +975,9 @@ module TBD
974
975
  end
975
976
  end
976
977
 
977
- surface[:heating] = heat[:spt] if heat[:spt] # if valid heating setpoints
978
- surface[:cooling] = cool[:spt] if cool[:spt] # if valid cooling setpoints
978
+ # Recover if valid setpoints.
979
+ surface[:heating] = heat[:spt] if heat && heat[:spt]
980
+ surface[:cooling] = cool[:spt] if cool && cool[:spt]
979
981
 
980
982
  tbd[:surfaces][s.nameString] = surface
981
983
  end # (opaque) surfaces populated
@@ -1174,9 +1176,6 @@ module TBD
1174
1176
  farthest_V = origin_point_V if farther
1175
1177
  end
1176
1178
 
1177
- puts "ADDITION!!" if id == "ADDITION"
1178
- puts "#{reference_V} vs #{farthest_V}" if id == "ADDITION"
1179
-
1180
1179
  angle = reference_V.angle(farthest_V)
1181
1180
  invalid("#{id} polar angle", mth, 0, ERROR, 0) if angle.nil?
1182
1181
  angle = 0 if angle.nil?
@@ -1488,7 +1487,7 @@ module TBD
1488
1487
 
1489
1488
  edge[:surfaces].keys.each do |i|
1490
1489
  break if is[:rimjoist] || is[:balcony]
1491
- break unless deratables.size == 2
1490
+ break unless deratables.size > 0
1492
1491
  break if floors.key?(id)
1493
1492
  next if i == id
1494
1493
  next unless floors.key?(i)
@@ -1771,13 +1770,120 @@ module TBD
1771
1770
  end
1772
1771
  end
1773
1772
 
1773
+ # Fetch edge multipliers for subsurfaces, if applicable.
1774
+ edges.values.each do |edge|
1775
+ next if edge.key?(:mult) # skip if already assigned
1776
+ next unless edge.key?(:surfaces)
1777
+ next unless edge.key?(:psi)
1778
+ ok = false
1779
+
1780
+ edge[:psi].keys.each do |k|
1781
+ break if ok
1782
+
1783
+ jamb = k.to_s.include?("jamb")
1784
+ sill = k.to_s.include?("sill")
1785
+ head = k.to_s.include?("head")
1786
+ ok = jamb || sill || head
1787
+ end
1788
+
1789
+ next unless ok # if OK, edge links subsurface(s) ... yet which one(s)?
1790
+
1791
+ edge[:surfaces].each do |id, surface|
1792
+ next unless tbd[:surfaces].key?(id) # look up parent (opaque) surface
1793
+
1794
+ [:windows, :doors, :skylights].each do |subtypes|
1795
+ next unless tbd[:surfaces][id].key?(subtypes)
1796
+
1797
+ tbd[:surfaces][id][subtypes].each do |nom, sub|
1798
+ next unless edge[:surfaces].key?(nom)
1799
+ next unless sub[:mult] > 1
1800
+
1801
+ # An edge may be tagged with (potentially conflicting) multipliers.
1802
+ # This is only possible if the edge links 2 subsurfaces, e.g. a
1803
+ # shared jamb between window & door. By default, TBD tags common
1804
+ # subsurface edges as (mild) "transitions" (i.e. PSI 0 W/K.m), so
1805
+ # there would be no point in assigning an edge multiplier. Users
1806
+ # can however reset an edge type via a TBD JSON input file (e.g.
1807
+ # "joint" instead of "transition"). It would be a very odd choice,
1808
+ # but TBD doesn't prohibit it. If linked subsurfaces have different
1809
+ # multipliers (e.g. 2 vs 3), TBD tracks the highest value.
1810
+ edge[:mult] = sub[:mult] unless edge.key?(:mult)
1811
+ edge[:mult] = sub[:mult] if sub[:mult] > edge[:mult]
1812
+ end
1813
+ end
1814
+ end
1815
+ end
1816
+
1817
+ # Unless a user has set the thermal bridge type of an individual edge via
1818
+ # JSON input, reset any subsurface's head, sill or jamb edges as (mild)
1819
+ # transitions when in close proximity to another subsurface edge. Both
1820
+ # edges' origin and terminal vertices must be in close proximity. Edges
1821
+ # of unhinged subsurfaces are ignored.
1822
+ edges.each do |id, edge|
1823
+ nb = 0 # linked subsurfaces (i.e. "holes")
1824
+ match = false
1825
+ next if edge.key?(:io_type) # skip if set in JSON
1826
+ next unless edge.key?(:v0)
1827
+ next unless edge.key?(:v1)
1828
+ next unless edge.key?(:psi)
1829
+ next unless edge.key?(:surfaces)
1830
+
1831
+ edge[:surfaces].keys.each do |identifier|
1832
+ break if match
1833
+ next unless holes.key?(identifier)
1834
+
1835
+ if holes[identifier].attributes.key?(:unhinged)
1836
+ nb = 0 if holes[identifier].attributes[:unhinged]
1837
+ break if holes[identifier].attributes[:unhinged]
1838
+ end
1839
+
1840
+ nb += 1
1841
+ match = true if nb > 1
1842
+ end
1843
+
1844
+ if nb == 1 # linking 1x subsurface, search for 1x other.
1845
+ e1 = { v0: edge[:v0].point, v1: edge[:v1].point }
1846
+
1847
+ edges.each do |nom, e|
1848
+ nb = 0
1849
+ break if match
1850
+ next if nom == id
1851
+ next if e.key?(:io_type)
1852
+ next unless e.key?(:psi)
1853
+ next unless e.key?(:surfaces)
1854
+
1855
+ e[:surfaces].keys.each do |identifier|
1856
+ next unless holes.key?(identifier)
1857
+
1858
+ if holes[identifier].attributes.key?(:unhinged)
1859
+ nb = 0 if holes[identifier].attributes[:unhinged]
1860
+ break if holes[identifier].attributes[:unhinged]
1861
+ end
1862
+
1863
+ nb += 1
1864
+ end
1865
+
1866
+ next unless nb == 1 # only process edge if linking 1x subsurface
1867
+
1868
+ e2 = { v0: e[:v0].point, v1: e[:v1].point }
1869
+ match = matches?(e1, e2, argh[:sub_tol])
1870
+ end
1871
+ end
1872
+
1873
+ next unless match
1874
+
1875
+ edge[:psi] = { transition: 0.000 }
1876
+ edge[:set] = json[:io][:building][:psi]
1877
+ end
1878
+
1774
1879
  # Loop through each edge and assign heat loss to linked surfaces.
1775
1880
  edges.each do |identifier, edge|
1776
1881
  next unless edge.key?(:psi)
1777
1882
  rsi = 0
1778
- max = edge[:psi].values.max
1779
- type = edge[:psi].key(max)
1883
+ max = edge[:psi ].values.max
1884
+ type = edge[:psi ].key(max)
1780
1885
  length = edge[:length]
1886
+ length *= edge[:mult ] if edge.key?(:mult)
1781
1887
  bridge = { psi: max, type: type, length: length }
1782
1888
  deratables = {}
1783
1889
  apertures = {}
@@ -1869,7 +1975,7 @@ module TBD
1869
1975
  # ... first 'uprate' targeted insulation layers (see ua.rb) before derating.
1870
1976
  # Check for new argh keys [:wall_uo], [:roof_uo] and/or [:floor_uo].
1871
1977
  up = argh[:uprate_walls] || argh[:uprate_roofs] || argh[:uprate_floors]
1872
- uprate(model, tbd[:surfaces], argh) if up
1978
+ uprate(model, tbd[:surfaces], argh) if up
1873
1979
 
1874
1980
  # Derated (cloned) constructions are unique to each deratable surface.
1875
1981
  # Unique construction names are prefixed with the surface name,
@@ -1975,6 +2081,7 @@ module TBD
1975
2081
  set = e[:set]
1976
2082
  t = e[:psi].key(v)
1977
2083
  l = e[:length]
2084
+ l *= e[:mult] if e.key?(:mult)
1978
2085
  edge = { psi: set, type: t, length: l, surfaces: e[:surfaces].keys }
1979
2086
  edge[:v0x] = e[:v0].point.x
1980
2087
  edge[:v0y] = e[:v0].point.y
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
  #
3
- # Copyright (c) 2020-2022 Denis Bourgeois & Dan Macumber
3
+ # Copyright (c) 2020-2023 Denis Bourgeois & Dan Macumber
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
  #
3
- # Copyright (c) 2020-2022 Denis Bourgeois & Dan Macumber
3
+ # Copyright (c) 2020-2023 Denis Bourgeois & Dan Macumber
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the "Software"), to deal
@@ -47,6 +47,8 @@ module TBD
47
47
  return mismatch("film", film, cl3, mth, DBG, res) unless film.is_a?(cl3)
48
48
  return mismatch("Ut", ut, cl3, mth, DBG, res) unless ut.is_a?(cl3)
49
49
 
50
+ loss = 0.0 # residual heatloss (not assigned) [W/K]
51
+ area = lc.getNetArea
50
52
  lyr = insulatingLayer(lc)
51
53
  lyr[:index] = nil unless lyr[:index].is_a?(Numeric)
52
54
  lyr[:index] = nil unless lyr[:index] >= 0
@@ -57,8 +59,6 @@ module TBD
57
59
  return zero("'#{id}': films", mth, WRN, res) unless film > TOL
58
60
  return zero("'#{id}': Ut", mth, WRN, res) unless ut > TOL
59
61
  return invalid("'#{id}': Ut", mth, 0, WRN, res) unless ut < 5.678
60
-
61
- area = lc.getNetArea
62
62
  return zero("'#{id}': net area (m2)", mth, ERR, res) unless area > TOL
63
63
 
64
64
  # First, calculate initial layer RSi to initially meet Ut target.
@@ -74,8 +74,6 @@ module TBD
74
74
 
75
75
  return zero("'#{id}': new Rsi", mth, ERR, res) unless new_r > 0.001
76
76
 
77
- loss = 0.0 # residual heatloss (not assigned) [W/K]
78
-
79
77
  if lyr[:type] == :massless
80
78
  m = lc.getLayer(lyr[:index]).to_MasslessOpaqueMaterial
81
79
  return invalid("'#{id}' massless layer?", mth, 0) if m.empty?
@@ -85,7 +83,7 @@ module TBD
85
83
  new_r = 0.001 unless new_r > 0.001
86
84
  loss = (new_u - 1 / new_r) * area unless new_r > 0.001
87
85
  m.setThermalResistance(new_r)
88
- else # type == :standard
86
+ else # type == :standard
89
87
  m = lc.getLayer(lyr[:index]).to_StandardOpaqueMaterial
90
88
  return invalid("'#{id}' standard layer?", mth, 0) if m.empty?
91
89
 
@@ -595,77 +593,46 @@ module TBD
595
593
  next unless surface[:net] > TOL
596
594
  next unless surface.key?(:u)
597
595
  next unless surface[:u] > TOL
598
- heating = 21.0
599
- heating = surface[:heating] if surface.key?(:heating)
600
- bloc = b1
601
- bloc = b2 if heating < 18
602
-
596
+ heating = 21.0
597
+ heating = surface[:heating] if surface.key?(:heating)
598
+ bloc = b1
599
+ bloc = b2 if heating < 18
603
600
  reference = surface.key?(:ref)
601
+
604
602
  if type == :wall
605
603
  areas[:walls][:net ] += surface[:net]
606
- bloc[:pro][:walls ] += surface[:net] * surface[:u ]
607
- bloc[:ref][:walls ] += surface[:net] * surface[:ref] if reference
608
- bloc[:ref][:walls ] += surface[:net] * surface[:u ] unless reference
604
+ bloc[:pro][:walls ] += surface[:net] * surface[:u ]
605
+ bloc[:ref][:walls ] += surface[:net] * surface[:ref] if reference
606
+ bloc[:ref][:walls ] += surface[:net] * surface[:u ] unless reference
609
607
  elsif type == :ceiling
610
608
  areas[:roofs][:net ] += surface[:net]
611
- bloc[:pro][:roofs ] += surface[:net] * surface[:u ]
612
- bloc[:ref][:roofs ] += surface[:net] * surface[:ref] if reference
613
- bloc[:ref][:roofs ] += surface[:net] * surface[:u ] unless reference
609
+ bloc[:pro][:roofs ] += surface[:net] * surface[:u ]
610
+ bloc[:ref][:roofs ] += surface[:net] * surface[:ref] if reference
611
+ bloc[:ref][:roofs ] += surface[:net] * surface[:u ] unless reference
614
612
  else
615
613
  areas[:floors][:net] += surface[:net]
616
- bloc[:pro][:floors] += surface[:net] * surface[:u ]
617
- bloc[:ref][:floors] += surface[:net] * surface[:ref] if reference
618
- bloc[:ref][:floors] += surface[:net] * surface[:u ] unless reference
619
- end
620
-
621
- if surface.key?(:doors)
622
- surface[:doors].values.each do |door|
623
- next unless door.key?(:gross)
624
- next unless door[:gross] > TOL
625
- next unless door.key?(:u)
626
- next unless door[:u] > TOL
627
- areas[:walls][:subs ] += door[:gross] if type == :wall
628
- areas[:roofs][:subs ] += door[:gross] if type == :ceiling
629
- areas[:floors][:subs] += door[:gross] if type == :floor
630
- bloc[:pro][:doors ] += door[:gross] * door[:u]
631
-
632
- ok = door.key?(:ref)
633
- bloc[:ref][:doors ] += door[:gross] * door[:ref] if ok
634
- bloc[:ref][:doors ] += door[:gross] * door[:u ] unless ok
635
- end
636
- end
637
-
638
- if surface.key?(:windows)
639
- surface[:windows].values.each do |window|
640
- next unless window.key?(:gross)
641
- next unless window[:gross] > TOL
642
- next unless window.key?(:u)
643
- next unless window[:u] > TOL
644
- areas[:walls][:subs ] += window[:gross] if type == :wall
645
- areas[:roofs][:subs ] += window[:gross] if type == :ceiling
646
- areas[:floors][:subs] += window[:gross] if type == :floor
647
- bloc[:pro][:windows] += window[:gross] * window[:u]
648
-
649
- ok = window.key?(:ref)
650
- bloc[:ref][:windows ] += window[:gross] * window[:ref] if ok
651
- bloc[:ref][:windows ] += window[:gross] * window[:u ] unless ok
652
- end
614
+ bloc[:pro][:floors ] += surface[:net] * surface[:u ]
615
+ bloc[:ref][:floors ] += surface[:net] * surface[:ref] if reference
616
+ bloc[:ref][:floors ] += surface[:net] * surface[:u ] unless reference
653
617
  end
654
618
 
655
- if surface.key?(:skylights)
656
- surface[:skylights].values.each do |sky|
657
- next unless sky.key?(:gross)
658
- next unless sky[:gross] > TOL
659
- next unless sky.key?(:u)
660
- next unless sky[:u] > TOL
661
- areas[:walls][:subs ] += sky[:gross] if type == :wall
662
- areas[:roofs][:subs ] += sky[:gross] if type == :ceiling
663
- areas[:floors][:subs ] += sky[:gross] if type == :floor
664
- bloc[:pro][:skylights] += sky[:gross] * sky[:u]
665
-
666
- ok = sky.key?(:ref)
667
- bloc[:ref][:skylights] += sky[:gross] * sky[:ref] if ok
668
- bloc[:ref][:skylights] += sky[:gross] * sky[:u ] unless ok
619
+ [:doors, :windows, :skylights].each do |subs|
620
+ next unless surface.key?(subs)
621
+
622
+ surface[subs].values.each do |sub|
623
+ next unless sub.key?(:gross)
624
+ next unless sub.key?(:u )
625
+ next unless sub[:gross] > TOL
626
+ next unless sub[:u ] > TOL
627
+
628
+ gross = sub[:gross]
629
+ gross *= sub[:mult ] if sub.key?(:mult)
630
+ areas[:walls ][:subs] += gross if type == :wall
631
+ areas[:roofs ][:subs] += gross if type == :ceiling
632
+ areas[:floors][:subs] += gross if type == :floor
633
+ bloc[:pro ][subs ] += gross * sub[:u ]
634
+ bloc[:ref ][subs ] += gross * sub[:ref] if sub.key?(:ref)
635
+ bloc[:ref ][subs ] += gross * sub[:u ] unless sub.key?(:ref)
669
636
  end
670
637
  end
671
638
 
@@ -953,7 +920,7 @@ module TBD
953
920
  model = "* modèle : #{ua[:file]}" if ua.key?(:file) && lang == :fr
954
921
  model += " (v#{ua[:version]})" if ua.key?(:version)
955
922
  report << model unless model.empty?
956
- report << "* TBD : v3.1.1"
923
+ report << "* TBD : v3.2.1"
957
924
  report << "* date : #{ua[:date]}"
958
925
 
959
926
  if lang == :en