Skip to content

Modules

Backporter

KNOWN_TO_VERSIONS = ['3.8.0', '3.9.0', '3.10.0'] module-attribute

List of all known versions that can be translated to.

Backporter

Source code in openstudiobackporter/backporter.py
32
33
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
class Backporter:

    def __init__(self, to_version: str, save_intermediate: bool = False):
        if to_version not in VERSION_TRANSLATION_MAP:
            raise ValueError(f"Unsupported target version: {to_version}")

        self.to_version = openstudio.VersionString(to_version)
        self.to_version_str = to_version
        self.save_intermediate = save_intermediate
        self.osm_path = Path("in.osm")

    def backport_file(self, osm_path: Path) -> openstudio.IdfFile:
        """Backport an OpenStudio Model (OSM) file to an earlier version."""
        if not osm_path.exists():
            raise FileNotFoundError(f"The specified OSM file does not exist: {osm_path}")

        self.osm_path = osm_path
        version = openstudio.IdfFile.loadVersionOnly(osm_path)
        if not version.is_initialized():
            raise ValueError(f"Could not determine version of the input OSM file: {osm_path}")
        version = version.get()
        logger.warning(f"Detected OSM version: {version.str()}")
        print(version.str())
        if version <= self.to_version:
            raise ValueError(
                f"The input OSM file version {version.str()} is not newer "
                f"than the target version {self.to_version_str}"
            )

        # Do not try to load from openstudio.model.Model.load since it will fail for any older version rather than the
        # current one.
        ori_idd = openstudio.IddFactory.instance().getIddFile(openstudio.IddFileType("OpenStudio"), version).get()

        idf_file = openstudio.IdfFile.load(osm_path, ori_idd)
        if not idf_file.is_initialized():
            raise ValueError(f"Failed to load model from '{osm_path}'")
        idf_file = idf_file.get()
        return self.backport(idf_file=idf_file)

    def backport(self, idf_file: openstudio.IdfFile) -> openstudio.IdfFile:
        """Backport an OpenStudio Model as an IdfFile to an earlier version."""

        version = idf_file.version()
        if version <= self.to_version:
            raise ValueError(
                f"The input OSM file version {version.str()} is not newer "
                f"than the target version {self.to_version_str}"
            )

        while version != self.to_version:
            version_str = normalize_version(version=version)
            if version_str not in VERSION_TRANSLATION_MAP:
                raise ValueError(f"No translation available from version {version_str} to {self.to_version_str}")
            logger.debug(f"Translating input data from version {version_str}")
            translator = VERSION_TRANSLATION_MAP[version_str]
            # Translators translate the data in place so no need to reassign.
            idf_file = translator(idf_file)
            version = idf_file.version()
            if self.save_intermediate:
                intermediate_path = self.osm_path.with_name(f"{self.osm_path.stem}_backported_to_{version.str()}.osm")
                idf_file.save(intermediate_path, True)
                logger.info(f"Saved intermediate backported file to '{intermediate_path}'")

        return idf_file

backport(idf_file: openstudio.IdfFile) -> openstudio.IdfFile

Backport an OpenStudio Model as an IdfFile to an earlier version.

Source code in openstudiobackporter/backporter.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
def backport(self, idf_file: openstudio.IdfFile) -> openstudio.IdfFile:
    """Backport an OpenStudio Model as an IdfFile to an earlier version."""

    version = idf_file.version()
    if version <= self.to_version:
        raise ValueError(
            f"The input OSM file version {version.str()} is not newer "
            f"than the target version {self.to_version_str}"
        )

    while version != self.to_version:
        version_str = normalize_version(version=version)
        if version_str not in VERSION_TRANSLATION_MAP:
            raise ValueError(f"No translation available from version {version_str} to {self.to_version_str}")
        logger.debug(f"Translating input data from version {version_str}")
        translator = VERSION_TRANSLATION_MAP[version_str]
        # Translators translate the data in place so no need to reassign.
        idf_file = translator(idf_file)
        version = idf_file.version()
        if self.save_intermediate:
            intermediate_path = self.osm_path.with_name(f"{self.osm_path.stem}_backported_to_{version.str()}.osm")
            idf_file.save(intermediate_path, True)
            logger.info(f"Saved intermediate backported file to '{intermediate_path}'")

    return idf_file

