osut 0.7.0__py3-none-any.whl → 0.8.2__py3-none-any.whl
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.
- osut/osut.py +525 -291
- {osut-0.7.0.dist-info → osut-0.8.2.dist-info}/METADATA +3 -3
- osut-0.8.2.dist-info/RECORD +7 -0
- {osut-0.7.0.dist-info → osut-0.8.2.dist-info}/WHEEL +1 -1
- {osut-0.7.0.dist-info → osut-0.8.2.dist-info}/licenses/LICENSE +1 -1
- osut-0.7.0.dist-info/RECORD +0 -7
- {osut-0.7.0.dist-info → osut-0.8.2.dist-info}/top_level.txt +0 -0
osut/osut.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BSD 3-Clause License
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2022-
|
|
3
|
+
# Copyright (c) 2022-2026, rd2
|
|
4
4
|
#
|
|
5
5
|
# Redistribution and use in source and binary forms, with or without
|
|
6
6
|
# modification, are permitted provided that the following conditions are met:
|
|
@@ -36,15 +36,24 @@ from dataclasses import dataclass
|
|
|
36
36
|
|
|
37
37
|
@dataclass(frozen=True)
|
|
38
38
|
class _CN:
|
|
39
|
-
DBG = oslg.CN.DEBUG
|
|
40
|
-
INF = oslg.CN.INFO
|
|
41
|
-
WRN = oslg.CN.WARN
|
|
42
|
-
ERR = oslg.CN.ERROR
|
|
43
|
-
FTL = oslg.CN.FATAL
|
|
44
|
-
TOL = 0.01
|
|
45
|
-
TOL2 = TOL * TOL
|
|
46
|
-
HEAD = 2.032
|
|
47
|
-
SILL = 0.762
|
|
39
|
+
DBG = oslg.CN.DEBUG # see github.com/rd2/pyOSlg
|
|
40
|
+
INF = oslg.CN.INFO # see github.com/rd2/pyOSlg
|
|
41
|
+
WRN = oslg.CN.WARN # see github.com/rd2/pyOSlg
|
|
42
|
+
ERR = oslg.CN.ERROR # see github.com/rd2/pyOSlg
|
|
43
|
+
FTL = oslg.CN.FATAL # see github.com/rd2/pyOSlg
|
|
44
|
+
TOL = 0.01 # default distance tolerance (m)
|
|
45
|
+
TOL2 = TOL * TOL # default area tolerance (m2)
|
|
46
|
+
HEAD = 2.032 # standard 80" door
|
|
47
|
+
SILL = 0.762 # standard 30" window sill
|
|
48
|
+
NS = "nameString" # OpenStudio object identifier method
|
|
49
|
+
DMIN = 0.010 # min. insulating material thickness
|
|
50
|
+
DMAX = 1.000 # max. insulating material thickness
|
|
51
|
+
KMIN = 0.010 # min. insulating material thermal conductivity
|
|
52
|
+
KMAX = 2.000 # max. insulating material thermal conductivity
|
|
53
|
+
UMAX = KMAX / DMIN # material USi upper limit, 200.000
|
|
54
|
+
UMIN = KMIN / DMAX # material USi lower limit, 0.010
|
|
55
|
+
RMIN = 1.0 / UMAX # material RSi lower limit, 0.005 (or R-IP 0.03)
|
|
56
|
+
RMAX = 1.0 / UMIN # material RSi upper limit, 100.000 (or R-IP 567.80)
|
|
48
57
|
CN = _CN()
|
|
49
58
|
|
|
50
59
|
# General surface orientations (see 'facets' method).
|
|
@@ -300,6 +309,456 @@ def clamp(value, minimum, maximum) -> float:
|
|
|
300
309
|
return value
|
|
301
310
|
|
|
302
311
|
|
|
312
|
+
def areStandardOpaqueLayers(lc=None) -> bool:
|
|
313
|
+
"""Validates if every material in a layered construction is standard/opaque.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
lc (openstudio.model.LayeredConstruction):
|
|
317
|
+
an OpenStudio layered construction
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
True: If all layers are valid (standard & opaque).
|
|
321
|
+
False: If invalid inputs (see logs).
|
|
322
|
+
|
|
323
|
+
"""
|
|
324
|
+
mth = "osut.areStandardOpaqueLayers"
|
|
325
|
+
cl = openstudio.model.LayeredConstruction
|
|
326
|
+
|
|
327
|
+
if not isinstance(lc, cl):
|
|
328
|
+
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, 0.0)
|
|
329
|
+
|
|
330
|
+
for m in lc.layers():
|
|
331
|
+
if not m.to_StandardOpaqueMaterial(): return False
|
|
332
|
+
|
|
333
|
+
return True
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def thickness(lc=None) -> float:
|
|
337
|
+
"""Returns total (standard opaque) layered construction thickness (m).
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
lc (openstudio.model.LayeredConstruction):
|
|
341
|
+
an OpenStudio layered construction
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
float: A standard opaque construction thickness.
|
|
345
|
+
0.0: If invalid inputs (see logs).
|
|
346
|
+
|
|
347
|
+
"""
|
|
348
|
+
mth = "osut.thickness"
|
|
349
|
+
cl = openstudio.model.LayeredConstruction
|
|
350
|
+
d = 0.0
|
|
351
|
+
|
|
352
|
+
if not isinstance(lc, cl):
|
|
353
|
+
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, 0.0)
|
|
354
|
+
if not areStandardOpaqueLayers(lc):
|
|
355
|
+
oslg.log(CN.ERR, "holding non-StandardOpaqueMaterial(s) %s" % mth)
|
|
356
|
+
return d
|
|
357
|
+
|
|
358
|
+
for m in lc.layers(): d += m.thickness()
|
|
359
|
+
|
|
360
|
+
return d
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def glazingAirFilmRSi(usi=5.85) -> float:
|
|
364
|
+
"""Returns total air film resistance of a fenestrated construction (m2•K/W).
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
usi (float):
|
|
368
|
+
A fenestrated construction's U-factor (W/m2•K).
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
float: Total air film resistances.
|
|
372
|
+
0.1216: If invalid input (see logs).
|
|
373
|
+
|
|
374
|
+
"""
|
|
375
|
+
# The sum of thermal resistances of calculated exterior and interior film
|
|
376
|
+
# coefficients under standard winter conditions are taken from:
|
|
377
|
+
#
|
|
378
|
+
# https://bigladdersoftware.com/epx/docs/9-6/engineering-reference/
|
|
379
|
+
# window-calculation-module.html#simple-window-model
|
|
380
|
+
#
|
|
381
|
+
# These remain acceptable approximations for flat windows, yet likely
|
|
382
|
+
# unsuitable for subsurfaces with curved or projecting shapes like domed
|
|
383
|
+
# skylights. The solution here is considered an adequate fix for reporting,
|
|
384
|
+
# awaiting eventual OpenStudio (and EnergyPlus) upgrades to report NFRC 100
|
|
385
|
+
# (or ISO) air film resistances under standard winter conditions.
|
|
386
|
+
#
|
|
387
|
+
# For U-factors above 8.0 W/m2•K (or invalid input), the function returns
|
|
388
|
+
# 0.1216 m2•K/W, which corresponds to a construction with a single glass
|
|
389
|
+
# layer thickness of 2mm & k = ~0.6 W/m.K.
|
|
390
|
+
#
|
|
391
|
+
# The EnergyPlus Engineering calculations were designed for vertical
|
|
392
|
+
# windows, not for horizontal, slanted or domed surfaces - use with caution.
|
|
393
|
+
mth = "osut.glazingAirFilmRSi"
|
|
394
|
+
val = 0.1216
|
|
395
|
+
|
|
396
|
+
try:
|
|
397
|
+
usi = float(usi)
|
|
398
|
+
except:
|
|
399
|
+
return oslg.mismatch("usi", usi, float, mth, CN.DBG, val)
|
|
400
|
+
|
|
401
|
+
if usi > 8.0:
|
|
402
|
+
return oslg.invalid("usi", mth, 1, CN.WRN, val)
|
|
403
|
+
elif usi < 0:
|
|
404
|
+
return oslg.negative("usi", mth, CN.WRN, val)
|
|
405
|
+
elif abs(usi) < CN.TOL:
|
|
406
|
+
return oslg.zero("usi", mth, CN.WRN, val)
|
|
407
|
+
|
|
408
|
+
rsi = 1 / (0.025342 * usi + 29.163853) # exterior film, next interior film
|
|
409
|
+
|
|
410
|
+
if usi < 5.85:
|
|
411
|
+
return rsi + 1 / (0.359073 * math.log(usi) + 6.949915)
|
|
412
|
+
|
|
413
|
+
return rsi + 1 / (1.788041 * usi - 2.886625)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def rsi(lc=None, film=0.0, t=0.0) -> float:
|
|
417
|
+
"""Returns a construction's 'standard calc' thermal resistance (m2•K/W),
|
|
418
|
+
which includes air film resistances. It excludes insulating effects of
|
|
419
|
+
shades, screens, etc. in the case of fenestrated constructions. Adapted
|
|
420
|
+
from BTAP's 'Material' Module "get_conductance" (P. Lopez).
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
lc (openstudio.model.LayeredConstruction):
|
|
424
|
+
an OpenStudio layered construction
|
|
425
|
+
film (float):
|
|
426
|
+
thermal resistance of surface air films (m2•K/W)
|
|
427
|
+
t (float):
|
|
428
|
+
gas temperature (°C) (optional)
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
float: A layered construction's thermal resistance.
|
|
432
|
+
0.0: If invalid input (see logs).
|
|
433
|
+
|
|
434
|
+
"""
|
|
435
|
+
mth = "osut.rsi"
|
|
436
|
+
cl = openstudio.model.LayeredConstruction
|
|
437
|
+
|
|
438
|
+
if not isinstance(lc, cl):
|
|
439
|
+
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, 0.0)
|
|
440
|
+
|
|
441
|
+
try:
|
|
442
|
+
film = float(film)
|
|
443
|
+
except:
|
|
444
|
+
return oslg.mismatch("film", film, float, mth, CN.DBG, 0.0)
|
|
445
|
+
|
|
446
|
+
try:
|
|
447
|
+
t = float(t)
|
|
448
|
+
except:
|
|
449
|
+
return oslg.mismatch("temp K", t, float, mth, CN.DBG, 0.0)
|
|
450
|
+
|
|
451
|
+
t += 273.0 # °C to K
|
|
452
|
+
|
|
453
|
+
if t < 0:
|
|
454
|
+
return oslg.negative("temp K", mth, CN.ERR, 0.0)
|
|
455
|
+
if film < 0:
|
|
456
|
+
return oslg.negative("film", mth, CN.ERR, 0.0)
|
|
457
|
+
|
|
458
|
+
rsi = film
|
|
459
|
+
|
|
460
|
+
for m in lc.layers():
|
|
461
|
+
if m.to_SimpleGlazing():
|
|
462
|
+
return 1 / m.to_SimpleGlazing().get().uFactor()
|
|
463
|
+
elif m.to_StandardGlazing():
|
|
464
|
+
rsi += m.to_StandardGlazing().get().thermalResistance()
|
|
465
|
+
elif m.to_RefractionExtinctionGlazing():
|
|
466
|
+
rsi += m.to_RefractionExtinctionGlazing().get().thermalResistance()
|
|
467
|
+
elif m.to_Gas():
|
|
468
|
+
rsi += m.to_Gas().get().getThermalResistance(t)
|
|
469
|
+
elif m.to_GasMixture():
|
|
470
|
+
rsi += m.to_GasMixture().get().getThermalResistance(t)
|
|
471
|
+
|
|
472
|
+
# Opaque materials next.
|
|
473
|
+
if m.to_StandardOpaqueMaterial():
|
|
474
|
+
rsi += m.to_StandardOpaqueMaterial().get().thermalResistance()
|
|
475
|
+
elif m.to_MasslessOpaqueMaterial():
|
|
476
|
+
rsi += m.to_MasslessOpaqueMaterial()
|
|
477
|
+
elif m.to_RoofVegetation():
|
|
478
|
+
rsi += m.to_RoofVegetation().get().thermalResistance()
|
|
479
|
+
elif m.to_AirGap():
|
|
480
|
+
rsi += m.to_AirGap().get().thermalResistance()
|
|
481
|
+
|
|
482
|
+
return rsi
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def insulatingLayer(lc=None) -> dict:
|
|
486
|
+
"""Identifies a layered construction's (opaque) insulating layer.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
lc (openStudio.model.LayeredConstruction):
|
|
490
|
+
an OpenStudio layered construction
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
An insulating-layer dictionary:
|
|
494
|
+
- "index" (int): construction's insulating layer index [0, n layers)
|
|
495
|
+
- "type" (str): layer material type ("standard" or "massless")
|
|
496
|
+
- "r" (float): material thermal resistance in m2•K/W.
|
|
497
|
+
If unsuccessful, dictionary is voided as follows (see logs):
|
|
498
|
+
"index": None
|
|
499
|
+
"type": None
|
|
500
|
+
"r": 0.0
|
|
501
|
+
|
|
502
|
+
"""
|
|
503
|
+
mth = "osut.insulatingLayer"
|
|
504
|
+
cl = openstudio.model.LayeredConstruction
|
|
505
|
+
res = dict(index=None, type=None, r=0.0)
|
|
506
|
+
i = 0 # iterator
|
|
507
|
+
|
|
508
|
+
if not isinstance(lc, cl):
|
|
509
|
+
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, res)
|
|
510
|
+
|
|
511
|
+
for l in lc.layers():
|
|
512
|
+
if l.to_MasslessOpaqueMaterial():
|
|
513
|
+
l = l.to_MasslessOpaqueMaterial().get()
|
|
514
|
+
|
|
515
|
+
if l.thermalResistance() < 0.001 or l.thermalResistance() < res["r"]:
|
|
516
|
+
i += 1
|
|
517
|
+
continue
|
|
518
|
+
else:
|
|
519
|
+
res["r" ] = m.thermalResistance()
|
|
520
|
+
res["index"] = i
|
|
521
|
+
res["type" ] = "massless"
|
|
522
|
+
|
|
523
|
+
if l.to_StandardOpaqueMaterial():
|
|
524
|
+
l = l.to_StandardOpaqueMaterial().get()
|
|
525
|
+
k = l.thermalConductivity()
|
|
526
|
+
d = l.thickness()
|
|
527
|
+
|
|
528
|
+
if (d < 0.003) or (k > 3.0) or (d / k < res["r"]):
|
|
529
|
+
i += 1
|
|
530
|
+
continue
|
|
531
|
+
else:
|
|
532
|
+
res["r" ] = d / k
|
|
533
|
+
res["index"] = i
|
|
534
|
+
res["type" ] = "standard"
|
|
535
|
+
|
|
536
|
+
i += 1
|
|
537
|
+
|
|
538
|
+
return res
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def isUniqueMaterial(m=None) -> bool:
|
|
542
|
+
"""Validates whether a material is both uniquely reserved to a single
|
|
543
|
+
layered construction in a model, and referenced only once in the
|
|
544
|
+
construction. Limited to 'standard' or 'massless' materials.
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
m (openStudio.model.OpaqueMaterial):
|
|
548
|
+
an OpenStudio opaque material
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
True: Whether material is unique.
|
|
552
|
+
False: If material is missing.
|
|
553
|
+
|
|
554
|
+
"""
|
|
555
|
+
mth = "osut.isUniqueMaterial"
|
|
556
|
+
cl = openstudio.model.OpaqueMaterial
|
|
557
|
+
|
|
558
|
+
if not isinstance(m, cl):
|
|
559
|
+
return oslg.mismatch("material", m, cl, mth, CN.DBG, False)
|
|
560
|
+
|
|
561
|
+
num = 0
|
|
562
|
+
lcs = m.model().getLayeredConstructions()
|
|
563
|
+
|
|
564
|
+
if m.to_MasslessOpaqueMaterial():
|
|
565
|
+
m = m.to_MasslessOpaqueMaterial().get()
|
|
566
|
+
|
|
567
|
+
for lc in lcs:
|
|
568
|
+
num += lc.getLayerIndices(m).size()
|
|
569
|
+
|
|
570
|
+
if num == 1: return True
|
|
571
|
+
|
|
572
|
+
if m.to_StandardOpaqueMaterial():
|
|
573
|
+
m = m.to_StandardOpaqueMaterial().get()
|
|
574
|
+
|
|
575
|
+
for lc in lcs:
|
|
576
|
+
num += lc.getLayerIndices(m).size()
|
|
577
|
+
|
|
578
|
+
if num == 1: return True
|
|
579
|
+
|
|
580
|
+
return False
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def assignUniqueMaterial(lc=None, index=None) -> bool:
|
|
584
|
+
"""Sets a layered construction material as unique. Solution similar to
|
|
585
|
+
OpenStudio::Model::LayeredConstruction's 'ensureUniqueLayers', yet limited
|
|
586
|
+
here to a single indexed OpenStudio material, typically the principal
|
|
587
|
+
insulating material. Returns true if the indexed material is already unique.
|
|
588
|
+
Limited to 'standard' or 'massless' materials.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
lc (OpenStudio::Model::LayeredConstruction):
|
|
592
|
+
A construction.
|
|
593
|
+
index:
|
|
594
|
+
The construction layer index of the material.
|
|
595
|
+
|
|
596
|
+
Returns:
|
|
597
|
+
True: If assigned as unique.
|
|
598
|
+
None: If invalid inputs (see logs).
|
|
599
|
+
|
|
600
|
+
"""
|
|
601
|
+
mth = "osut.assignUniqueMaterial"
|
|
602
|
+
cl = openstudio.model.LayeredConstruction
|
|
603
|
+
|
|
604
|
+
if not isinstance(lc, cl):
|
|
605
|
+
return oslg.mismatch("construction", lc, cl, mth, CN.DBG, False)
|
|
606
|
+
|
|
607
|
+
try:
|
|
608
|
+
index = int(index)
|
|
609
|
+
except:
|
|
610
|
+
return oslg.mismatch("index", index, int, mth, CN.DBG, False)
|
|
611
|
+
|
|
612
|
+
if index < 0 or index > lc.numLayers() - 1:
|
|
613
|
+
return oslg.invalid("index", mth, 0, CN.DBG, False)
|
|
614
|
+
|
|
615
|
+
m = lc.getLayer(index)
|
|
616
|
+
|
|
617
|
+
if m.to_MasslessOpaqueMaterial():
|
|
618
|
+
m = m.to_MasslessOpaqueMaterial().get()
|
|
619
|
+
|
|
620
|
+
if isUniqueMaterial(m): return True
|
|
621
|
+
|
|
622
|
+
mat = m.clone(m.model()).to_MasslessOpaqueMaterial().get()
|
|
623
|
+
return lc.setLayer(index, mat)
|
|
624
|
+
|
|
625
|
+
if m.to_StandardOpaqueMaterial():
|
|
626
|
+
m = m.to_StandardOpaqueMaterial().get()
|
|
627
|
+
|
|
628
|
+
if isUniqueMaterial(m): return True
|
|
629
|
+
|
|
630
|
+
mat = m.clone(m.model()).to_StandardOpaqueMaterial().get()
|
|
631
|
+
|
|
632
|
+
return False
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
def resetUo(lc=None, film=None, index=None, uo=None, uniq=False) -> float:
|
|
636
|
+
"""Resets a construction's Uo factor by adjusting its insulating layer
|
|
637
|
+
thermal conductivity, then if needed its thickness (or its RSi value if
|
|
638
|
+
massless). Unless material uniquness is requested, a matching material is
|
|
639
|
+
recovered instead of instantiating a new one. The latter is renamed
|
|
640
|
+
according to its adjusted conductivity/thickness (or RSi value).
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
lc (OpenStudio::Model::LayeredConstruction):
|
|
644
|
+
A construction.
|
|
645
|
+
film (float):
|
|
646
|
+
The construction air film resistance.
|
|
647
|
+
index (int):
|
|
648
|
+
The insulating layer's array index.
|
|
649
|
+
uo (float):
|
|
650
|
+
Desired Uo factor (with air film resistance).
|
|
651
|
+
uniq (bool):
|
|
652
|
+
Whether to enforce material uniqueness.
|
|
653
|
+
|
|
654
|
+
Returns:
|
|
655
|
+
float: New layer RSi [CN.RMIN, CN.RMAX].
|
|
656
|
+
0.0: If invalid inputs (see logs).
|
|
657
|
+
|
|
658
|
+
"""
|
|
659
|
+
mth = "osut.resetUo"
|
|
660
|
+
r = 0.0 # thermal resistance of new material
|
|
661
|
+
cl = openstudio.model.LayeredConstruction
|
|
662
|
+
|
|
663
|
+
if not isinstance(lc, cl):
|
|
664
|
+
return oslg.mismatch("construction", lc, cl, mth, CN.DBG, r)
|
|
665
|
+
if not isinstance(uniq, bool):
|
|
666
|
+
uniq = False
|
|
667
|
+
|
|
668
|
+
try:
|
|
669
|
+
film = float(film)
|
|
670
|
+
except:
|
|
671
|
+
return oslg.mismatch("film", film, float, mth, CN.DBG, r)
|
|
672
|
+
|
|
673
|
+
try:
|
|
674
|
+
index = int(index)
|
|
675
|
+
except:
|
|
676
|
+
return oslg.mismatch("index", index, int, mth, CN.DBG, r)
|
|
677
|
+
|
|
678
|
+
try:
|
|
679
|
+
uo = float(uo)
|
|
680
|
+
except:
|
|
681
|
+
return oslg.mismatch("uo", uo, float, mth, CN.DBG, r)
|
|
682
|
+
|
|
683
|
+
if film < 0:
|
|
684
|
+
return oslg.negative("film", mth, CN.DBG, r)
|
|
685
|
+
if index < 0 or index > lc.numLayers() - 1:
|
|
686
|
+
return oslg.invalid("index", mth, 3, CN.DBG, r)
|
|
687
|
+
if uo < CN.UMIN or uo > CN.UMAX:
|
|
688
|
+
uo = clamp(uo, CN.UMIN, CN.UMAX)
|
|
689
|
+
msg = "Resetting Uo %s to %.3f (%s)" % (lc.nameString(), uo, mth)
|
|
690
|
+
oslg.log(CN.WRN, msg)
|
|
691
|
+
|
|
692
|
+
r0 = rsi(lc, film) # current construction RSi value
|
|
693
|
+
ro = 1 / uo # desired construction RSi value
|
|
694
|
+
dR = ro - r0 # desired increase in construction RSi
|
|
695
|
+
m = lc.getLayer(index)
|
|
696
|
+
|
|
697
|
+
if m.to_MasslessOpaqueMaterial():
|
|
698
|
+
m = m.to_MasslessOpaqueMaterial().get()
|
|
699
|
+
r = m.thermalResistance()
|
|
700
|
+
if round(abs(dR), 2) == 0.00: return r
|
|
701
|
+
|
|
702
|
+
r = clamp(r + dR, RMIN, RMAX)
|
|
703
|
+
id = "OSut:RSi%.2f" % r
|
|
704
|
+
mt = lc.model().getMasslessOpaqueMaterialByName(id)
|
|
705
|
+
|
|
706
|
+
# Existing material?
|
|
707
|
+
if mt:
|
|
708
|
+
mt = mt.get()
|
|
709
|
+
|
|
710
|
+
if round(r, 2) == round(mt.thermalResistance(), 2) and uniq == False:
|
|
711
|
+
lc.setLayer(index, mt)
|
|
712
|
+
return r
|
|
713
|
+
|
|
714
|
+
mt = m.clone(m.model()).to_MasslessOpaqueMaterial().get()
|
|
715
|
+
mt.setName(id)
|
|
716
|
+
|
|
717
|
+
if not mt.setThermalResistance(r):
|
|
718
|
+
oslg.log(CN.WRN, "Failed to reset %s: RSi%.2f (%s)" % (id, r, mth))
|
|
719
|
+
return 0.0
|
|
720
|
+
|
|
721
|
+
lc.setLayer(index, mt)
|
|
722
|
+
return r
|
|
723
|
+
|
|
724
|
+
if m.to_StandardOpaqueMaterial():
|
|
725
|
+
m = m.to_StandardOpaqueMaterial().get()
|
|
726
|
+
r = m.thickness() / m.conductivity()
|
|
727
|
+
if round(abs(dR), 2) == 0.00: return r
|
|
728
|
+
|
|
729
|
+
k = clamp(m.thickness() / (r + dR), CN.KMIN, CN.KMAX)
|
|
730
|
+
d = clamp(k * (r + dR), CN.DMIN, CN.DMAX)
|
|
731
|
+
r = d / k
|
|
732
|
+
id = "OSut:K%.3f:%03d" % (k, d*1000)
|
|
733
|
+
mt = lc.model().getStandardOpaqueMaterialByName(id)
|
|
734
|
+
|
|
735
|
+
# Existing material?
|
|
736
|
+
if mt:
|
|
737
|
+
mt = mt.get()
|
|
738
|
+
rt = mt.thickness() / mt.conductivity()
|
|
739
|
+
|
|
740
|
+
if round(r, 2) == round(rt, 2) and uniq == False:
|
|
741
|
+
lc.setlayer(index, mt)
|
|
742
|
+
return r
|
|
743
|
+
|
|
744
|
+
mt = m.clone(m.model()).to_StandardOpaqueMaterial().get()
|
|
745
|
+
mt.setName(id)
|
|
746
|
+
|
|
747
|
+
if not mt.setThermalConductivity(k):
|
|
748
|
+
oslg.log(CN.WRN, "Failed to reset %s: K%.3f (%s)" % (id, k, mth))
|
|
749
|
+
return 0.0
|
|
750
|
+
|
|
751
|
+
if not mt.setThickness(d):
|
|
752
|
+
d = int(d*1000)
|
|
753
|
+
oslg.log(CN.WRN, "Failed to reset %s: %dmm (%s)" % (id, d, mth))
|
|
754
|
+
return 0.0
|
|
755
|
+
|
|
756
|
+
lc.setLayer(index, mt)
|
|
757
|
+
return r
|
|
758
|
+
|
|
759
|
+
return 0.0
|
|
760
|
+
|
|
761
|
+
|
|
303
762
|
def genConstruction(model=None, specs=dict()):
|
|
304
763
|
"""Generates an OpenStudio multilayered construction, + materials if needed.
|
|
305
764
|
|
|
@@ -345,12 +804,10 @@ def genConstruction(model=None, specs=dict()):
|
|
|
345
804
|
except:
|
|
346
805
|
return oslg.mismatch(id + " Uo", u, float, mth, CN.ERR)
|
|
347
806
|
|
|
348
|
-
if u <
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if u > 5.678:
|
|
353
|
-
return oslg.invalid(id + " Uo (> 5.678)", mth, 2, CN.ERR)
|
|
807
|
+
if u < CN.UMIN or u > CN.UMAX:
|
|
808
|
+
u0 = u
|
|
809
|
+
u = clamp(u0, CN.UMIN, CN.UMAX)
|
|
810
|
+
oslg.log(CN.ERR, "Resetting Uo %.3f to %.3f (%s)" % (u0, u, mth))
|
|
354
811
|
|
|
355
812
|
# Optional specs. Log/reset if invalid.
|
|
356
813
|
if "clad" not in specs: specs["clad" ] = "light" # exterior
|
|
@@ -643,7 +1100,7 @@ def genConstruction(model=None, specs=dict()):
|
|
|
643
1100
|
if u and not a["glazing"]:
|
|
644
1101
|
ro = 1 / u - flm
|
|
645
1102
|
|
|
646
|
-
if ro >
|
|
1103
|
+
if ro > CN.RMIN:
|
|
647
1104
|
if specs["type"] == "door": # 1x layer, adjust conductivity
|
|
648
1105
|
layer = c.getLayer(0).to_StandardOpaqueMaterial()
|
|
649
1106
|
|
|
@@ -653,38 +1110,14 @@ def genConstruction(model=None, specs=dict()):
|
|
|
653
1110
|
layer = layer.get()
|
|
654
1111
|
k = layer.thickness() / ro
|
|
655
1112
|
layer.setConductivity(k)
|
|
656
|
-
|
|
657
|
-
else: # multiple layers, adjust insulating layer thickness
|
|
1113
|
+
else: # multiple layers
|
|
658
1114
|
lyr = insulatingLayer(c)
|
|
659
1115
|
|
|
660
|
-
if not lyr["index"] or not lyr["type"] or
|
|
661
|
-
return oslg.invalid(
|
|
1116
|
+
if not lyr["index"] or not lyr["type"] or round(lyr["r"], 2) == 0:
|
|
1117
|
+
return oslg.invalid(ide + " construction", mth, 0)
|
|
662
1118
|
|
|
663
1119
|
index = lyr["index"]
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
if not layer:
|
|
667
|
-
return oslg.invalid(id + " material %d" % index, mth, 0)
|
|
668
|
-
|
|
669
|
-
layer = layer.get()
|
|
670
|
-
k = layer.conductivity()
|
|
671
|
-
d = (ro - rsi(c) + lyr["r"]) * k
|
|
672
|
-
|
|
673
|
-
if d < 0.03:
|
|
674
|
-
m = id + " adjusted material thickness"
|
|
675
|
-
return oslg.invalid(m, mth, 0)
|
|
676
|
-
|
|
677
|
-
nom = re.sub(r'[^a-zA-Z]', '', layer.nameString())
|
|
678
|
-
nom = re.sub(r'OSut', '', nom)
|
|
679
|
-
nom = "OSut." + nom + ".%03d" % int(d * 1000)
|
|
680
|
-
|
|
681
|
-
if model.getStandardOpaqueMaterialByName(nom):
|
|
682
|
-
omat = model.getStandardOpaqueMaterialByName(nom).get()
|
|
683
|
-
c.setLayer(index, omat)
|
|
684
|
-
else:
|
|
685
|
-
layer.setName(nom)
|
|
686
|
-
layer.setThickness(d)
|
|
687
|
-
|
|
1120
|
+
resetUo(c, flm, index, u)
|
|
688
1121
|
return c
|
|
689
1122
|
|
|
690
1123
|
|
|
@@ -874,7 +1307,7 @@ def genMass(sps=None, ratio=2.0) -> bool:
|
|
|
874
1307
|
return True
|
|
875
1308
|
|
|
876
1309
|
|
|
877
|
-
def holdsConstruction(cset=None, base=None, gr=False, ex=False, type=""):
|
|
1310
|
+
def holdsConstruction(cset=None, base=None, gr=False, ex=False, type="") -> bool:
|
|
878
1311
|
"""Validates whether a default construction set holds a base construction.
|
|
879
1312
|
|
|
880
1313
|
Args:
|
|
@@ -886,7 +1319,7 @@ def holdsConstruction(cset=None, base=None, gr=False, ex=False, type=""):
|
|
|
886
1319
|
Whether ground-facing surface.
|
|
887
1320
|
ex (bool):
|
|
888
1321
|
Whether exterior-facing surface.
|
|
889
|
-
type:
|
|
1322
|
+
type (str):
|
|
890
1323
|
An OpenStudio surface (or sub surface) type (e.g. "Wall").
|
|
891
1324
|
|
|
892
1325
|
Returns:
|
|
@@ -1012,271 +1445,71 @@ def defaultConstructionSet(s=None):
|
|
|
1012
1445
|
|
|
1013
1446
|
ground = True if s.isGroundSurface() else False
|
|
1014
1447
|
exterior = True if bnd == "outdoors" else False
|
|
1448
|
+
adjacent = None
|
|
1449
|
+
aspace = None
|
|
1450
|
+
typ = None
|
|
1451
|
+
|
|
1452
|
+
if s.adjacentSurface():
|
|
1453
|
+
adjacent = s.adjacentSurface().get()
|
|
1454
|
+
typ = adjacent.surfaceType()
|
|
1455
|
+
|
|
1456
|
+
if adjacent.space():
|
|
1457
|
+
aspace = adjacent.space().get()
|
|
1015
1458
|
|
|
1016
1459
|
if space.defaultConstructionSet():
|
|
1017
|
-
|
|
1460
|
+
set = space.defaultConstructionSet().get()
|
|
1018
1461
|
|
|
1019
|
-
if holdsConstruction(
|
|
1462
|
+
if holdsConstruction(set, base, ground, exterior, type): return set
|
|
1463
|
+
elif aspace:
|
|
1464
|
+
if aspace.defaultConstructionSet():
|
|
1465
|
+
set = aspace.defaultConstructionSet().get()
|
|
1466
|
+
|
|
1467
|
+
if holdsConstruction(set, base, ground, exterior, typ): return set
|
|
1020
1468
|
|
|
1021
1469
|
if space.spaceType():
|
|
1022
1470
|
spacetype = space.spaceType().get()
|
|
1023
1471
|
|
|
1024
1472
|
if spacetype.defaultConstructionSet():
|
|
1025
|
-
|
|
1473
|
+
set = spacetype.defaultConstructionSet().get()
|
|
1474
|
+
|
|
1475
|
+
if holdsConstruction(set, base, ground, exterior, type): return set
|
|
1476
|
+
|
|
1477
|
+
if aspace and aspace.spaceType():
|
|
1478
|
+
spacetype = aspace.spaceType().get()
|
|
1479
|
+
|
|
1480
|
+
if spacetype.defaultConstructionSet():
|
|
1481
|
+
set = spacetype.defaultConstructionSet().get()
|
|
1026
1482
|
|
|
1027
|
-
if holdsConstruction(
|
|
1028
|
-
return cset
|
|
1483
|
+
if holdsConstruction(set, base, ground, exterior, typ): return set
|
|
1029
1484
|
|
|
1030
1485
|
if space.buildingStory():
|
|
1031
1486
|
story = space.buildingStory().get()
|
|
1032
1487
|
|
|
1033
1488
|
if story.defaultConstructionSet():
|
|
1034
|
-
|
|
1489
|
+
set = story.defaultConstructionSet().get()
|
|
1490
|
+
|
|
1491
|
+
if holdsConstruction(set, base, ground, exterior, type): return set
|
|
1035
1492
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1493
|
+
if aspace and aspace.buildingStory():
|
|
1494
|
+
story = aspace.buildingStory().get()
|
|
1038
1495
|
|
|
1496
|
+
if story.defaultConstructionSet():
|
|
1497
|
+
set = story.defaultConstructionSet().get()
|
|
1498
|
+
|
|
1499
|
+
if holdsConstruction(set, base, ground, exterior, typ):
|
|
1500
|
+
return set
|
|
1039
1501
|
|
|
1040
1502
|
building = mdl.getBuilding()
|
|
1041
1503
|
|
|
1042
1504
|
if building.defaultConstructionSet():
|
|
1043
|
-
|
|
1505
|
+
set = building.defaultConstructionSet().get()
|
|
1044
1506
|
|
|
1045
|
-
if holdsConstruction(
|
|
1046
|
-
return
|
|
1507
|
+
if holdsConstruction(set, base, ground, exterior, type):
|
|
1508
|
+
return set
|
|
1047
1509
|
|
|
1048
1510
|
return None
|
|
1049
1511
|
|
|
1050
1512
|
|
|
1051
|
-
def areStandardOpaqueLayers(lc=None) -> bool:
|
|
1052
|
-
"""Validates if every material in a layered construction is standard/opaque.
|
|
1053
|
-
|
|
1054
|
-
Args:
|
|
1055
|
-
lc (openstudio.model.LayeredConstruction):
|
|
1056
|
-
an OpenStudio layered construction
|
|
1057
|
-
|
|
1058
|
-
Returns:
|
|
1059
|
-
True: If all layers are valid (standard & opaque).
|
|
1060
|
-
False: If invalid inputs (see logs).
|
|
1061
|
-
|
|
1062
|
-
"""
|
|
1063
|
-
mth = "osut.areStandardOpaqueLayers"
|
|
1064
|
-
cl = openstudio.model.LayeredConstruction
|
|
1065
|
-
|
|
1066
|
-
if not isinstance(lc, cl):
|
|
1067
|
-
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, 0.0)
|
|
1068
|
-
|
|
1069
|
-
for m in lc.layers():
|
|
1070
|
-
if not m.to_StandardOpaqueMaterial(): return False
|
|
1071
|
-
|
|
1072
|
-
return True
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
def thickness(lc=None) -> float:
|
|
1076
|
-
"""Returns total (standard opaque) layered construction thickness (m).
|
|
1077
|
-
|
|
1078
|
-
Args:
|
|
1079
|
-
lc (openstudio.model.LayeredConstruction):
|
|
1080
|
-
an OpenStudio layered construction
|
|
1081
|
-
|
|
1082
|
-
Returns:
|
|
1083
|
-
float: A standard opaque construction thickness.
|
|
1084
|
-
0.0: If invalid inputs (see logs).
|
|
1085
|
-
|
|
1086
|
-
"""
|
|
1087
|
-
mth = "osut.thickness"
|
|
1088
|
-
cl = openstudio.model.LayeredConstruction
|
|
1089
|
-
d = 0.0
|
|
1090
|
-
|
|
1091
|
-
if not isinstance(lc, cl):
|
|
1092
|
-
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, 0.0)
|
|
1093
|
-
if not areStandardOpaqueLayers(lc):
|
|
1094
|
-
oslg.log(CN.ERR, "holding non-StandardOpaqueMaterial(s) %s" % mth)
|
|
1095
|
-
return d
|
|
1096
|
-
|
|
1097
|
-
for m in lc.layers(): d += m.thickness()
|
|
1098
|
-
|
|
1099
|
-
return d
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
def glazingAirFilmRSi(usi=5.85) -> float:
|
|
1103
|
-
"""Returns total air film resistance of a fenestrated construction (m2•K/W).
|
|
1104
|
-
|
|
1105
|
-
Args:
|
|
1106
|
-
usi (float):
|
|
1107
|
-
A fenestrated construction's U-factor (W/m2•K).
|
|
1108
|
-
|
|
1109
|
-
Returns:
|
|
1110
|
-
float: Total air film resistances.
|
|
1111
|
-
0.1216: If invalid input (see logs).
|
|
1112
|
-
|
|
1113
|
-
"""
|
|
1114
|
-
# The sum of thermal resistances of calculated exterior and interior film
|
|
1115
|
-
# coefficients under standard winter conditions are taken from:
|
|
1116
|
-
#
|
|
1117
|
-
# https://bigladdersoftware.com/epx/docs/9-6/engineering-reference/
|
|
1118
|
-
# window-calculation-module.html#simple-window-model
|
|
1119
|
-
#
|
|
1120
|
-
# These remain acceptable approximations for flat windows, yet likely
|
|
1121
|
-
# unsuitable for subsurfaces with curved or projecting shapes like domed
|
|
1122
|
-
# skylights. The solution here is considered an adequate fix for reporting,
|
|
1123
|
-
# awaiting eventual OpenStudio (and EnergyPlus) upgrades to report NFRC 100
|
|
1124
|
-
# (or ISO) air film resistances under standard winter conditions.
|
|
1125
|
-
#
|
|
1126
|
-
# For U-factors above 8.0 W/m2•K (or invalid input), the function returns
|
|
1127
|
-
# 0.1216 m2•K/W, which corresponds to a construction with a single glass
|
|
1128
|
-
# layer thickness of 2mm & k = ~0.6 W/m.K.
|
|
1129
|
-
#
|
|
1130
|
-
# The EnergyPlus Engineering calculations were designed for vertical
|
|
1131
|
-
# windows, not for horizontal, slanted or domed surfaces - use with caution.
|
|
1132
|
-
mth = "osut.glazingAirFilmRSi"
|
|
1133
|
-
val = 0.1216
|
|
1134
|
-
|
|
1135
|
-
try:
|
|
1136
|
-
usi = float(usi)
|
|
1137
|
-
except:
|
|
1138
|
-
return oslg.mismatch("usi", usi, float, mth, CN.DBG, val)
|
|
1139
|
-
|
|
1140
|
-
if usi > 8.0:
|
|
1141
|
-
return oslg.invalid("usi", mth, 1, CN.WRN, val)
|
|
1142
|
-
elif usi < 0:
|
|
1143
|
-
return oslg.negative("usi", mth, CN.WRN, val)
|
|
1144
|
-
elif abs(usi) < CN.TOL:
|
|
1145
|
-
return oslg.zero("usi", mth, CN.WRN, val)
|
|
1146
|
-
|
|
1147
|
-
rsi = 1 / (0.025342 * usi + 29.163853) # exterior film, next interior film
|
|
1148
|
-
|
|
1149
|
-
if usi < 5.85:
|
|
1150
|
-
return rsi + 1 / (0.359073 * math.log(usi) + 6.949915)
|
|
1151
|
-
|
|
1152
|
-
return rsi + 1 / (1.788041 * usi - 2.886625)
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
def rsi(lc=None, film=0.0, t=0.0) -> float:
|
|
1156
|
-
"""Returns a construction's 'standard calc' thermal resistance (m2•K/W),
|
|
1157
|
-
which includes air film resistances. It excludes insulating effects of
|
|
1158
|
-
shades, screens, etc. in the case of fenestrated constructions. Adapted
|
|
1159
|
-
from BTAP's 'Material' Module "get_conductance" (P. Lopez).
|
|
1160
|
-
|
|
1161
|
-
Args:
|
|
1162
|
-
lc (openstudio.model.LayeredConstruction):
|
|
1163
|
-
an OpenStudio layered construction
|
|
1164
|
-
film (float):
|
|
1165
|
-
thermal resistance of surface air films (m2•K/W)
|
|
1166
|
-
t (float):
|
|
1167
|
-
gas temperature (°C) (optional)
|
|
1168
|
-
|
|
1169
|
-
Returns:
|
|
1170
|
-
float: A layered construction's thermal resistance.
|
|
1171
|
-
0.0: If invalid input (see logs).
|
|
1172
|
-
|
|
1173
|
-
"""
|
|
1174
|
-
mth = "osut.rsi"
|
|
1175
|
-
cl = openstudio.model.LayeredConstruction
|
|
1176
|
-
|
|
1177
|
-
if not isinstance(lc, cl):
|
|
1178
|
-
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, 0.0)
|
|
1179
|
-
|
|
1180
|
-
try:
|
|
1181
|
-
film = float(film)
|
|
1182
|
-
except:
|
|
1183
|
-
return oslg.mismatch("film", film, float, mth, CN.DBG, 0.0)
|
|
1184
|
-
|
|
1185
|
-
try:
|
|
1186
|
-
t = float(t)
|
|
1187
|
-
except:
|
|
1188
|
-
return oslg.mismatch("temp K", t, float, mth, CN.DBG, 0.0)
|
|
1189
|
-
|
|
1190
|
-
t += 273.0 # °C to K
|
|
1191
|
-
|
|
1192
|
-
if t < 0:
|
|
1193
|
-
return oslg.negative("temp K", mth, CN.ERR, 0.0)
|
|
1194
|
-
if film < 0:
|
|
1195
|
-
return oslg.negative("film", mth, CN.ERR, 0.0)
|
|
1196
|
-
|
|
1197
|
-
rsi = film
|
|
1198
|
-
|
|
1199
|
-
for m in lc.layers():
|
|
1200
|
-
if m.to_SimpleGlazing():
|
|
1201
|
-
return 1 / m.to_SimpleGlazing().get().uFactor()
|
|
1202
|
-
elif m.to_StandardGlazing():
|
|
1203
|
-
rsi += m.to_StandardGlazing().get().thermalResistance()
|
|
1204
|
-
elif m.to_RefractionExtinctionGlazing():
|
|
1205
|
-
rsi += m.to_RefractionExtinctionGlazing().get().thermalResistance()
|
|
1206
|
-
elif m.to_Gas():
|
|
1207
|
-
rsi += m.to_Gas().get().getThermalResistance(t)
|
|
1208
|
-
elif m.to_GasMixture():
|
|
1209
|
-
rsi += m.to_GasMixture().get().getThermalResistance(t)
|
|
1210
|
-
|
|
1211
|
-
# Opaque materials next.
|
|
1212
|
-
if m.to_StandardOpaqueMaterial():
|
|
1213
|
-
rsi += m.to_StandardOpaqueMaterial().get().thermalResistance()
|
|
1214
|
-
elif m.to_MasslessOpaqueMaterial():
|
|
1215
|
-
rsi += m.to_MasslessOpaqueMaterial()
|
|
1216
|
-
elif m.to_RoofVegetation():
|
|
1217
|
-
rsi += m.to_RoofVegetation().get().thermalResistance()
|
|
1218
|
-
elif m.to_AirGap():
|
|
1219
|
-
rsi += m.to_AirGap().get().thermalResistance()
|
|
1220
|
-
|
|
1221
|
-
return rsi
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
def insulatingLayer(lc=None) -> dict:
|
|
1225
|
-
"""Identifies a layered construction's (opaque) insulating layer.
|
|
1226
|
-
|
|
1227
|
-
Args:
|
|
1228
|
-
lc (openStudio.model.LayeredConstruction):
|
|
1229
|
-
an OpenStudio layered construction
|
|
1230
|
-
|
|
1231
|
-
Returns:
|
|
1232
|
-
An insulating-layer dictionary:
|
|
1233
|
-
- "index" (int): construction's insulating layer index [0, n layers)
|
|
1234
|
-
- "type" (str): layer material type ("standard" or "massless")
|
|
1235
|
-
- "r" (float): material thermal resistance in m2•K/W.
|
|
1236
|
-
If unsuccessful, dictionary is voided as follows (see logs):
|
|
1237
|
-
"index": None
|
|
1238
|
-
"type": None
|
|
1239
|
-
"r": 0.0
|
|
1240
|
-
|
|
1241
|
-
"""
|
|
1242
|
-
mth = "osut.insulatingLayer"
|
|
1243
|
-
cl = openstudio.model.LayeredConstruction
|
|
1244
|
-
res = dict(index=None, type=None, r=0.0)
|
|
1245
|
-
i = 0 # iterator
|
|
1246
|
-
|
|
1247
|
-
if not isinstance(lc, cl):
|
|
1248
|
-
return oslg.mismatch("lc", lc, cl, mth, CN.DBG, res)
|
|
1249
|
-
|
|
1250
|
-
for l in lc.layers():
|
|
1251
|
-
if l.to_MasslessOpaqueMaterial():
|
|
1252
|
-
l = l.to_MasslessOpaqueMaterial().get()
|
|
1253
|
-
|
|
1254
|
-
if l.thermalResistance() < 0.001 or l.thermalResistance() < res["r"]:
|
|
1255
|
-
i += 1
|
|
1256
|
-
continue
|
|
1257
|
-
else:
|
|
1258
|
-
res["r" ] = m.thermalResistance()
|
|
1259
|
-
res["index"] = i
|
|
1260
|
-
res["type" ] = "massless"
|
|
1261
|
-
|
|
1262
|
-
if l.to_StandardOpaqueMaterial():
|
|
1263
|
-
l = l.to_StandardOpaqueMaterial().get()
|
|
1264
|
-
k = l.thermalConductivity()
|
|
1265
|
-
d = l.thickness()
|
|
1266
|
-
|
|
1267
|
-
if (d < 0.003) or (k > 3.0) or (d / k < res["r"]):
|
|
1268
|
-
i += 1
|
|
1269
|
-
continue
|
|
1270
|
-
else:
|
|
1271
|
-
res["r" ] = d / k
|
|
1272
|
-
res["index"] = i
|
|
1273
|
-
res["type" ] = "standard"
|
|
1274
|
-
|
|
1275
|
-
i += 1
|
|
1276
|
-
|
|
1277
|
-
return res
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
1513
|
def areSpandrels(surfaces=None) -> bool:
|
|
1281
1514
|
"""Validates whether one or more opaque surface(s) can be considered as
|
|
1282
1515
|
curtain wall (or similar technology) spandrels, regardless of construction
|
|
@@ -1289,6 +1522,7 @@ def areSpandrels(surfaces=None) -> bool:
|
|
|
1289
1522
|
Returns:
|
|
1290
1523
|
bool: Whether surface(s) can be considered 'spandrels'.
|
|
1291
1524
|
False: If invalid input (see logs).
|
|
1525
|
+
|
|
1292
1526
|
"""
|
|
1293
1527
|
mth = "osut.areSpandrels"
|
|
1294
1528
|
cl = openstudio.model.Surface
|
|
@@ -1384,7 +1618,7 @@ def isFenestrated(s=None) -> bool:
|
|
|
1384
1618
|
# - UNCONDITIONED space: an ENCLOSED space that is NOT a conditioned
|
|
1385
1619
|
# space or a SEMIHEATED space (see above).
|
|
1386
1620
|
#
|
|
1387
|
-
#
|
|
1621
|
+
# Note: Crawlspaces, attics, and parking garages with natural or
|
|
1388
1622
|
# mechanical ventilation are considered UNENCLOSED spaces.
|
|
1389
1623
|
#
|
|
1390
1624
|
# 2.3.3 Modeling Requirements: surfaces adjacent to UNENCLOSED spaces
|
|
@@ -5320,7 +5554,7 @@ def spaceWidth(space=None) -> float:
|
|
|
5320
5554
|
polyg = list(polyg)
|
|
5321
5555
|
polyg.reverse()
|
|
5322
5556
|
|
|
5323
|
-
res = realignedFace(polyg)
|
|
5557
|
+
res = realignedFace(polyg, True)
|
|
5324
5558
|
if not res["box"]: return 0
|
|
5325
5559
|
|
|
5326
5560
|
# A bounded box's 'height', at its narrowest, is its 'width'.
|
|
@@ -5380,7 +5614,7 @@ def genAnchors(s=None, sset=[], tag="box") -> int:
|
|
|
5380
5614
|
# Validate individual subsets. Purge surface-specific leader line anchors.
|
|
5381
5615
|
for i, st in enumerate(sset):
|
|
5382
5616
|
str1 = ide + "subset %d" % (i+1)
|
|
5383
|
-
str2 = str1 + " %s" %
|
|
5617
|
+
str2 = str1 + " %s" % oslg.trim(tag)
|
|
5384
5618
|
|
|
5385
5619
|
if not isinstance(st, dict):
|
|
5386
5620
|
return oslg.mismatch(str1, st, dict, mth, CN.DBG, n)
|
|
@@ -5559,7 +5793,7 @@ def genExtendedVertices(s=None, sset=[], tag="vtx") -> openstudio.Point3dVector:
|
|
|
5559
5793
|
# Validate individual subsets.
|
|
5560
5794
|
for i, st in enumerate(sset):
|
|
5561
5795
|
str1 = ide + "subset %d" % (i+1)
|
|
5562
|
-
str2 = str1 + " %s" %
|
|
5796
|
+
str2 = str1 + " %s" % oslg.trim(tag)
|
|
5563
5797
|
|
|
5564
5798
|
if not isinstance(st, dict):
|
|
5565
5799
|
return oslg.mismatch(str1, st, dict, mth, CN.DBG, a)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: osut
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: OpenStudio SDK utilities for Python
|
|
5
5
|
Author-email: Denis Bourgeois <denis@rd2.ca>
|
|
6
6
|
Maintainer-email: Denis Bourgeois <denis@rd2.ca>
|
|
@@ -19,7 +19,7 @@ Requires-Dist: oslg
|
|
|
19
19
|
Dynamic: license-file
|
|
20
20
|
|
|
21
21
|
# pyOSut
|
|
22
|
-
Python implementation of the
|
|
22
|
+
Python implementation of the _OSut_ Ruby gem for the OpenStudio SDK.
|
|
23
23
|
|
|
24
24
|
- PyPi [package](https://pypi.org/project/osut/)
|
|
25
25
|
- Ruby [gem](https://rubygems.org/gems/osut)
|
|
@@ -45,6 +45,6 @@ To import the _OSut_ module in a Python project:
|
|
|
45
45
|
|
|
46
46
|
____
|
|
47
47
|
|
|
48
|
-
To run the _OSut_ unit tests on a `git clone` of the
|
|
48
|
+
To run the _OSut_ unit tests on a `git clone` of the repo:
|
|
49
49
|
|
|
50
50
|
`python -m unittest`
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
osut/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
osut/osut.py,sha256=CaWim0LjjbtUK3L1tS2VHdZ-zo_AAIDbZEgLrXkMung,310282
|
|
3
|
+
osut-0.8.2.dist-info/licenses/LICENSE,sha256=1fpl5h5cQqIU55E156I1kBIko9NJjDmEw3e-ysLhg3Y,1495
|
|
4
|
+
osut-0.8.2.dist-info/METADATA,sha256=30uZ9aAZVGdY0nO3x6xRDJ9Vmk-2jbqbq22iB2OibHQ,1252
|
|
5
|
+
osut-0.8.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
6
|
+
osut-0.8.2.dist-info/top_level.txt,sha256=elxZoPwvGd11mNFvZvnG07mjsDiiiiU2VwmzXjnSWT4,5
|
|
7
|
+
osut-0.8.2.dist-info/RECORD,,
|
osut-0.7.0.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
osut/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
osut/osut.py,sha256=YyaK3ft1WRnPrAGkNGeTCA48ya_TapHccQkAmLm20M4,302755
|
|
3
|
-
osut-0.7.0.dist-info/licenses/LICENSE,sha256=Ag_zDZp4XtiEQWfCwuPk25nVa9gsNhJ3SIx2Ahh1I6c,1495
|
|
4
|
-
osut-0.7.0.dist-info/METADATA,sha256=6l48aPzuzXNVJXckxyvtEuq5Sl7g2M5GSAT2CSm8kzA,1254
|
|
5
|
-
osut-0.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
-
osut-0.7.0.dist-info/top_level.txt,sha256=elxZoPwvGd11mNFvZvnG07mjsDiiiiU2VwmzXjnSWT4,5
|
|
7
|
-
osut-0.7.0.dist-info/RECORD,,
|
|
File without changes
|