Skip to content

API Reference

API Reference

create_example_model(include_geometry_diagnostics: bool = False) -> openstudio.model.Model

Create an example OpenStudio model with two stories and optionally geometry diagnostics.

Parameters:

Name Type Description Default
include_geometry_diagnostics bool

If True, will reverse a surface on purpose

False

Returns:

Name Type Description
model Model

Example model

Source code in effibemviewer/gltf.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
def create_example_model(include_geometry_diagnostics: bool = False) -> openstudio.model.Model:
    """Create an example OpenStudio model with two stories and optionally geometry diagnostics.

    Args:
        include_geometry_diagnostics: If True, will reverse a surface on purpose

    Returns:
        model (openstudio.model.Model): Example model
    """
    model = openstudio.model.exampleModel()
    space = model.getSpaceByName("Space 1").get()
    # Move space type assignment from Building to Space, so I can have one without it
    space_type = space.spaceType().get()
    [space.setSpaceType(space_type) for space in model.getSpaces()]
    b = model.getBuilding()
    b.setNorthAxis(45)
    b.resetSpaceType()

    space_clone = space.clone(model).to_Space().get()
    space_clone.setName("Space Level 2")
    space_clone.resetSpaceType()
    # Set it above the original space for better viewing
    z = space.boundingBox().maxZ().get()
    assert z == 3.0
    space_clone.setZOrigin(z)
    story2 = openstudio.model.BuildingStory(model)
    story2.setName("Second Story")
    story2.setNominalZCoordinate(z)
    story2.setNominalFloortoFloorHeight(z)
    space_clone.setBuildingStory(story2)

    if include_geometry_diagnostics:
        surface = next(s for s in space_clone.surfaces() if s.surfaceType() == "Wall")
        # Make one surface incorrectly oriented
        surface.setVertices(openstudio.reverse(surface.vertices()))

    return model

display_model(model: openstudio.model.Model, height: str = '500px', use_iframe: bool = False, include_geometry_diagnostics: bool = False, cdn: bool = False) -> HTML | IFrame

Display an OpenStudio model in a Jupyter notebook.

Parameters:

Name Type Description Default
model Model

OpenStudio model to render

required
height str

CSS height value (default "500px")

'500px'
use_iframe bool

If True, use IFrame for nbclassic compatibility

False
include_geometry_diagnostics bool

If True, include geometry diagnostic info

False
cdn bool

If True, load JS/CSS from CDN (better caching on re-runs)

False

Returns:

Type Description
HTML | IFrame

IPython display object (HTML or IFrame)

Source code in effibemviewer/gltf.py
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
def display_model(
    model: openstudio.model.Model,
    height: str = "500px",
    use_iframe: bool = False,
    include_geometry_diagnostics: bool = False,
    cdn: bool = False,
) -> HTML | IFrame:
    """Display an OpenStudio model in a Jupyter notebook.

    Args:
        model: OpenStudio model to render
        height: CSS height value (default "500px")
        use_iframe: If True, use IFrame for nbclassic compatibility
        include_geometry_diagnostics: If True, include geometry diagnostic info
        cdn: If True, load JS/CSS from CDN (better caching on re-runs)

    Returns:
        IPython display object (HTML or IFrame)
    """
    fragment = model_to_gltf_html(
        model=model,
        height=height,
        pretty_json=False,
        include_geometry_diagnostics=include_geometry_diagnostics,
        embedded=True,
        loader_mode=False,
        script_only=True,
        cdn=cdn,
    )
    if not use_iframe:
        from IPython.display import HTML

        return HTML(fragment)

    import base64
    import datetime

    from IPython.display import IFrame

    current_year = datetime.datetime.now().year
    footer = f"""<p>
      Copyright &copy; 2026 - {current_year} <a href="https://effibem.com" target="_blank">EffiBEM EURL</a>
    </p>"""
    full_html = f"""<!DOCTYPE html>
<html>
  <head>
  </head>
  <body style='margin:0'>
{fragment}

  <footer>
    {footer}
  </footer>
  </body>
</html>"""
    data_url = f"data:text/html;base64,{base64.b64encode(full_html.encode()).decode()}"
    # Parse height for IFrame (needs integer pixels)
    h = int(height.replace("px", "")) if height.endswith("px") else 500
    return IFrame(src=data_url, width="100%", height=h)

generate_loader_html(height: str = '100vh', include_geometry_diagnostics: bool = False, embedded: bool = True, cdn: bool = False, script_only: bool = False) -> str

Generate a standalone HTML page with a file input for loading GLTF files.

Parameters:

Name Type Description Default
height str

CSS height value (default "100vh" for full viewport)

'100vh'
include_geometry_diagnostics bool

If True, enable geometry diagnostic display

False
embedded bool

If True, inline the JS library. If False, reference external JS file.

True
cdn bool

If True, reference JS/CSS from jsDelivr CDN (overrides embedded)

False
script_only bool

If True, generate only the script fragment (used in gh-pages only)