backport_file(osm_path: Path) -> openstudio.IdfFile

Backport an OpenStudio Model (OSM) file to an earlier version.

Source code in openstudiobackporter/backporter.py
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
def backport_file(self, osm_path: Path) -> openstudio.IdfFile:
    """Backport an OpenStudio Model (OSM) file to an earlier version."""
    if not osm_path.exists():
        raise FileNotFoundError(f"The specified OSM file does not exist: {osm_path}")

    self.osm_path = osm_path
    version = openstudio.IdfFile.loadVersionOnly(osm_path)
    if not version.is_initialized():
        raise ValueError(f"Could not determine version of the input OSM file: {osm_path}")
    version = version.get()
    logger.warning(f"Detected OSM version: {version.str()}")
    print(version.str())
    if version <= self.to_version:
        raise ValueError(
            f"The input OSM file version {version.str()} is not newer "
            f"than the target version {self.to_version_str}"
        )

    # Do not try to load from openstudio.model.Model.load since it will fail for any older version rather than the
    # current one.
    ori_idd = openstudio.IddFactory.instance().getIddFile(openstudio.IddFileType("OpenStudio"), version).get()

    idf_file = openstudio.IdfFile.load(osm_path, ori_idd)
    if not idf_file.is_initialized():
        raise ValueError(f"Failed to load model from '{osm_path}'")
    idf_file = idf_file.get()
    return self.backport(idf_file=idf_file)

normalize_version(version: openstudio.VersionString) -> str

Normalize a version string to the format 'X.Y.Z'.

Source code in openstudiobackporter/backporter.py
27
28
29
def normalize_version(version: openstudio.VersionString) -> str:
    """Normalize a version string to the format 'X.Y.Z'."""
    return f"{version.major()}.{version.minor()}.{version.patch().value_or(0)}"

run_translation_noop(idf_file: openstudio.IdfFile) -> openstudio.IdfFile

No-op translation function.

Source code in openstudiobackporter/backporter.py
 9
10
11
def run_translation_noop(idf_file: openstudio.IdfFile) -> openstudio.IdfFile:
    """No-op translation function."""
    return idf_file

Individual translations

run_translation(idf_3_11_0: openstudio.IdfFile) -> openstudio.IdfFile

Backport an IdfFile from 3.11.0 to 3.10.0.

