diff --git a/src/blenderbim/docs/users/dealing_with_large_models.rst b/src/blenderbim/docs/users/dealing_with_large_models.rst
index 74ac75c1de..abb4c13527 100644
--- a/src/blenderbim/docs/users/dealing_with_large_models.rst
+++ b/src/blenderbim/docs/users/dealing_with_large_models.rst
@@ -125,8 +125,8 @@ elements starting at item number 30,000 and ending at item number 50,000. This
allows you to arbitrarily break down large models into submodels. This can be
combined with other filters.
-`Beta versions of Blender 3.3 `__
-may be used which will result in a faster load time (~50%).
+Using Blender 3.3 and above will result in a faster load time (~50%) compared to
+older Blender versions.
Coordination only models
------------------------
diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python.rst b/src/ifcopenshell-python/docs/ifcopenshell-python.rst
index 8cf57134cc..9e9471369e 100644
--- a/src/ifcopenshell-python/docs/ifcopenshell-python.rst
+++ b/src/ifcopenshell-python/docs/ifcopenshell-python.rst
@@ -12,5 +12,5 @@ system, as well as high level analysis and authoring functions.
ifcopenshell-python/installation
ifcopenshell-python/hello_world
ifcopenshell-python/code_examples
- ifcopenshell-python/geometry_iterator
+ ifcopenshell-python/geometry_processing
ifcopenshell-python/developer_guide
diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_iterator.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_iterator.rst
deleted file mode 100644
index faa717a2e0..0000000000
--- a/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_iterator.rst
+++ /dev/null
@@ -1,53 +0,0 @@
-Geometry iterator
-=================
-
-IfcOpenShell provides a geometry iterator function to efficiently process
-geometry in an IFC model. The iterator is always used in IfcConvert, and may
-also be invoked in C++ or in Python.
-
-The geometry iterator makes it easy to collect possible geometry in a model,
-supports multicore processing, and implements caching and reuse to improve the
-efficiency of geometry processing. It is also possible to process geometry one
-by one using ``create_shape()``, but is significantly less efficient.
-
-By default, the geometry iterator processes all 3D geometry in a model from all
-elements, and returns a list of X Y Z vertex ordinates in a flattend list, as
-well as a flattend list of triangulated faces denoted by vertex indices.
-
-There are a variety of configuration settings to get different output. For
-example, you may filter elements from processing, extract 2D data, or return
-non-triangulated OpenCASCADE BReps. For more information on the various
-settings, see :doc:`Geometry Settings<../ifcopenshell/geometry_settings>`.
-
-Here is a simple example in Python:
-
-.. code-block:: python
-
- import multiprocessing
- import ifcopenshell
- import ifcopenshell.geom
-
- ifc_file = ifcopenshell.open('model.ifc')
-
- settings = ifcopenshell.geom.settings()
- iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
- if iterator.initialize():
- while True:
- shape = iterator.get()
- # Get the current IFC element we are iterating over
- element = ifc_file.by_guid(shape.guid)
- # Indices of vertices per triangle face e.g. [f1v1, f1v2, f1v3, f2v1, f2v2, f2v3, ...]
- faces = shape.geometry.faces
- # X Y Z of vertices in flattened list e.g. [v1x, v1y, v1z, v2x, v2y, v2z, ...]
- verts = shape.geometry.verts
- # Material names and colour style information that are relevant to this shape
- materials = shape.geometry.materials
- # Indices of material applied per triangle face e.g. [f1m, f2m, ...]
- material_ids = shape.geometry.material_ids
-
- # Since the lists are flattened, you may prefer to group them per
- # face like so depending on your geometry kernel
- grouped_verts = [[verts[i], verts[i + 1], verts[i + 2]] for i in range(0, len(verts), 3)]
- grouped_faces = [[faces[i], faces[i + 1], faces[i + 2]] for i in range(0, len(faces), 3)]
- if not iterator.next():
- break
diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst
new file mode 100644
index 0000000000..987cb23dae
--- /dev/null
+++ b/src/ifcopenshell-python/docs/ifcopenshell-python/geometry_processing.rst
@@ -0,0 +1,202 @@
+Geometry processing
+===================
+
+Geometry is specified in many ways in IFC. Some geometry is defined explicitly
+with coordinates, vertices, and faces. Some geometry is defined implicitly with
+equations, boolean operations, and parametric shapes.
+
+Individual processing
+---------------------
+
+The simplest way to process any geometry in a standardised fashion is to use the
+IfcOpenShell ``create_shape()`` function. This will provide a list of vertices,
+edges, and faces, or alternatively an OpenCASCADE BRep.
+
+Here is a simple example of processing a single wall into a list of vertices and
+faces. In this example, a ``shape`` variable is returned, which holds geometry
+related information in ``shape.geometry``:
+
+.. code-block:: python
+
+ import ifcopenshell
+ import ifcopenshell.geom
+
+ ifc_file = ifcopenshell.open('model.ifc')
+ element = ifc_file.by_type('IfcWall')[0]
+
+ settings = ifcopenshell.geom.settings()
+ shape = ifcopenshell.geom.create_shape(settings, element)
+
+ # The GUID of the element we processed
+ print(shape.guid)
+
+ # The ID of the element we processed
+ print(shape.id)
+
+ # The element we are processing
+ print(ifc_file.by_guid(shape.guid))
+
+ # A unique geometry ID, useful to check whether or not two geometries are
+ # identical for caching and reuse. The naming scheme is:
+ # IfcShapeRepresentation.id{-layerset-LayerSet.id}{-material-Material.id}{-openings-[Opening n.id ...]}{-world-coords}
+ print(shape.geometry.id())
+
+ # Indices of vertices per triangle face e.g. [f1v1, f1v2, f1v3, f2v1, f2v2, f2v3, ...]
+ faces = shape.geometry.faces
+
+ # X Y Z of vertices in flattened list e.g. [v1x, v1y, v1z, v2x, v2y, v2z, ...]
+ verts = shape.geometry.verts
+
+ # Since the lists are flattened, you may prefer to group them per face like so depending on your geometry kernel
+ grouped_verts = [[verts[i], verts[i + 1], verts[i + 2]] for i in range(0, len(verts), 3)]
+ grouped_faces = [[faces[i], faces[i + 1], faces[i + 2]] for i in range(0, len(faces), 3)]
+
+ # A list of styles that are relevant to this shape
+ styles = shape.geometry.materials
+
+ for style in styles:
+ # Each style is named after the entity class if a default
+ # material is applied. Otherwise, it is named "surface-style-{SurfaceStyle.name}"
+ # All non-alphanumeric characters are replaced with a "-".
+ print(style.original_name)
+
+ # A more human readable name
+ print(style.name)
+
+ # Each style may have diffuse colour RGB codes
+ if style.has_diffuse:
+ print(style.diffuse)
+
+ # Each style may have transparency data
+ if style.has_transparency:
+ print(style.transparency)
+
+ # Indices of material applied per triangle face e.g. [f1m, f2m, ...]
+ material_ids = shape.geometry.material_ids
+
+Alternatively, you may choose to retrieve an OpenCASCADE BRep:
+
+.. code-block:: python
+
+ import ifcopenshell
+ import ifcopenshell.geom
+
+ ifc_file = ifcopenshell.open('model.ifc')
+ element = ifc_file.by_type('IfcWall')[0]
+
+ settings = ifcopenshell.geom.settings()
+ settings.set(settings.USE_PYTHON_OPENCASCADE, True)
+
+ try:
+ shape = geom.create_shape(settings, element)
+ geometry = shape.geometry # see #1124
+ # These are methods of the TopoDS_Shape class from pythonOCC
+ shape_gpXYZ = geometry.Location().Transformation().TranslationPart()
+ # These are methods of the gpXYZ class from pythonOCC
+ print(shape_gpXYZ.X(), shape_gpXYZ.Y(), shape_gpXYZ.Z())
+ except:
+ print("Shape creation failed")
+
+When an entire element is passed into ``create_shape()``, the 3D representation
+is processed by default with all openings applied. However, it is also possible
+to only process a single shape representation with no openings, representation
+item, or profile definition.
+
+In these scenarios, a ``geometry`` is returned directly, equivalent to
+``shape.geometry`` in the example above.
+
+.. code-block:: python
+
+ ifc_file = ifcopenshell.open('model.ifc')
+ element = ifc_file.by_type('IfcWall')[0]
+
+ # Process a shape representation
+ body = ifcopenshell.util.representation.get_representation(element, "Model", "Body")
+
+ # Note: geometry is returned directly, equivalent to shape.geometry when passing in an element
+ geometry = geom.create_shape(settings, body)
+
+ # Process a representation item
+ geometry = geom.create_shape(settings, ifc_file.by_type("IfcExtrudedAreaSolid")[0])
+
+ # Process a profile
+ geometry = geom.create_shape(settings, ifc_file.by_type("IfcProfileDef")[0])
+
+Geometry iterator
+-----------------
+
+IfcOpenShell provides a geometry iterator function to efficiently process
+geometry in an IFC model. The iterator is always used in IfcConvert, and may
+also be invoked in C++ or in Python. It offers the same features as the
+``create_shape()`` function for `Individual processing`_.
+
+The geometry iterator makes it easy to collect possible geometry in a model,
+supports multicore processing, and implements caching and reuse to improve the
+efficiency of geometry processing. For any bulk geometry processing, it is
+always recommended to use the iterator.
+
+By default, the geometry iterator processes all 3D geometry in a model from all
+elements, and returns a list of X Y Z vertex ordinates in a flattened list, as
+well as a flattened list of triangulated faces denoted by vertex indices.
+
+There are a variety of configuration settings to get different output. For
+example, you may filter elements from processing, extract 2D data, or return
+non-triangulated OpenCASCADE BReps. For more information on the various
+settings, see :doc:`Geometry Settings<../ifcopenshell/geometry_settings>`.
+
+Here is a simple example in Python:
+
+.. code-block:: python
+
+ import multiprocessing
+ import ifcopenshell
+ import ifcopenshell.geom
+
+ ifc_file = ifcopenshell.open('model.ifc')
+
+ settings = ifcopenshell.geom.settings()
+ iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
+ if iterator.initialize():
+ while True:
+ shape = iterator.get()
+ faces = shape.geometry.faces
+ verts = shape.geometry.verts
+ materials = shape.geometry.materials
+ material_ids = shape.geometry.material_ids
+ # ... write code to process geometry here ...
+ if not iterator.next():
+ break
+
+.. note::
+
+ The iterator can only be used to process whole elements, not individual
+ shape representations, representation items, and profiles.
+
+Manual parsing
+--------------
+
+IfcOpenShell lets you traverse any IFC entity graph. This means it is possible
+for you to manually browse through the ``Representation`` attribute of IFC
+elements, and parse the corresponding IFC shape representations yourself instead
+of using generic geometric processing such as `Individual processing`_ and the
+`Geometry iterator`_.
+
+This approach requires an in-depth understanding of IFC geometry
+representations, as well as its many caveats with units and transformations, but
+can be very simple and extremely fast to extract specific types of geometry. For
+example, if you know you are dealing with IfcCircle geometry, you can
+specifically pinpoint the Radius parameter.
+
+.. code-block:: python
+
+ unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
+
+ for circle in ifc_file.by_type("IfcCircle"):
+ # In project length units
+ print(circle.Radius)
+
+ # In SI meters
+ print(circle.Radius * unit_scale)
+
+Given the advanced nature of manual processing, it is generally not recommended
+except in specific tasks.
diff --git a/src/ifcopenshell-python/docs/ifcpatch.rst b/src/ifcopenshell-python/docs/ifcpatch.rst
index af1f3eb5b6..a1efe2b46d 100644
--- a/src/ifcopenshell-python/docs/ifcpatch.rst
+++ b/src/ifcopenshell-python/docs/ifcpatch.rst
@@ -41,8 +41,8 @@ utility:
Exactly how it is run depends on the recipe. A recipe may require zero or more
arguments which are specific to the recipe. Here's an example which runs the
`ExtractElements` recipe, which, as the same suggests, extracts out elements.
-This recipe expects one argument, which uses the [IFC Query
-syntax](https://wiki.osarch.org/index.php?title=IfcOpenShell_code_examples#IFC_Query_Syntax).
+This recipe expects one argument, which uses the `IFC Query
+syntax `_.
In this example, we'll extract out all `IfcWall` elements.
::