Class: BTAP::Structure
- Inherits:
-
Object
- Object
- BTAP::Structure
- Extended by:
- StructureData
- Defined in:
- lib/openstudio-standards/btap/structure.rb
Instance Attribute Summary collapse
-
#category ⇒ String
readonly
Building type CATEGORY (e.g. “public”).
-
#cladding ⇒ Symbol
readonly
Building cladding (e.g. :medium).
-
#co2 ⇒ Hash
readonly
Calculated embodied carbon (CO2-e kg).
-
#deadload ⇒ Float
readonly
Estimated dead load, in kg/m2 of floor area.
-
#feedback ⇒ Hash
readonly
Logged messages.
-
#finish ⇒ Symbol
readonly
Building finish (e.g. :light).
-
#framing ⇒ Symbol
readonly
Building framing (e.g. :steel).
-
#liveload ⇒ Float
readonly
Estimated non-occupant live load, in kg/m2 of floor area.
-
#structure ⇒ Symbol
readonly
Building STRUCTURE selection (e.g. :steel).
Instance Method Summary collapse
-
#initialize(model = nil, activity = nil, massive = true) ⇒ Structure
constructor
Initialize BTAP STRUCTURE parameters.
-
#tallyCO2(model) ⇒ Hash
Updates and returns embodied carbon estimates (A1-A3).
Methods included from StructureData
Constructor Details
#initialize(model = nil, activity = nil, massive = true) ⇒ Structure
Initialize BTAP STRUCTURE parameters.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 |
# File 'lib/openstudio-standards/btap/structure.rb', line 287 def initialize(model = nil, activity = nil, massive = true) mth = "BTAP::Structure::#{__callee__}" @feedback = {logs: []} lgs = @feedback[:logs] massive = false unless [true, false].include?(massive) unless model.is_a?(OpenStudio::Model::Model) lgs << "Invalid or empty OpenStudio model (#{mth})" return end unless activity.is_a?(BTAP::Activity) lgs << "Invalid or empty BTAP::Activity instance (#{mth})" return end cat = activity.category lload = activity.liveload if cat.respond_to?(:to_s) cat = cat.to_s.downcase if cat.empty? lgs << "Empty building category (#{mth})" return else unless data[:category].keys.include?(cat) lgs << "Unknown building category: #{cat} (#{mth})" return end end else lgs << "Invalid building category: #{cat.class} (#{mth})" return end if lload.respond_to?(:to_f) lload = lload.to_f else lgs << "Invalid live load (kg/m2): #{lload.class} (#{mth})" return end # Cap internal mass density to 1000 kg/m3, and thickness to 6". rho = 1000.0 th = 0.150 bldg = model.getBuilding @category = cat @structure = data[:category][cat][:small] # Switch to :large structure, instead of default :small. if data[:category][cat].key?(:stories) mx = data[:category][cat][:stories] n = bldg.standardsNumberOfAboveGroundStories n = n.empty? ? 1 : n.get @structure = data[:category][cat][:large] if n > mx elsif data[:category][cat].key?(:height) mx = data[:category][cat][:height] n = bldg.standardsNumberOfAboveGroundStories n = n.empty? ? 1 : n.get if n > 1 @structure = data[:category][cat][:large] else h = 0 model.getSpaces.each do |space| h = [mx, BTAP::Geometry::Spaces.space_height(space)].max end @structure = data[:category][cat][:large] if h > mx end end # Reset :clt and :metal structure selections - not yet available. @todo @structure = :steel if @structure == :metal @structure = :concrete if @structure == :clt # Set building framing, e.g. light-gauge :steel. @framing = data[:structure][@structure][:framing] # Set exterior cladding. @cladding = :light @cladding = :medium if @structure == :cmu @cladding = :medium if @category == "public" @cladding = :heavy if @category == "robust" # Set interior finish. @finish = :light @finish = :none if @framing == :cmu @finish = :heavy if @category == "robust" # 'Dead load' refers to the self-weight of structural elements of a # building, as well as non-structural fixtures that are permanently # attached to the building. They are considered 'dead' as they typically # do not move around during the life of the building. If/once a building # is resold, its new owners recover dead load as 'real estate assets'. # Dead load typically falls under design scopes of architects/engineers. # Although there are obvious design constraints to consider (e.g. fire # safety, $), designers do get to make design decisions when it comes to # dead load, e.g.: # - between steel vs concrete post/beam/slab structural options # - between light-gauge steel vs CMU wall construction options # - between foam vs fibrous insulation options # # Most dead load is modelled explicitly in OpenStudio, like envelope and # interzone sub/surfaces. Rough estimates of embodied carbon (in CO2-e # kg/m2) can be reasonably associated to selected construction assemblies # (based on m2), such as the embodied carbon of chosen insulation # materials or framing options. Other dead load, like lighting and HVAC, # are not modelled explicitly. Here, the 'deadload' attribute represents # a mass floor area density estimate (kg/m2) of non-modelled structural # and non-structural items like fixed furniture, partitions, columns, # beams, shear walls and bracing. # First, isolate occupied spaces & floors, as well as exposed surfaces. cspaces = model.getSpaces.select { |sp| sp.partofTotalFloorArea } floor_m2 = TBD.facets(cspaces, "all", "floor").map(&:grossArea).sum ofloor_m2 = TBD.facets(cspaces, "outdoors", "floor").map(&:grossArea).sum ifloor_m2 = TBD.facets(cspaces, "surface", "floor").map(&:grossArea).sum roof_m2 = TBD.facets(cspaces, "outdoors", "roofceiling").map(&:grossArea).sum wall_m2 = TBD.facets(cspaces, "outdoors", "walls").map(&:grossArea).sum iwall_m2 = TBD.facets(cspaces, "surface", "wall").map(&:grossArea).sum # In OpenStudio, partitions are usually limited to interzone walls between # zones, in order to save on simulation times. Partitions typically absent # from a model include walls surrounding lobbies, stairwells, WCs and # technical rooms, as well as separations between similar rooms (e.g. # multiple, side-by-side enclosed offices, a row of hotel rooms). # Comparing BTAP prototype models and samples of building plans for # similar facilities suggest matching modelled partition m2 (or total # floor m2) as a suitable basis to determine the weight of non-modelled # partitions. As this estimate may be more on the high side for many # prototype models, fixed appliances (e.g. fixtures, counters, doors and # windows) are considered included. # partition_m2 = iwall_m2 # partition_m2 = floor_m2 if partition_m2 > floor_m2 partition_m2 = floor_m2 # For wood-framed partitions, representative material volumes (per m2): # - 16% wood-framing: 0.0224 m3/m2 x 540 kg/m3 = 12.1 kg/m2 (35.7%) # - 84% insulation : 0.1176 m3/m2 x 19 kg/m3 = 2.2 kg/m2 ( 6.5%) # - drywall (2x) : 0.0250 m3/m2 x 785 kg/m3 = 19.6 kg/m2 (57.8%) # = 33.9 kg/m2 # # For steel-framed partitions, representative material volumes (per m2): # - 1% steel-framing: 1.25 x 2.5 x 1.5 kg/m = 4.7 kg/m2 (17.3%) # - 99% insulation : 0.1504 m3/m2 x 19 kg/m3 = 2.9 kg/m2 (10.7%) # - drywall (2x) : 0.0250 m3/m2 x 785 kg/m3 = 19.6 kg/m2 (72.0%) # = 27.2 kg/m2 # # For CMU partitions, representative material volumes (per m2): # - 10" medium weight CMU = 250.0 kg/m2 (approx.) case @framing when :cmu then partition_kgm2 = 250.0 * partition_m2 / floor_m2 when :wood then partition_kgm2 = 33.9 * partition_m2 / floor_m2 else partition_kgm2 = 27.2 * partition_m2 / floor_m2 end # Structural dead load - not explicitly modelled - include columns, # bracing, connectors, etc. For BTAP purposes, some basic assumptions are # required: # - 9m x 9m spans # - approx. 15 columns / 1000 m2 of floor area # - approx. 14" x 14" columns (0.126 m2) # - if structure :steel or :metal (HP14x102): # - 152 kg/m (x 125% for bracing, etc.) = 190 kg/m # - if structure :concrete # - concrete: 2240 kg/m3 x 0.126 m2 x 97% = 274 kg/m # - rebar: 7850 kg/m3 x 0.126 m2 x 3% = 30 kg/m # = 304 kg/m (+11%) # - if structure :cmu (mix of load bearing walls + smaller pours) # - 1/2 :concrete = 150 kg/m # - if structure :clt # - wood: 540 kg/m3 x 0.126 m2 x 97% = 66 kg/m # - anchors: 7850 kg/m3 x 0.126 m2 x 3% = 30 kg/m # = 96 kg/m (+45%) # - if structure :wood # - 1/2 :clt = 48 kg/m # Fetch approx. total column height (m) in building (including plenums). column_m = 0 model.getSpaces.each do |space| column_m += BTAP::Geometry::Spaces.space_height(space) * 15 / 1000 end case @structure when :steel then column_kgm2 = 190 * column_m / floor_m2 when :metal then column_kgm2 = 190 * column_m / floor_m2 when :cmu then column_kgm2 = 150 * column_m / floor_m2 when :wood then column_kgm2 = 48 * column_m / floor_m2 when :clt then column_kgm2 = 96 * column_m / floor_m2 else column_kgm2 = 304 * column_m / floor_m2 end @deadload = partition_kgm2 + column_kgm2 # The 'liveload' attribute represents the mass area density (kg/m2) of # dynamic, yet uniform floor live load from non-permanent items like # furniture, documents, copiers and computers, i.e. not real estate # assets. Architects and engineers deal with (fixed) live load as design # constraint - not as potential design option. Non-occupant live load is # taken into account when setting internal mass. Yet as a non-real estate # item, live load is not considered when tallying embodied carbon. # # Within BTAP, non-occupant live load estimates are stored in the # "NECB_building_types.csv" file, parsed/stored in a BTAP::Activity # instance (1x per building activity). These estimates are initially based # on NBC Part 4 minimum live load requirements (kPa), as well as data from # established structural engineering resources. Minimum live load kPa (or # psf) estimates, corresponding to hundreds of kg/m2 of floor area, are # strictly for structural dimensioning/safety purposes. They are not (or # are very rarely) representative of actual day-to-day loads. Back of the # envelope calculations suggest reducing live load code requirements down # to ~1/12th of their initial values for internal mass purposes. These # code requirements also include occupants, which should be set aside - # by subtracting the total building population mass: # # - NECB building occupant density (occupant/m2) x avg. 80 kg/adult # # This gives for instance a resulting live load estimate of 23 kg/m2 for # housing (low) and a 61 kg/m2 for manufacturing (high). It is obviously # challenging to pin down a single-number estimate for several building # types, including bigbox retail and warehousing. Grain of salt. @liveload = lload # Add internal mass objects, 1x instance per occupied space. cspaces.each do |space| break unless massive matID = "#{space.nameString} : Mass Material" conID = "#{space.nameString} : Mass Construction" defID = "#{space.nameString} : Mass Definition" mssID = "#{space.nameString} : Mass" # Calculate total mass of internal mass (kg), then thickness. kg = space.floorArea * (@liveload + @deadload) m2 = kg / rho / th mat = OpenStudio::Model::StandardOpaqueMaterial.new(model) mat.setName(matID) mat.setRoughness("MediumRough") mat.setThickness(th) mat.setConductivity(1.0) mat.setDensity(rho) mat.setSpecificHeat(1000) mat.setThermalAbsorptance(0.9) mat.setSolarAbsorptance(0.7) mat.setVisibleAbsorptance(0.7) con = OpenStudio::Model::Construction.new(model) con.setName(conID) layers = OpenStudio::Model::MaterialVector.new layers << mat con.setLayers(layers) df = OpenStudio::Model::InternalMassDefinition.new(model) df.setName(defID) df.setConstruction(con) df.setSurfaceArea(space.floorArea) df.setSurfaceArea(m2) mass = OpenStudio::Model::InternalMass.new(df) mass.setName(mssID) mass.setSpace(space) end # Embodied CO2-e kg (A1-A3) of a model is tallied separately as follows: @co2 = {} @co2[:structure ] = 0 @co2[:insulation] = 0 @co2[:cladding ] = 0 # The :structure key/value pair includes above grade 'structures' (e.g. # slabs, columns) and 'framing' (e.g. wood-framed vs steel-framed). Why # grouped together? In many smaller-scale facilities, structure and # framing are synonymous, or at least tightly coupled, e.g.: # - load-bearing wood-framed walls in small-scale residential # - load-bearing CMU walls in small-scale industrial # - metal buildings # # Below-grade structures (rebar + poured concrete) are ignored - no # alternative options are considered for the moment, e.g. lower carbon # concrete mixes. # # Upon initialization, only the :structure tally is set. It is assumed # that structure (and main framing) do not change with NECB U-factor # requirements, e.g.: # - NECB 2011 vs 2020 # - Vancouver vs Calgary # # Once default construction sets are (later) established (+), followed by # TBD uprating assemblies as per NECB 2017 & 2020 requirements (++), # embodied carbon tallies can be updated/reset, which would include: # - cladding # - insulation # - secondary framing (proportional to insulation thicknesses) # # (+) openstudio-standards/standards/necb/NECB2011/building_envelope.rb # (++) openstudio-standards/btap/bridging.rb # Start with occupied floors & roofs (exclude slabs on grade). m2 = ofloor_m2 + ifloor_m2 + roof_m2 case @structure when :wood then # engineered I-joists + plywood floor_kg = 50.0 * m2 floor_m3 = floor_kg / 540.0 # kg/m3 floor_co2kg = floor_m3 * 55.0 / floor_kg # kgCO2-e/kg when :concrete then # 200mm flat slab, 3% rebar + accessories floor_m3 = 0.200 * m2 floor_kgm3 = (0.03 * 7850.0) + (0.97 * 2240.0) # kg/m3 floor_co2kg = (0.03 * 0.854 ) + (0.97 * 0.250) # kgCO2-e/kg floor_kg = floor_m3 * floor_kgm3 else # :steel, includes joists/fasteners floor_m3 = 0.125 * m2 floor_kgm3 = (0.08 * 7850.0) + (0.92 * 2240.0) # kg/m3 floor_co2kg = (0.08 * 0.854 ) + (0.92 * 0.250) # kgCO2-e/kg floor_kg = floor_m3 * floor_kgm3 end @co2[:structure] += floor_co2kg * floor_kg # Add exposed walls and interior partitions. m2 = wall_m2 + iwall_m2 + partition_m2 case @framing when :wood then # basic 2x6 construction, 16% of framing cavity wall_kg = 12.1 * m2 wall_co2kg = 55.0 / 540.0 # 55 kgCO2-e/m3 / density when :cmu then # 250mm medium weight CMU, 200 kgCO2-e/m3 wall_m3 = 0.250 * m2 # nominal m3 wall_kg = 250.0 * m2 # volume-weighted concrete/air/grout wall_kgm3 = 0.250 / 250.0 wall_co2kg = 200.0 / wall_kgm3 # 200 kgCO2-e/m3 / density else # :steel, 1% lightweight steel-framing wall_kg = 4.7 * m2 wall_co2kg = 2.440 end @co2[:structure] += wall_co2kg * wall_kg true end |
Instance Attribute Details
#category ⇒ String (readonly)
Returns building type CATEGORY (e.g. “public”).
255 256 257 |
# File 'lib/openstudio-standards/btap/structure.rb', line 255 def category @category end |
#cladding ⇒ Symbol (readonly)
Returns building cladding (e.g. :medium).
264 265 266 |
# File 'lib/openstudio-standards/btap/structure.rb', line 264 def cladding @cladding end |
#co2 ⇒ Hash (readonly)
Returns calculated embodied carbon (CO2-e kg).
276 277 278 |
# File 'lib/openstudio-standards/btap/structure.rb', line 276 def co2 @co2 end |
#deadload ⇒ Float (readonly)
Returns estimated dead load, in kg/m2 of floor area.
270 271 272 |
# File 'lib/openstudio-standards/btap/structure.rb', line 270 def deadload @deadload end |
#feedback ⇒ Hash (readonly)
Returns logged messages.
279 280 281 |
# File 'lib/openstudio-standards/btap/structure.rb', line 279 def feedback @feedback end |
#finish ⇒ Symbol (readonly)
Returns building finish (e.g. :light).
267 268 269 |
# File 'lib/openstudio-standards/btap/structure.rb', line 267 def finish @finish end |
#framing ⇒ Symbol (readonly)
Returns building framing (e.g. :steel).
261 262 263 |
# File 'lib/openstudio-standards/btap/structure.rb', line 261 def framing @framing end |
#liveload ⇒ Float (readonly)
Returns estimated non-occupant live load, in kg/m2 of floor area.
273 274 275 |
# File 'lib/openstudio-standards/btap/structure.rb', line 273 def liveload @liveload end |
#structure ⇒ Symbol (readonly)
Returns building STRUCTURE selection (e.g. :steel).
258 259 260 |
# File 'lib/openstudio-standards/btap/structure.rb', line 258 def structure @structure end |
Instance Method Details
#tallyCO2(model) ⇒ Hash
Updates and returns embodied carbon estimates (A1-A3).
642 643 644 645 646 647 648 649 650 651 652 653 654 655 |
# File 'lib/openstudio-standards/btap/structure.rb', line 642 def tallyCO2(model) mth = "BTAP::Structure::#{__callee__}" cl = OpenStudio::Model::Model lgs = @feedback[:logs] unless model.is_a?(cl) lgs << "Invalid OpenStudio model (#{mth})" return 0 end # @todo @co2 end |