Source code in openstudiobackporter/backport_3_11_0_to_3_10_0.py
 33
 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
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
def run_translation(idf_3_11_0: openstudio.IdfFile) -> openstudio.IdfFile:
    """Backport an IdfFile from 3.11.0 to 3.10.0."""
    logger.info("Backporting from 3.11.0 to 3.10.0")

    idd_3_10_0 = (
        openstudio.IddFactory.instance()
        .getIddFile(openstudio.IddFileType("OpenStudio"), openstudio.VersionString(3, 10, 0))
        .get()
    )
    targetIdf = openstudio.IdfFile(idd_3_10_0)

    for obj in idf_3_11_0.objects():
        iddname = obj.iddObject().name()

        iddObject_ = idd_3_10_0.getObject(iddname)
        if not iddObject_.is_initialized():
            # Object type doesn't exist in target version, skip it
            logger.warning(f"{brief_description(idf_obj=obj)} does not exist in version 3.10.0, skipping.")
            continue

        iddObject = iddObject_.get()
        newObject = openstudio.IdfObject(iddObject)

        if iddname in COILS_WITH_AVAIL_SCHED:
            # 1 Field has been inserted from 3.10.0 to 3.11.0:
            # ----------------------------------------------
            # * Availability Schedule Name * 2

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={2})
            targetIdf.addObject(newObject)

        elif iddname == "OS:Site:WaterMainsTemperature":
            # 2 Fields have been inserted (at end) from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * Temperature Multiplier * 5
            # * Temperature Offset * 6

            # copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={5, 6})
            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=5)
            targetIdf.addObject(newObject)

        elif iddname == "OS:ThermalStorage:ChilledWater:Stratified":
            # 1 Field was made required from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * Nominal Cooling Capacity * 10
            # But we don't care about this change, so just copy as is

            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

            # Note: we added a WaterHeater:Sizing object for it though, this we'll delete

        elif iddname == "OS:WaterHeater:Sizing":
            wh_ = get_target(idf_file=idf_3_11_0, idf_obj=obj, index=1)
            if wh_.is_initialized():  # pragma: no cover
                whIddObject = wh_.get().iddObject()
                if whIddObject.name() == "OS:ThermalStorage:ChilledWater:Stratified":
                    # skip this object
                    continue

            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

        elif iddname == "OS:Sizing:Zone":
            # 2 Fields have been added (at end) from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * Heating Coil Sizing Method * 40
            # * Maximum Heating Capacity To Cooling Load Sizing Ratio * 41

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=40)
            targetIdf.addObject(newObject)

        elif iddname == "OS:Sizing:System":
            # 2 Fields have been added (at end) from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * Heating Coil Sizing Method * 39
            # * Maximum Heating Capacity To Cooling Capacity Sizing Ratio * 40

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=39)
            targetIdf.addObject(newObject)

        elif iddname == "OS:HeatPump:AirToWater:FuelFired:Heating":
            # 1 Field has been added (at end) from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * Minimum Unloading Ratio * 32

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=32)
            targetIdf.addObject(newObject)

        elif iddname == "OS:HeatPump:AirToWater:FuelFired:Cooling":
            # 1 Field has been added (at end) from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * Minimum Unloading Ratio * 27

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=27)
            targetIdf.addObject(newObject)

        elif iddname in DX_HEATING_COIL_SIZING_RATIOS:
            # 1 Field has been added (at end) from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * DX Heating Coil Sizing Ratio * variable

            cut_off = DX_HEATING_COIL_SIZING_RATIOS[iddname]

            if iddname == "OS:AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed":
                # This is the only field where it wasn't added at the end... Here Field 10 was changed from
                # Minimum Outdoor Dry-Bulb Temperature for Compressor Operation to
                # DX Heating Coil Sizing Ratio, so we need to skip it there
                copy_object_as_is(obj=obj, newObject=newObject)
                # Information is gone, so we reset to the OS SDK Ctor default from v3.10.0
                newObject.setDouble(cut_off, -8.0)
            else:
                copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=cut_off)

            targetIdf.addObject(newObject)

        elif iddname == "OS:Controller:MechanicalVentilation":

            # 1 Field has been modified from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * System Outdoor Air Method * 4 - Removed ProportionalControl
            # as mapping to ProportionalControlBasedonOccupancySchedule
            # Don't care
            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

        elif iddname == "OS:People":
            # 2 Fields have been inserted from 3.10.0 to 3.11.0:
            # ------------------------------------------------
            # * Clothing Insulation Calculation Method * 8
            # * Clothing Insulation Calculation Method Schedule Name * 9

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={8, 9})
            targetIdf.addObject(newObject)

        elif iddname == "OS:EvaporativeFluidCooler:SingleSpeed":
            # 1 Field has been inserted from 3.10.0 to 3.10.1:
            # ------------------------------------------------
            # * Heat Rejection Capacity and Nominal Capacity Sizing Ratio * 9

            # 3 Fields were made required from 3.10.0 to 3.10.1:
            # ------------------------------------------------
            # * Design Entering Water Temperature * 14
            # * Design Entering Air Temperature * 15
            # * Design Entering Air Wet-bulb Temperature * 16
            # That part I don't care, leave the new values as is

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={9})
            targetIdf.addObject(newObject)

        elif iddname == "OS:EvaporativeFluidCooler:TwoSpeed":
            # 3 Fields were made required from 3.10.0 to 3.10.1:
            # ------------------------------------------------
            # * Design Entering Water Temperature * 24
            # * Design Entering Air Temperature * 25
            # * Design Entering Air Wet-bulb Temperature * 26
            # Don't care, leave the new values as is
            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

        elif iddname == "OS:OutputControl:Files":
            # 1 Field has been added (at end) from 3.10.0 to 3.11.0:
            # ----------------------------------------------
            # * Output Plant Component Sizing * 32

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=32)
            targetIdf.addObject(newObject)

        else:
            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

    return targetIdf

run_translation(idf_3_10_0: openstudio.IdfFile) -> openstudio.IdfFile

Backport an IdfFile from 3.10.0 to 3.9.0.

