Constrained form-finding meets automatic differentiation.


Structural model of a form-found bridge over the Sihl river, Switzerland.


COMPAS CEM is a structural design tool that generates efficient form for spatial bar structures subjected to combinations of tension and compression forces. Examples of such structures are gridshells, bridges, stadiums, tensegrities and multistory buildings.

The generated forms can be steered to meet force and geometrical constraints, such as limiting the length of a selection of elements in the structure, best-fitting an arbitrary target surface, or restraining the magnitude of the reactions forces at the supports of a structure.

This constrained form-finding process is solved under the hood using numerical optimization and automatic differentiation – two commonplace techniques in the world of machine learning that COMPAS CEM makes readily accesible to designers around the world.

COMPAS CEM is a COMPAS extension written in pure Python. It runs on Windows, MacOS and Linux and it does not depend on any CAD software to work.

Are you a Grasshopper person though? Worry not. CAD-independence doesn’t mean CAD-incompatibility: we ship COMPAS CEM as a Grasshopper plugin so that you can readily integrate our constrained form-finding engine into your next parametric pipeline.

Feel free to check the Examples and the API Reference sections to glimpse into what COMPAS CEM can do for you.

If you are further interested in learning more about the underpinnings of the CEM framework, the constrained form-finding method that COMPAS CEM implements, we refer you to this journal paper.


COMPAS CEM is developed by Rafael Pastrana at the CREATE Laboratory at the Princeton University School of Architecture in collaboration with Patrick Ole Ohlbrock from the Chair of Structural Design at ETH Zürich and Pierluigi D’Acunto from the Professorship of Structural Design at the Technical University of Munich.

First Example

With compas_cem, you can create a tension-compression structure in equilibrium with Python using an object-oriented interface.

from compas_cem.diagrams import TopologyDiagram

from compas_cem.elements import Node
from compas_cem.elements import TrailEdge
from compas_cem.elements import DeviationEdge

from compas_cem.loads import NodeLoad
from compas_cem.supports import NodeSupport

from compas_cem.plotters import TopologyPlotter
from compas_cem.plotters import FormPlotter
from compas_cem.equilibrium import static_equilibrium

# create a topology diagram
topology = TopologyDiagram()

# add nodes
topology.add_node(Node(0, [0.0, 0.0, 0.0]))
topology.add_node(Node(1, [1.0, 0.0, 0.0]))
topology.add_node(Node(2, [2.5, 0.0, 0.0]))
topology.add_node(Node(3, [3.5, 0.0, 0.0]))

# add edges with negative values for a compression-only structure
topology.add_edge(TrailEdge(0, 1, length=-1.0))
topology.add_edge(DeviationEdge(1, 2, force=-1.0))
topology.add_edge(TrailEdge(2, 3, length=-1.0))

# add supports

# add loads
topology.add_load(NodeLoad(1, [0.0, -1.0, 0.0]))
topology.add_load(NodeLoad(2, [0.0, -1.0, 0.0]))

# assemble trails

# print("trails", topology.trails(), "*trail edges", list(topology.trail_edges()))
# calculate equilibrium
form = static_equilibrium(topology, eta=1e-6, tmax=100, verbose=True)

# plot topology
plotter = TopologyPlotter(topology, figsize=(16, 9))
plotter.draw_loads(radius=0.03, draw_arrows=True, scale=0.25)

# plot form
plotter = FormPlotter(form, figsize=(16, 9))
plotter.draw_nodes(radius=0.03, text="key-xyz")

Table of Contents