False
Source code in effibemviewer/gltf.py
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
def generate_loader_html(
    height: str = "100vh",
    include_geometry_diagnostics: bool = False,
    embedded: bool = True,
    cdn: bool = False,
    script_only: bool = False,
) -> str:
    """Generate a standalone HTML page with a file input for loading GLTF files.

    Args:
        height: CSS height value (default "100vh" for full viewport)
        include_geometry_diagnostics: If True, enable geometry diagnostic display
        embedded: If True, inline the JS library. If False, reference external JS file.
        cdn: If True, reference JS/CSS from jsDelivr CDN (overrides embedded)
        script_only: If True, generate only the script fragment (used in gh-pages only)
    Returns:
        str: Full HTML page with file input for loading GLTF files
    """
    template = env.get_template("effibemviewer.html.j2")

    html = template.render(
        height=height,
        gltf_data=None,
        indent=None,
        include_geometry_diagnostics=include_geometry_diagnostics,
        embedded=embedded,
        loader_mode=True,
        script_only=script_only,
        cdn_base_url=CDN_BASE_URL if cdn else None,
    )
    return html

get_css_library(height: str = '100vh') -> str

Get the EffiBEMViewer CSS library content.

Returns:

Name Type Description
str str

The CSS library content

Source code in effibemviewer/gltf.py
57
58
59
60
61
62
63
64
def get_css_library(height: str = "100vh") -> str:
    """Get the EffiBEMViewer CSS library content.

    Returns:
        str: The CSS library content
    """
    template = env.get_template("effibemviewer.css.j2")
    return template.render(height=height)

get_js_library() -> str

Get the EffiBEMViewer JavaScript library content.

Returns:

Name Type Description
str str

The JavaScript library content (uses bare specifiers, requires importmap)

Source code in effibemviewer/gltf.py
47
48
49
50
51
52
53
54
def get_js_library() -> str:
    """Get the EffiBEMViewer JavaScript library content.

    Returns:
        str: The JavaScript library content (uses bare specifiers, requires importmap)
    """
    template = env.get_template("effibemviewer.js.j2")
    return template.render()

model_to_gltf_html(model: openstudio.model.Model, height: str = '100vh', pretty_json: bool = False, include_geometry_diagnostics: bool = False, embedded: bool = True, loader_mode: bool = False, script_only: bool = False, cdn: bool = False) -> str

Generate a full standalone HTML page for viewing an OpenStudio model.

Parameters:

Name Type Description Default
model Model

OpenStudio model to render

required
height str

CSS height value (default "100vh" for full viewport)

'100vh'
pretty_json bool

If True, format JSON with indentation

False
include_geometry_diagnostics bool

If True, include geometry diagnostic info

False
embedded bool

If True, inline the JS library. If False, reference external JS file.

True
loader_mode bool

If True, generate file-input loader instead of embedding model data

False
script_only bool

If True, generate only the script fragment (for Jupyter)

False
cdn bool

If True, reference JS/CSS from jsDelivr CDN (overrides embedded)

False
Source code in effibemviewer/gltf.py
 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
def model_to_gltf_html(
    model: openstudio.model.Model,
    height: str = "100vh",
    pretty_json: bool = False,
    include_geometry_diagnostics: bool = False,
    embedded: bool = True,
    loader_mode: bool = False,
    script_only: bool = False,
    cdn: bool = False,
) -> str:
    """Generate a full standalone HTML page for viewing an OpenStudio model.

    Args:
        model: OpenStudio model to render
        height: CSS height value (default "100vh" for full viewport)
        pretty_json: If True, format JSON with indentation
        include_geometry_diagnostics: If True, include geometry diagnostic info
        embedded: If True, inline the JS library. If False, reference external JS file.
        loader_mode: If True, generate file-input loader instead of embedding model data
        script_only: If True, generate only the script fragment (for Jupyter)
        cdn: If True, reference JS/CSS from jsDelivr CDN (overrides embedded)
    """
    data = model_to_gltf_json(model=model, include_geometry_diagnostics=include_geometry_diagnostics)

    template = env.get_template("effibemviewer.html.j2")
    indent = 2 if pretty_json else None

    return template.render(
        height=height,
        gltf_data=data,
        indent=indent,
        include_geometry_diagnostics=include_geometry_diagnostics,
        embedded=embedded,
        loader_mode=loader_mode,
        script_only=script_only,
        cdn_base_url=CDN_BASE_URL if cdn else None,
    )

model_to_gltf_json(model: openstudio.model.Model, include_geometry_diagnostics: bool = False) -> dict

Convert an OpenStudio model to GLTF JSON format (dict).

Parameters:

Name Type Description Default
model Model

OpenStudio model to convert

required
include_geometry_diagnostics bool

If True, include geometry diagnostic info in the output

False

Returns:

Name Type Description
dict dict

GLTF JSON data representing the model

Raises:

Type Description
ValueError

If geometry diagnostics are requested but not supported by the OpenStudio version

Source code in effibemviewer/gltf.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def model_to_gltf_json(model: openstudio.model.Model, include_geometry_diagnostics: bool = False) -> dict:
    """Convert an OpenStudio model to GLTF JSON format (dict).

    Args:
        model: OpenStudio model to convert
        include_geometry_diagnostics: If True, include geometry diagnostic info in the output

    Returns:
        dict: GLTF JSON data representing the model

    Raises:
        ValueError: If geometry diagnostics are requested but not supported by the OpenStudio version
    """
    ft = openstudio.gltf.GltfForwardTranslator()
    if include_geometry_diagnostics:
        if not callable(getattr(openstudio.gltf.GltfForwardTranslator, "setIncludeGeometryDiagnostics", None)):
            raise ValueError(
                "Geometry diagnostics not supported in this version of OpenStudio. Please update to use this feature."
            )
        ft.setIncludeGeometryDiagnostics(True)
    return ft.modelToGLTFJSON(model)