Source code in openstudiobackporter/backport_3_10_0_to_3_9_0.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
64
def run_translation(idf_3_10_0: openstudio.IdfFile) -> openstudio.IdfFile:
    """Backport an IdfFile from 3.10.0 to 3.9.0."""
    logger.info("Backporting from 3.10.0 to 3.9.0")

    idd_3_9_0 = (
        openstudio.IddFactory.instance()
        .getIddFile(openstudio.IddFileType("OpenStudio"), openstudio.VersionString(3, 9, 0))
        .get()
    )
    targetIdf = openstudio.IdfFile(idd_3_9_0)

    for obj in idf_3_10_0.objects():
        iddname = obj.iddObject().name()

        iddObject_ = idd_3_9_0.getObject(iddname)
        if not iddObject_.is_initialized():
            # Object type doesn't exist in target version, skip it
            logger.warning(f"{brief_description(idf_obj=obj)} does not exist in version 3.9.0, skipping.")
            continue

        iddObject = iddObject_.get()
        newObject = openstudio.IdfObject(iddObject)

        if iddname == "OS:WaterHeater:HeatPump":

            # 1 Field has been inserted from 3.9.0 to 3.10.0:
            # ----------------------------------------------
            # * Tank Element Control Logic * 25

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={25})
            targetIdf.addObject(newObject)

        elif iddname == 'OS:GroundHeatExchanger:Vertical':
            # 1 Field has been inserted from 3.9.0 to 3.10.0:
            # ----------------------------------------------
            # * Bore Hole Top Depth * 6

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={6})
            targetIdf.addObject(newObject)

        elif iddname == 'OS:ZoneVentilation:DesignFlowRate' or iddname == "OS:SpaceInfiltration:DesignFlowRate":
            # 1 Field has been added (at end) from 3.9.0 to 3.10.0:
            # -------------------------------------------
            # * Density Basis * 13 or 26
            cut_off = 26 if iddname == "OS:ZoneVentilation:DesignFlowRate" else 13
            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=cut_off)
            targetIdf.addObject(newObject)

        else:
            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

    return targetIdf

run_translation(idf_3_9_0: openstudio.IdfFile) -> openstudio.IdfFile

Backport an IdfFile from 3.9.0 to 3.8.0.

Source code in openstudiobackporter/backport_3_9_0_to_3_8_0.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 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
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def run_translation(idf_3_9_0: openstudio.IdfFile) -> openstudio.IdfFile:
    """Backport an IdfFile from 3.9.0 to 3.8.0."""
    logger.info("Backporting from 3.9.0 to 3.8.0")

    idd_3_8_0 = (
        openstudio.IddFactory.instance()
        .getIddFile(openstudio.IddFileType("OpenStudio"), openstudio.VersionString(3, 8, 0))
        .get()
    )
    targetIdf = openstudio.IdfFile(idd_3_8_0)

    for obj in idf_3_9_0.objects():
        iddname = obj.iddObject().name()

        iddObject_ = idd_3_8_0.getObject(iddname)
        if not iddObject_.is_initialized():  # pragma: no cover
            # Object type doesn't exist in target version, skip it (None in 3.9.0 to 3.8.0 backport)
            logger.warning(f"{brief_description(idf_obj=obj)} does not exist in version 3.8.0, skipping.")
            continue

        iddObject = iddObject_.get()
        newObject = openstudio.IdfObject(iddObject)

        if iddname == "OS:Controller:OutdoorAir":

            # 2 Fields have been made required from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * High Humidity Outdoor Air Flow Ratio * 24
            # * Control High Indoor Humidity Based on Outdoor Humidity Ratio * 25
            # Fields were made required, so they filled in the default value. I don't see the point reverting that.
            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

        elif iddname == "OS:OutputControl:Files":
            # 1 Field has been added from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Output Space Sizing * 9

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={9})
            targetIdf.addObject(newObject)

        elif iddname == "OS:HeatPump:PlantLoop:EIR:Heating":

            # 3 Fields have been inserted from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Heat Recovery Inlet Node Name * 7
            # * Heat Recovery Outlet Node Name * 8
            # * Heat Recovery Reference Flow Rate * 12

            # 1 required Field has been added from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Minimum Heat Recovery Outlet Temperature * 36

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={7, 8, 12, 36})
            targetIdf.addObject(newObject)

        elif iddname == "OS:HeatPump:PlantLoop:EIR:Cooling":

            # 3 Fields have been inserted from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Heat Recovery Inlet Node Name * 7
            # * Heat Recovery Outlet Node Name * 8
            # * Heat Recovery Reference Flow Rate * 12

            # 5 fields added at end, 2 are required Fields, from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Maximum Heat Recovery Outlet Temperature * 26
            # * Minimum Thermosiphon Minimum Temperature Difference * 30

            copy_with_deleted_fields(obj=obj, newObject=newObject, skip_indices={7, 8, 12, 26, 27, 28, 29, 30})
            targetIdf.addObject(newObject)

        elif iddname == "OS:AirTerminal:SingleDuct:SeriesPIU:Reheat":
            # 5 Fields have been added (at the end) from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Fan Control Type * 16
            # * Minimum Fan Turn Down Ratio * 17
            # * Heating Control Type * 18
            # * Design Heating Discharge Air Temperature * 19
            # * High Limit Heating Discharge Air Temperature * 20

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=16)
            targetIdf.addObject(newObject)

        elif iddname == "OS:AirTerminal:SingleDuct:ParallelPIU:Reheat":
            # 5 Fields have been added (at the end) from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Fan Control Type * 17
            # * Minimum Fan Turn Down Ratio * 18
            # * Heating Control Type * 19
            # * Design Heating Discharge Air Temperature * 20
            # * High Limit Heating Discharge Air Temperature * 21

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=17)
            targetIdf.addObject(newObject)

        elif iddname == "OS:Chiller:Electric:EIR":
            # 7 fields added at end, 3 required Fields has been added from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Condenser Flow Control * 35
            # * Condenser Minimum Flow Fraction * 38
            # * Thermosiphon Minimum Temperature Difference * 40

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=35)
            targetIdf.addObject(newObject)

        elif iddname == "OS:Chiller:Electric:ReformulatedEIR":
            # 7 fields added at end, 3 required Fields has been added from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Condenser Flow Control * 31
            # * Condenser Minimum Flow Fraction * 34
            # * Thermosiphon Minimum Temperature Difference * 36 (at end)

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=31)
            targetIdf.addObject(newObject)

        elif iddname == "OS:Sizing:Zone":
            # 1 required Field has been added from 3.8.0 to 3.9.0:
            # ----------------------------------------------
            # * Sizing Option * 39 (at end)

            copy_with_cutoff_fields(obj=obj, newObject=newObject, cutoff_index=39)
            targetIdf.addObject(newObject)

        else:
            copy_object_as_is(obj=obj, newObject=newObject)
            targetIdf.addObject(newObject)

    return targetIdf

Helpers

brief_description(idf_obj: openstudio.IdfObject) -> str

Get a brief description of the IdfObject.

Source code in openstudiobackporter/helpers.py
12
13
14
15
16
17
def brief_description(idf_obj: openstudio.IdfObject) -> str:
    """Get a brief description of the IdfObject."""
    if (name_ := idf_obj.name()).is_initialized():
        return f"{idf_obj.iddObject().name()} '{name_}'"
    else:
        return f"{idf_obj.iddObject().name()}"

copy_object_as_is(obj: openstudio.IdfObject, newObject: openstudio.IdfObject) -> None

Copy an IdfObject as is.

Even though the object didn't change, the IddObject might have changed field names or memo, etc.

Args:
  • obj: (openstudio.IdfObject) (float): The source IdfObject, from the newer version
  • newObject: (openstudio.IdfObject) The target IdfObject, from the older version
Source code in openstudiobackporter/helpers.py
38
39
40
41
42
43
44
45
46
47
48
49
50
def copy_object_as_is(obj: openstudio.IdfObject, newObject: openstudio.IdfObject) -> None:
    """Copy an IdfObject as is.

    Even though the object didn't change, the IddObject might have changed field names or memo, etc.

    Args:
    -----
    * obj: (openstudio.IdfObject) (float): The source IdfObject, from the newer version
    * newObject: (openstudio.IdfObject) The target IdfObject, from the older version
    """
    for i in range(obj.numFields()):
        if value := obj.getString(i):
            newObject.setString(i, value.get())

copy_with_cutoff_fields(obj: openstudio.IdfObject, newObject: openstudio.IdfObject, cutoff_index: int) -> None

Copy an IdfObject while skipping fields from a certain index onward.

Args:
  • obj: (openstudio.IdfObject) (float): The source IdfObject, from the newer version
  • newObject: (openstudio.IdfObject) The target IdfObject, from the older version
  • cutoff_index: (int) The index from which to stop copying fields (0-indexed)
Source code in openstudiobackporter/helpers.py
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def copy_with_cutoff_fields(obj: openstudio.IdfObject, newObject: openstudio.IdfObject, cutoff_index: int) -> None:
    """Copy an IdfObject while skipping fields from a certain index onward.

    Args:
    -----
    * obj: (openstudio.IdfObject) (float): The source IdfObject, from the newer version
    * newObject: (openstudio.IdfObject) The target IdfObject, from the older version
    * cutoff_index: (int) The index from which to stop copying fields (0-indexed)
    """
    for i in range(obj.numFields()):
        if i >= cutoff_index:
            break
        if value := obj.getString(i):
            newObject.setString(i, value.get())

copy_with_deleted_fields(obj: openstudio.IdfObject, newObject: openstudio.IdfObject, skip_indices: set[int]) -> None

Copy an IdfObject while skipping certain field indices.

Args:
  • obj: (openstudio.IdfObject) (float): The source IdfObject, from the newer version
  • newObject: (openstudio.IdfObject) The target IdfObject, from the older version
  • skip_indices: (set[int]) The set of field indices to skip (0-indexed)
Source code in openstudiobackporter/helpers.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def copy_with_deleted_fields(
    obj: openstudio.IdfObject, newObject: openstudio.IdfObject, skip_indices: set[int]
) -> None:
    """Copy an IdfObject while skipping certain field indices.

    Args:
    -----
    * obj: (openstudio.IdfObject) (float): The source IdfObject, from the newer version
    * newObject: (openstudio.IdfObject) The target IdfObject, from the older version
    * skip_indices: (set[int]) The set of field indices to skip (0-indexed)
    """

    offset = 0
    for i in range(obj.numFields()):
        if i in skip_indices:
            offset += 1
            continue
        if value := obj.getString(i):
            newObject.setString(i - offset, value.get())

get_objects_by_type(idf_file: openstudio.IdfFile, idd_object_type_name: str) -> list[openstudio.IdfObject]

Similar to workspace.getObjectsByType, but for IdfFile with an older Version.

In an older version, the IddFile being not the current version, every object is label UserCustom except version.

Source code in openstudiobackporter/helpers.py
4
5
6
7
8
9
def get_objects_by_type(idf_file: openstudio.IdfFile, idd_object_type_name: str) -> list[openstudio.IdfObject]:
    """Similar to workspace.getObjectsByType, but for IdfFile with an older Version.

    In an older version, the IddFile being not the current version, every object is label UserCustom except version.
    """
    return [obj for obj in idf_file.objects() if obj.iddObject().name() == idd_object_type_name]

get_target(idf_file: openstudio.IdfFile, idf_obj: openstudio.IdfObject, index: int) -> openstudio.OptionalIdfObject

Get the target object for the Inlet Air Mixer Schedule.

Source code in openstudiobackporter/helpers.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def get_target(idf_file: openstudio.IdfFile, idf_obj: openstudio.IdfObject, index: int) -> openstudio.OptionalIdfObject:
    """Get the target object for the Inlet Air Mixer Schedule."""
    # Can't call getTarget, this is not a Workspace
    handle_: openstudio.OptionalString = idf_obj.getString(index, returnDefault=False, returnUninitializedEmpty=True)
    if not handle_.is_initialized():
        print(f"For {brief_description(idf_obj=idf_obj)}, String at index {index} is not initialized.")
        return openstudio.OptionalIdfObject()

    handle_str = handle_.get()

    # m_handle isn't being set properly until you reload the model, so we have to look it up ourselves
    for x in idf_file.objects():
        if x.getString(0).value_or('') == handle_str:
            return openstudio.OptionalIdfObject(x)

    return openstudio.OptionalIdfObject()