Code Documentation¶
ADA¶
The main library.
ada
¶
Beam
¶
Bases: BackendGeom
The base Beam object
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n1
|
Node | Iterable
|
Start position of beam. List or Node object |
required |
n2
|
Node | Iterable
|
End position of beam. List or Node object |
required |
sec
|
str | Section
|
Section definition. Str or Section Object |
required |
mat
|
str | Material
|
Material. Str or Material object. String: ['S355' & 'S420'] (default is 'S355' if None is parsed) |
None
|
name
|
Name of beam |
required |
concept_fem
property
¶
FEM concepts (e.g. end supports) assigned on this beam. Created on first access.
orientation
property
writable
¶
This is the local orientation and position of the Beam within the local placement object
length
property
¶
Returns the length of the beam
ori
property
¶
Get the x-vector, y-vector and z-vector of a given beam
xvec
property
¶
Local X-vector
yvec
property
¶
Local Y-vector
xvec_e
property
¶
Local X-vector (including eccentricities)
array_from_list_of_coords(list_of_coords, sec, mat=None, name_gen=None, make_closed=False)
staticmethod
¶
Create an array of beams from a list of coordinates
get_cog_and_mass()
¶
COG and mass from a single curve-offset solve.
Equivalent to (get_cog(), get_mass()) but resolves the beam's curve offsets / absolute placement once instead of twice — Part.calculate_cog needs both per beam.
get_node_on_beam_by_point(point)
¶
Returns node on beam from point
get_node_on_beam_by_fraction(fraction)
¶
Returns node as a fraction of the beam length from n1-node.
get_outer_points()
¶
Returns outer points of beam
copy_to(name=None, p1=None, p2=None, rotation_axis=None, rotation_angle=None)
¶
Copy beam to new position
bbox()
¶
Bounding Box of beam
to_plates()
¶
Create a plate representation of the beam.
axis_global()
¶
This beam's (start, end) in global coordinates.
The nodes are expressed in the beam's own frame, so a beam inside a
placed :class:~ada.Part has to be pushed through the accumulated
placement. Exporters share this so they cannot disagree on where a beam
is — the Genie SAT body imprints the axis onto the plates and the XML
references the resulting edge, and the two must land on each other.
BeamCurved
¶
Bases: Beam
A beam whose axis is an arbitrary 3D curve, carried natively.
Where :class:BeamRevolve models a circular arc (revolve of the section) and a
plain :class:Beam a straight chord, BeamCurved holds the exact ngeom curve
its axis follows — e.g. the BSplineCurveWithKnots a Genie stiffener's arc was
authored as in the ACIS body. The curve is the sweep path (the section is the
profile swept along it), so no read-side approximation is needed: the geometry
lives in its native container rather than being collapsed to the guide chord.
solid_geom()
¶
Sweep the section profile along the axis curve (a fixed-reference sweep).
The profile is placed at the first node, its plane perpendicular to the
chord (a stable, twist-free reference); the directrix is the exact 3D
curve, so the swept solid follows the real arc, not the chord.
BeamHinge
dataclass
¶
BeamRevolve
¶
Bases: Beam
solid_geom()
¶
Revolve the section profile around the curve's rotation axis.
The profile is placed perpendicular to the arc at p1 and revolved.
The placement frame is X = radial (p1 -> away from axis), Y = rotation
axis (the section "up"), Z = arc tangent (the profile normal).
The revolution axis is in global coordinates — the convention both CAD
backends build from. The IFC writer converts it to the Position-local frame
that IfcRevolvedAreaSolid.Axis requires.
Boolean
¶
Bases: BackendGeom
Connection
¶
Bases: Part
A Part subclass for connection components (e.g. welded joints).
Owns the geometry of the connection itself — sample/host members,
stiffener plates (via add_plate), boolean cutting objects (via
add_boolean on the contained beams), and welds (via add_weld). Carries
optional lineage attrs spec_name and spec_inputs so a Connection
built from a registered ConnectionSpec can be round-tripped.
ArcSegment
¶
CurvePoly2d
¶
Bases: CurveOpen2d
A closed curve defined by a list of 2d points represented by line and arc segments.
from_fem_shell(points3d, tol=0.001, parent=None)
classmethod
¶
Fast constructor for flat FEM shell elements (3- or 4-gon, no arcs/radii).
Geometrically equivalent to from_3d_points for a radius-free polygon,
but it (a) computes the orientation once and injects it via
:meth:Placement.from_dirs_precomputed, so the computed-placement LRU is
never touched (it thrashes on per-element placements), and (b) builds the
closed line loop directly instead of running build_polycurve /
SegCreator. Used only by the FEM shell -> Plate conversion; the general
from_3d_points path (arcs, fillets, radii) is unchanged.
from_fem_shells_batch(pts, parent=None, tol=0.001)
classmethod
¶
Vectorized :meth:from_fem_shell for m same-arity k-gons (m, k, 3).
The per-element orientation/projection math runs once over arrays
(:func:ada.core.vector_transforms.shell_orientations_bulk — same
floating-point operation order and Decimal rounding as the scalar
chain); only the output objects (Point/LineSegment/Node) are built per
element. Rows the bulk math can't take (degenerate corners/edges)
return None — the caller runs those through from_fem_shell.
build_edge_segments(points3d, edge_curves=None)
staticmethod
¶
Ordered, closed loop of 3D segments from ordered corner points + optional PlateEdgeCurve.
Consecutive corners form each edge; an edge whose endpoints match a spec becomes an
ArcSegment (circle/ellipse) or SplineSegment, otherwise a LineSegment. Endpoint
match is winding-agnostic; an unmatched spec (e.g. a corner pruned as collinear) just leaves
that edge straight. Feed the result to :meth:from_segments / Plate.from_segments.
from_segments(segments, tol=0.001, parent=None, xdir=None, flip_n=False)
classmethod
¶
Construct directly from an ordered, closed loop of 3D segments (line/arc/spline).
Unlike :meth:from_3d_points this neither samples nor rebuilds the boundary via
build_polycurve: each segment is carried through to both the 3D and the projected 2D
outline as-is (arc midpoints, spline curves preserved), so analytic edges survive to IFC/STEP
and are discretized only downstream at tessellation. Orientation is derived from the corner
points exactly like :meth:from_3d_points (Placement.from_co_linear_points), so a
segments-built plate sits in the same frame a points-built one would.
Bolts
¶
Bases: BackendGeom
TODO: Create a bolt class based on the IfcMechanicalFastener concept.
https://standards.buildingsmart.org/IFC/RELEASE/IFC4_1/FINAL/HTML/schema/ifcsharedcomponentelements/lexical/ifcmechanicalfastener.htm
Which in turn should likely be inside another element components class
https://standards.buildingsmart.org/IFC/RELEASE/IFC4_1/FINAL/HTML/schema/ifcsharedcomponentelements/lexical/ifcelementcomponent.htm
IntermittentSpec
dataclass
¶
Intermittent weld pattern: weld for length_on, skip length_off, repeat with pitch centre-to-centre.
Weld
¶
Bases: BackendGeom
First-class weld object.
Geometric placement is always required: p1/p2 (linear extrude)
or sweep_curve (curved sweep). xdir is also required — it
orients the profile cross-section in 3D, which member geometry alone
cannot disambiguate (a fillet between the same members has two valid
fill sides).
The profile is either supplied explicitly (profile=) or derived
from parametric inputs (weld_type + throat and optionally
leg1/2/groove_angle/root_gap/root_face) via build_profile.
WeldType
¶
Bases: BaseEnum
Weld type catalog mirroring the 27-value set from upstream weld libraries.
Names are stripped of the WELD_TYPE_ prefix; values match the
names. from_str accepts both stripped and prefixed forms case-
insensitively.
MassPoint
¶
Bases: PrimSphere
Concept mass point object, added to handle export to genie xml without needing to use fem-object
Node
¶
Plate
¶
Bases: BackendGeom
A plate object. The plate element covers all plate elements.
Contains a dictionary with each point of the plate described by an id (index) and a Node object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Name of plate |
required |
points
|
CurvePoly2d | CoordinateSequence
|
List of 2D point coordinates (or a PolyCurve) that make up the plate. Each point is (x, y, optional [radius]) |
required |
t
|
float
|
Thickness of plate |
required |
mat
|
str | Material
|
Material. Can be either Material object or built-in materials ('S420' or 'S355') |
'S420'
|
origin
|
Iterable | Point
|
Explicitly define origin of plate. If not set |
None
|
xdir
|
Iterable | Direction
|
Explicitly define x direction of plate. If not set |
None
|
normal
|
Iterable | Direction
|
Explicitly define normal direction of plate. If not set |
None
|
t
property
writable
¶
Plate thickness
normal
property
¶
Normal vector
from_segments(name, segments, t, mat='S420', color=None, metadata=None, flip_normal=False, **kwargs)
staticmethod
¶
Build a plate whose outline is an ordered list of LineSegment/ArcSegment/SplineSegment.
Use this instead of from_3d_points when the boundary is genuinely a mix of line and analytic
curve edges (e.g. an ACIS/SAT plate with a circular or spline boundary): the segments are carried
verbatim rather than sampled into a point cloud and rebuilt, so arcs/splines survive analytically
into IFC/STEP and are discretized only downstream at tessellation.
from_fem_shell(name, points, t, mat='S420', color=None, metadata=None, parent=None, detached=False, **kwargs)
staticmethod
¶
Fast Plate constructor for flat FEM shell elements (no arcs/radii).
Equivalent geometry to from_3d_points but routed through
CurvePoly2d.from_fem_shell, which skips build_polycurve and the
computed-placement LRU. See :meth:CurvePoly2d.from_fem_shell.
detached yields a transient plate (no material back-reference) for
streaming exporters that build, emit and discard it — see
:meth:ada.Part.iter_objects_from_fem.
bbox()
¶
Bounding Box of plate
get_cog()
¶
Plate centroid in global coordinates.
Convention: - poly.points2d are expressed in the plate's 2D local system (X,Y). - poly.origin is the local 3D origin of that 2D system. - poly.xdir defines local X direction in 3D. - poly.normal defines local Z (plane normal) in 3D. - local Y is constructed as (normal × xdir) to enforce right-hand rule.
The centroid is found in the local system of the plate, and moved to global coordinates by the absolute placement of the plate.
outline_global()
¶
This plate's outline and normal in global coordinates.
poly.points3d is expressed in the plate's own frame, so a plate that
sits inside a placed :class:~ada.Part has to be pushed through the
accumulated placement before being written out. Exporters share this so
they cannot disagree on where a plate is (the Genie SAT body used to
ignore part placements entirely while the polygon writer honoured them).
PlateCurved
¶
Bases: BackendGeom
Plate built on a non-planar face (typically a B-spline patch).
Used by readers that surface a curved surface — the gxml importer
for advanced SAT faces, and the loft tool for ruled corner-
transition surfaces between sharp and rounded profiles. Carries
the underlying :class:~ada.geom.Geometry directly; rendering
paths convert it via :func:ada.occ.geom.geom_to_occ_geom and the
GLB tessellator's PlateCurved branch.
Quacks like :class:Plate for the parts of the Part-attachment
contract that add_plate exercises: exposes nodes (derived
from the face's outer wire), accepts a same-value units
re-assignment, and inherits change_type from
:class:Root. Cross-unit conversion isn't implemented yet — set
the right units before constructing the plate.
nodes
property
¶
Boundary nodes from the outer wire of the wrapped face.
Part.add_plate registers these into the parent Part's
node container so the curved plate participates in node-based
lookups (selection, FEM mesh anchors) the same way a planar
Plate.nodes would. Cached on first access; the underlying
geometry isn't expected to mutate post-construction.
Falls back to an empty list when the geometry can't be converted to an OCC face — the gxml importer flags some advanced faces with a flat-fallback path, and we'd rather let the plate attach with zero boundary nodes than blow up the caller.
from_occ_face(name, occ_face, t, mat='S420', **kwargs)
classmethod
¶
Construct a PlateCurved from a raw OCC TopoDS_Face.
Bypasses the :class:~ada.geom.Geometry →
:class:~ada.geom.surfaces.AdvancedFace round-trip that the
regular __init__ path relies on. The loft tool uses this
when it already has the OCC face from
BRepOffsetAPI_ThruSections — going via AdvancedFace would
only re-decode the same surface back into OCC, and the
occ_face_to_ada_face → make_face_from_geom round-trip
currently has a bounds-structure mismatch (the STEP reader
emits raw curve types as AdvancedFace.bounds while the
OCC builder expects FaceBound wrappers around
EdgeLoops).
Behaviour: solid_occ returns the wrapped face directly;
extruded_solid_occ extrudes it along its normal; nodes
walks the face's outer wire. The geom / solid_geom
accessors return None — callers that need an adapy
Geometry must use the __init__ constructor instead.
gxml_sense_flag()
¶
The gxml curved_shell sense flag (does the desired shell normal agree
with the wrapped face's own normal). Authored data preserved by the gxml
reader in metadata["props"]["gxml_sense_flag"]; defaults to True.
thickness_direction()
¶
Sense-corrected unit thickness direction: the wrapped face's own oriented normal (probed kernel-free at a representative parameter), flipped when the gxml sense flag is false. None when the surface has no kernel-free probe.
solid_geom()
¶
The plate's SOLID geometry: a thickness-t analytic ClosedShell (built
kernel-free by :func:ada.geom.primitive_brep.face_to_thick_shell, honouring
Config().geom_thickness_anchor) when Config().geom_thicken_curved_shells
is on and the face is thickenable — else the bare face Geometry as before.
extruded_solid_occ()
¶
Prism-extrude the curved face by t along its normal so
the rendered plate carries thickness like a planar
Plate.from_3d_points does.
Returns a backend ShapeHandle (Solid) ready for the tessellator.
Falls back to the bare face shape on any prism failure so the caller
still gets something to render.
Surface
¶
Bases: Plate
Planar surface — :class:Plate without thickness.
Same geometry contract as Plate (planar polygon bounded by a
CurvePoly2d) but rendered as a 2D face rather than an
extruded prism. Useful for visualisation-only output or for
pipelines that supply thickness separately (FEM shell elements
where the thickness lives on the section, not the geometry).
Subclasses Plate so every Plate-dispatching consumer (the GLB
tessellator, IFC writer, Part.add_plate, BoundingBox) picks
it up automatically. solid_occ is overridden to return the
planar face shape instead of attempting a zero-thickness prism
extrusion (which would otherwise crash in
BRepPrimAPI_MakePrism).
SurfaceCurved
¶
Bases: PlateCurved
Non-planar surface — :class:PlateCurved without thickness.
Same underlying B-spline / advanced face data as PlateCurved but
rendered as a 2D face. The PlateCurved render path already
short-circuits to the bare face when t == 0 (in
extruded_solid_occ), so subclassing with a forced zero
thickness is the entire change.
from_occ_face(name, occ_face, mat='S420', **kwargs)
classmethod
¶
Construct a thickness-less curved surface from a raw OCC face.
Mirrors :meth:PlateCurved.from_occ_face but pins thickness to
zero so downstream rendering emits the bare face.
PrimBox
¶
Shape
¶
Bases: BackendGeom
cog_abs
property
¶
COG in absolute coordinate system
cog
property
writable
¶
COG in the local coordinate system
__getstate__()
¶
Drop transient OCC state when pickling.
_occ_cache may hold a TopoDS_Shape (STEP/SAT import,
or the cached transform result above) — OCC objects aren't
picklable, and even when wrapped in some forks they don't
survive a process boundary cleanly. Callers that need the
OCC body after unpickling can rebuild it via
:meth:solid_occ from the parametric _geom; raw-OCC
Shapes lose their geometry on round-trip, which is the
honest answer (we don't have a serialisable representation
for arbitrary OCC bodies).
Assembly
¶
Bases: Part
The Assembly object. A top level container of parts, beams, plates, shapes and FEM.
cad_config
property
writable
¶
CAD backend + tessellation-path config (:class:ada.cad.CadConfig).
Defaults lazily to the best path available in the environment — libtess2 when adacpp is
installed (OCC-free, step2glb-parity), else OCC. Set it to pick a path explicitly; pass
it on to factory functions, e.g. stream_step_to_glb(..., cad_config=asm.cad_config).
read_ifc(ifc_file, data_only=False, elements2part=None, reader=None)
¶
Import from IFC file.
reader="native" uses adacpp's pure-C++ IFC reader (IfcNgeomStream) to build a
geometry-shapes Part/ShapeProxy tree (no ifcopenshell/OCC) — colour + spatial hierarchy from
the C++ resolver; does NOT reconstruct typed Beam/Plate objects. Default (ifcopenshell)
is the full typed reader.
read_fem(fem_file, fem_format=None, name=None, fem_converter='default', report_file=None)
¶
Import a Finite Element model. Currently supported FEM formats: Abaqus, Sesam and Calculix
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report_file
|
str | PathLike | None
|
Write what the reader could not carry into the model to this path as
a JSON conversion report ( |
None
|
to_fem(name, fem_format, scratch_dir=None, metadata=None, execute=False, run_ext=False, cpus=1, gpus=None, overwrite=False, fem_converter='default', exit_on_complete=True, run_in_shell=False, make_zip_file=False, return_fea_results=True, model_data_only=False, write_input_files_only=False, report_file=None, formulations=None)
¶
Create a FEM input file deck for executing fem analysis in a specified FEM format. Currently there is limited write support for the following FEM formats:
Open Source
- Calculix
- Code_Aster
not open source
- Abaqus
- Usfos
- Sesam
Write support is added on a need-only-basis. Any contributions are welcomed!
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Name of FEM analysis input deck |
required |
fem_format
|
FEATypes | str
|
Desired fem format |
required |
scratch_dir
|
Output directory for analysis input deck |
None
|
|
metadata
|
Parse additional commands to FEM solver not supported by the generalized classes |
None
|
|
execute
|
Execute analysis on complete |
False
|
|
run_ext
|
Run analysis externally or wait for complete |
False
|
|
cpus
|
Number of cpus for running the analysis |
1
|
|
gpus
|
Number of gpus for running the analysis (wherever relevant) |
None
|
|
overwrite
|
Overwrite existing input file deck |
False
|
|
fem_converter
|
Set desired fem converter. Use either 'default' or 'meshio'. |
'default'
|
|
exit_on_complete
|
|
True
|
|
run_in_shell
|
|
False
|
|
make_zip_file
|
|
False
|
|
return_fea_results
|
Automatically import the result mesh into |
True
|
|
model_data_only
|
Only write the model data (nodes, elements, etc.) to the FEM file |
False
|
|
write_input_files_only
|
Only write the input files, do not execute the analysis |
False
|
|
report_file
|
str | PathLike | None
|
Write what the writer could not carry into the input deck -- constructs
the format has no form for, and what it approximated -- to this path as a JSON
conversion report ( |
None
|
formulations
|
How to choose each element's type in the target format, ahead of the
element's own source formulation and the writer's defaults: a mapping (keyed by the
source Abaqus Metadata: |
None
|
to_pickle(pickle_file)
¶
Serialize this Assembly to a pickle file (round-trips via :func:ada.from_pickle).
adapy objects are kept picklable on purpose — backend CAD bodies live in the transient
_occ_cache slot, not on the object — so the parametric model round-trips cleanly. Lets
a source parsed once be reused for many exports without re-reading/re-parsing it.
to_genie_xml(destination_xml, writer_postprocessor=None, embed_sat=None, streaming=False, merge_strategy=None)
¶
Write a Genie (DNV) concept XML.
embed_sat embeds the plate geometry as a ready-built ACIS SAT body
that each <flat_plate> references by face name. Without it the plates
are written as bare polygons and Genie must rebuild — and imprint — the
ACIS itself on import, which dominates load time on a large model. It
needs a CAD backend (see CadBackend.imprint_planar_faces).
Defaults to None = on whenever it can be produced. It can't be with
merge_strategy, which sources plates from the FEM-shell face engine
without ever materialising the Plate objects the SAT body is built
from; asking for both explicitly is contradictory and raises rather than
quietly dropping one.
streaming emits the per-object <structure> entries straight to
the file instead of building the whole DOM, cutting peak RSS on large
FEM-derived models. It composes with embed_sat (the SAT body itself
is inherently whole-model, so only the concept entries stream).
merge_strategy (None | "none" | "coplanar" | ...) sources plates from
the object-free vectorized FEM-shell face engine — streaming path only.
to_gnx(destination_gnx, writer_postprocessor=None, streaming=False, merge_strategy=None)
¶
Write a Genie (DNV) workspace file (.gnx).
The workspace is the concept XML to_genie_xml writes plus its ACIS
body, zipped the way Genie saves one — so the OS association opens the
model straight into Genie, with no import step. The SAT body is always
built (this is embed_sat=True; a workspace has no polygon-only mode
because Genie stores the body beside the XML, never rebuilds it).
streaming/merge_strategy take the streaming XML writer's route
through a temporary XML and repack it, for large FEM-derived models.
Part
¶
Bases: BackendGeom
A Part superclass design to host all relevant information for cad and FEM modelling.
concept_fem
property
¶
Returns the ConceptFEM object associated with this Part.
add_joint(joint)
¶
This method takes a Joint element containing two intersecting beams. It will check with the existing list of joints to see whether or not it is part of a larger more complex joint. It usese primarily two criteria.
Criteria 1: If both elements are in an existing joint already, it will u
Criteria 2: If the intersecting point coincides within a specified tolerance (currently 10mm) with an exisiting joint intersecting point. If so it will add the elements to this joint. If not it will create a new joint based on these two members.
welds_for(member)
¶
Return every Weld at or below this Part whose members include member.
add_sections_in_batch(secs)
¶
Add each unique section exactly once. Returns a map original_section -> container_section.
add_materials_in_batch(mats)
¶
Add each unique material exactly once. Returns a map original_material -> container_material.
add_objects_in_batch(objects, add_to_layer=None)
¶
Batch-add beams and plates. Returns the list of added (or existing) objects. Only supports Beam/BeamTapered and Plate for now.
read_step_file(step_path, name=None, scale=None, transform=None, rotate=None, colour=None, opacity=1.0, source_units=Units.M, include_shells=False, reader=None, product_tree=False)
¶
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
step_path
|
Can be path to stp file or path to directory of step files. |
required | |
name
|
Desired name of destination Shape object |
None
|
|
scale
|
Scale the step content upon import (uniform, about the world origin) |
None
|
|
transform
|
Translate the step content upon import (a Placement's origin, or x, y, z) |
None
|
|
rotate
|
Rotate step content upon import (a Rotation: degrees about an axis). When several are given they apply in the order scale, rotate, translate. |
None
|
|
colour
|
Assign a specific colour upon import |
None
|
|
opacity
|
Assign Opacity upon import |
1.0
|
|
source_units
|
Unit of the imported STEP file. Default is 'm' |
M
|
|
include_shells
|
No effect; kept so existing calls keep working. The OCAF reader returns every labelled shape, solids and shells alike. |
False
|
|
reader
|
Literal['occ', 'stream', 'auto', 'tolerant', 'native'] | None
|
STEP read path. |
None
|
create_objects_from_fem(skip_plates=False, skip_beams=False, merge=False, reconstruct_surfaces=False)
¶
Build Beams and Plates from the contents of the local FEM object.
merge folds the one-object-per-element output back down by
merging coplanar shell plates (same material + thickness) and
colinear beams (same section + material). Best-effort: a group is
merged only when it collapses cleanly, else its elements are kept.
Defaults off here to keep the 1:1 element→object mapping callers
expect; the FEM→CAD conversion path opts in (merge_fem_objects).
reconstruct_surfaces (opt-in) instead recovers smooth structured
quad panels as single curved plates (NURBS B-rep) — a large size/time
reduction for CAD export of meshes generated from curved panels.
Non-reconstructable elements fall back to flat plates (coplanar-merged
when merge is on). Beams are unaffected.
iter_objects_from_fem(beams=True, plates=True, detached=True, mat_cache=None, merge_strategy=None)
¶
Lazily build concept objects from this part's FEM mesh.
Streaming sibling of :meth:create_objects_from_fem: yields one object
at a time WITHOUT materialising the full set or adding them to the
part's containers, so a streaming exporter (e.g.
Assembly.to_ifc(streaming=True)) keeps peak memory bounded. Beams
are yielded before plates.
detached (default) yields transient plates carrying no material
back-reference, so each frees as soon as the consumer drops it.
merge_strategy selects how shells fold into plates: None (default)
keeps the legacy 1:1 element→plate mapping; any strategy value
("coplanar"/...) sources plates from the object-free vectorized face
engine (:func:ada.fem.formats.mesh_faces.faces_from_fem) and wraps each
merged face in a single transient :class:Plate. This is the one place
the merge strategy lives, so every streaming consumer (Genie XML, IFC,
STEP) folds shells the same way. Beams are unaffected (they fold via the
colinear pass on the object create path; the strategy is shell-only).
mat_cache (name → :class:Material) lets the caller pin which
material objects the plates reference — pass the already-consolidated
materials so the streamed plates share the exporter's material identity
(else a post-consolidation materials.add would mint a fresh copy).
get_part(name, search_all_parts_in_assembly=False)
¶
Get part by name.
get_by_name(name)
¶
Get element of any type by its name.
consolidate_sections(include_self=True)
¶
Moves all sections from all sub-parts to this part
get_all_welds()
¶
Single source of truth for iterating welds across the part tree.
Welds live in Part._welds — a container intentionally
separate from get_all_physical_objects because the IFC /
FEM / GXML writers can't process them (no .material, no
solid_geom until the Weld.solid_geom delegation, etc.).
The GLB pipeline composes both iterators explicitly: tessellation
+ GraphStore add welds via this method on top of the physical
objects. Avoids scattering include_welds=False opt-outs
across every non-GLB caller.
beam_clash_check(margins=5e-05)
¶
For all beams in a Assembly get all beams touching or within the beam. Essentially a clash check is performed and it returns a dictionary of all beam ids and the touching beams. A margin to the beam volume can be included.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
margins
|
Add margins to the volume box (equal in all directions). Input is in meters. Can be negative. |
5e-05
|
Returns:
| Type | Description |
|---|---|
|
A map generator for the list of beams and resulting intersecting beams |
copy_to(name=None, position=None, rotation_axis=None, rotation_angle=None, add_object_copy_suffix=True)
¶
Copy the part and all its sub_parts to a new part. Optionally add translation and/or rotation to the new part
to_trimesh_scene(render_override=None, filter_by_guids=None, merge_meshes=True, stream_from_ifc=False, params=None, include_ada_ext=False)
¶
Create a Trimesh.Scene from ada.Part.
render_offscreen(camera=None, *, backend='pygfx', preset=None, size=(640, 480))
¶
Render the part to a PIL Image.
Parameters¶
camera
Legacy pygfx camera. When supplied with backend="pygfx",
the trimesh-scene render path is used (kept for callers that
already pass a hand-built Camera). When None, both
backends route through the embed's applyCameraPreset
math so pygfx, chromium, and the live 3D viewer all use
identical camera setup.
backend
"pygfx" (default) — fast offscreen render via wgpu.
"chromium" drives the production adapy embed in headless
Chromium via Playwright. camera is ignored by chromium;
pass preset to override the embed's CameraPreset.
preset
Camera preset dict (azimuth_deg, elevation_deg, fov_deg,
distance, margin, …). Honored by both backends when
camera is None — same field names as
paradoc.camera.presets.CameraPreset so the three
render paths read from a single source of truth.
size
Viewport size (also the output PNG size at DPR=1).
Equipment
¶
Bases: Part
add_port(port)
¶
Attach a port to this equipment (sets port.parent).
all_ports(include_nested=True)
¶
This equipment's own ports, followed by those of any nested child
Equipment. A vessel modelled with sub-compartments hangs each
compartment's nozzles on a child equipment one level down, so the
nozzle list of the item as a whole is only complete with those included.
include_nested=False returns :attr:ports unchanged.
connect(port_name, system)
¶
Connect the named port to system (delegates to system.connect).
CableSystem
¶
Bases: System
Routed cable-tray carrier for signal services. Rendered as an open cable tray (a CHANNEL cross-section swept along the route), not a round pipe.
DuctSystem
¶
Bases: System
Routed HVAC/process ducting. Rendered as a rectangular duct (a BOX cross-section swept along the route), not a round pipe.
ElectricalSystem
¶
Bases: CableSystem
Cable system carrying electrical power at a given supply voltage. Shares
the cable-tray geometry of :class:CableSystem.
Port
dataclass
¶
get_global_position()
¶
World position of the port. Note: adds the parent's origin only — equipment rotation is not modeled (Equipment carries no Placement frame).
System
¶
Base system; subclasses fix the service category ports must match.
site_connections
property
¶
The system's site-boundary terminals (inputs/outputs), in order.
connect(equipment_or_port, port_name=None)
¶
Connect this system to a port, in either of two shapes: connect(equipment, port_name)
looks the port up by name (the original, string-lookup form); connect(port) -- one
argument, no port_name -- takes the :class:Port object directly, e.g. whatever
equipment.add_port(...) returned. The direct form is sturdier (a typo'd name fails where
the port was built, not three lines later here) and is what :meth:add_leg accepts too;
the name-lookup form stays for the common case of wiring against equipment you didn't just
construct yourself. Returns self so connections chain fluently.
connect_port(port)
¶
Connect this system directly to an already-built :class:Port -- the piece
:meth:connect and :meth:add_leg share. Same validation as the name-lookup form of
:meth:connect: the port's category must match this system's, and it must not already
belong to another system.
add_leg(name, start, end)
¶
Connect one branch leg -- a from/to port pair -- as a named
:class:~.segments.SystemSegment. Two or more legs sharing a junction equipment (three or
more runs meeting at a fitting) turn this system into a branch:
ada.topology.routing.route_system detects that shape and routes every leg instead of
just ports[0]/ports[-1].
start/end each take either shape :meth:connect does -- a :class:Port object, or
an (equipment, port_name) pair -- and may mix (one as a Port, the other by name).
Returns self so legs chain fluently, e.g.::
system = (
PipingSystem("L-301")
.add_leg("L-301/1", vessel_out, tee_n1)
.add_leg("L-301/2", tee_n2, pump_a_in)
.add_leg("L-301/3", tee_n3, pump_b_in)
)
connect_site(name, position, direction=PortDirection.INOUT, direction_vector=(0, 0, 1))
¶
Terminate this system at a fixed site location — a site input or
site output — rather than an equipment port. This is where the system
crosses the model boundary (grid supply, cooling-water make-up, a drain
to site, …). position is a world-space point; direction must be
IN (into the site) or OUT (out of the site). Returns self so
it chains fluently with :meth:connect.
route(grid, rules=None)
¶
Route this system through grid and generate its geometry.
Convenience wrapper over ada.topology.routing — returns
self.route_geometry.
SystemModel
dataclass
¶
Equipment, ports and the systems joining them -- what a P&ID actually says.
Built by a reader (today :func:ada.from_dexpi), consumed by :meth:to_assembly to produce a
3D model and by :meth:to_dexpi to write one back out. Both directions start here; neither
needs the other to have run.
ports()
¶
Every port on every piece of equipment, in equipment order.
from_dexpi(path, *, name=None, flavour=None, definitions=None, inline_components='metadata', strict=False)
classmethod
¶
Read a DEXPI P&ID -- either flavour, sniffed from the root tag -- into a system model.
This reads and resolves; it does not build. Every item resolves through the equipment
definition list to a physical envelope with real ports, and every PipingNetworkSegment
and signal line becomes a system joining them. No coordinates: a P&ID says what exists
and what is connected to what, and nothing about where any of it stands.
definitions is the equipment definition list (a path to JSON/XLSX, a loaded dict, or
None for the shipped class defaults). inline_components="equipment" materialises each
in-line valve as its own small equipment rather than recording it in the run's metadata --
it decides what exists, which is why it is a read argument and deck bounds are not.
Nothing is dropped quietly. Everything the read could not carry -- a segment whose ends
the P&ID never named, an item that resolved to nothing placeable -- lands in :attr:report
and is summarised in one warning; strict=True raises instead. Gaps found while
building are a different failure with a different fix, and are reported separately on the
assembly the build produces.
to_assembly(spec=None)
¶
Build a 3D model: generate decks, place the equipment on them, route the systems.
spec (a :class:~ada.topo_model.build_spec.ProceduralBuildSpec) carries every choice
the build makes -- deck bounds, design ruleset, structural blueprint, whether to route,
whether to feed routing failures back into the layout. Defaults build a routed model with
the standard rules.
The result is a new assembly and this model is unchanged, so a second build with different rules starts from the same resolved input rather than from the first build's output.
to_dexpi(destination, *, flavour='proteus', from_scratch=False)
¶
Write this model out as a DEXPI P&ID.
The default is a merge, not a regeneration: it starts from :attr:source_document and
re-serializes the equipment, ports and systems adapy owns from the live objects, so an edit
made in Python lands in the output, while everything the source carried that adapy does not
model -- the shape catalogue, presentation, attributes this branch does not touch -- is
echoed back verbatim.
from_scratch=True writes a brand-new document from the live objects alone. It is the
only option for a model with no source document, and lossy by construction even for one
that has it: there is no chamber, no piping class and no schematic drawing on the live
objects to write back.
Placement
¶
origin
property
writable
¶
Get origin using optimized caching.
xdir
property
¶
Get xdir using optimized caching.
ydir
property
¶
Get ydir using optimized caching.
zdir
property
¶
Get zdir using optimized caching.
rot_matrix
cached
property
¶
The rotation from the local to the parent system: its COLUMNS are xdir, ydir and zdir.
xdir/ydir/zdir are where the local x, y and z axes point in the parent system, as in an
IfcAxis2Placement3D (RefDirection and Axis). So rot_matrix @ p + origin takes a local point p
to the parent system.
rot_matrix_inv
cached
property
¶
Inverse of :attr:rot_matrix, resolved once per placement.
One placement is the reference frame for every element transformed
against it, so the inverse of a single small constant matrix was being
recomputed once per element. Cached on the same grounds as
rot_matrix itself: a placement's rotation is fixed once built.
from_rot_matrix(rot_matrix, origin=None)
staticmethod
¶
A placement from a rotation matrix whose columns are the world directions of the local axes
from_axis_angle(axis, angle, origin=None)
staticmethod
¶
Axis is a list of 3 floats, angle is in degrees.
from_co_linear_points(points, xdir=None, flip_n=False)
staticmethod
¶
Create a placement from a list of points that are co-linear.
from_dirs_precomputed(origin, xdir, ydir, zdir)
staticmethod
¶
Build a Placement whose ComputedPlacement is injected directly.
The lazy xdir/ydir/zdir getters short-circuit on a populated
_computed_placement (see :meth:xdir), so accessing the directions
never calls the global get_computed_placement_cached LRU. That cache
thrashes on FEM meshes (a distinct placement per element), so the fast
shell path computes the orientation once and injects it here.
The three directions must already be normalized and rounded to match the
output of compute_orientation_vec (i.e. what the LRU path would have
produced), otherwise geometry will not match the general construction.
get_absolute_placement(include_rotations=False)
¶
This placement accumulated through its owner's ancestry.
Treat the result as read-only. It was already free to be self (an
unparented placement is its own absolute placement), and the fast paths
below widen that: elements sharing a container can be handed the same
resolved object.
rotate(axis, angle)
¶
Rotate the placement around an axis. Returns a new placement.
transfom_point_to_absolute(p)
¶
Transforms a point p from the local system into absolute/global, using self.placement. If identity, returns p unchanged.
transform_vector(vec, inverse=False)
¶
Transform a vector using optimized caching.
transform_local_points_back_to_global(points2d)
¶
Local (2d or 3d) points to the parent system. The same as :meth:transform_local_points_to_global.
transform_global_points_back_to_local(points3d)
¶
Transform points from the global coordinate system to the coordinate system of this placement.
transform_global_points_to_local(points3d)
¶
Transform points from the global coordinate system to the coordinate system of this placement.
with_zdir(new_zdir)
¶
Returns a new Placement with the zdir transformed to match new_zdir.
copy_to()
¶
Make a copy of this placement
Wall
¶
Bases: BackendGeom
TYPES_JUSL = WallJustification
class-attribute
instance-attribute
¶
A wall object representing
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
Points making up wall |
required | |
height
|
Height |
required | |
thickness
|
Thickness |
required | |
origin
|
Origin |
required | |
offset
|
Wall offset from points making up the wall centerline. Accepts float | CENTER | LEFT | RIGHT |
required |
FEM
dataclass
¶
springs
property
¶
Spring elements keyed by name — a view over :attr:elements, not a store.
Springs used to sit in a dict of their own, outside the element container, and
so missed everything that container does for an element: id lookup, the
internal->external renumbering pass, set resolution. A Sesam deck whose
GSETMEMB named a spring therefore failed outright on the array-backed reader
(The elem id "128374" is not found) and silently kept the spring's
pre-renumber id on the object reader. Deriving the view instead of duplicating
the objects is what makes a spring get all of it for free.
Read-only on purpose: a setter would be a second way in, and a second way in is
how the same spring ends up added twice. Use :meth:add_spring.
add_set(fem_set, p=None, vol_box=None, vol_cyl=None, single_member=False, tol=0.0001)
¶
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fem_set
|
FemSet
|
A fem set object |
required |
p
|
Single point (x,y,z) |
None
|
|
vol_box
|
Search by a box volume. Where p is (xmin, ymin, zmin) and vol_box is (xmax, ymax, zmax) |
None
|
|
vol_cyl
|
Search by cylindrical volume. Used together with p to find nodes within cylinder inputted by [radius, height, thickness] |
None
|
|
single_member
|
Set True if you wish to keep only a single member |
False
|
|
tol
|
Point Tolerances. Default is 1e-4 |
0.0001
|
add_step(step)
¶
Add an analysis step to the assembly
add_rp(name, node)
¶
Adds a reference point in assembly with a specific name
add_interface_nodes(interface_nodes)
¶
Nodes used for interfacing between other parts. Pass a custom Constraint if specific coupling is needed
create_fem_elem_from_obj(obj, el_type=None)
¶
Converts structural object to FEM elements. Currently only BEAM is supported
get_all_bcs()
¶
Get all the boundary conditions in the entire assembly
get_all_masses()
¶
Get all the Masses in the entire assembly
ConstraintConceptBeamEnd
dataclass
¶
A support at one end of a beam. In a shell/solid mesh it restrains the cross-section face of that end, through
a coupled reference node or directly on the section nodes (see :data:SectionSupport).
position
property
¶
The beam end position, in the coordinate system of the beam's parent part
ConstraintConceptDofType
dataclass
¶
Material
¶
Bases: Root
The base material class. Currently only supports Metals. Default material model is S355 carbon steel
__eq__(other)
¶
Assuming uniqueness of Material Name and parent
TODO: Make this check for same Material Model parameters
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Material
|
|
required |
Returns:
| Type | Description |
|---|---|
|
|
copy_to(new_name=None, parent=None)
¶
Make a copy of the material with a new name and parent
Section
¶
Bases: Root
w_btn
property
writable
¶
Width of bottom flange
t_w
property
¶
Thickness of web
t_ftop
property
¶
Thickness of top flange
t_fbtn
property
¶
Thickness of bottom flange
r
property
writable
¶
Radius (Outer)
wt
property
writable
¶
Wall thickness
from_str(section_str)
staticmethod
¶
Create a section from a string representation. If tapered, returns a list of two sections
copy_to(name=None)
¶
Make a copy of the section
deprecated(reason)
¶
A decorator to mark functions or classes as deprecated. Emits a warning when the function or class is used, including the module path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reason
|
str
|
Explanation of why the function/class is deprecated. |
required |
dexpi_to_procedural(path, *, flavour=None, definitions=None, layout=None, base_doc=None, inline_components='metadata')
¶
Read a DEXPI P&ID and return (procedural document, equipment catalog).
The useful seam under :func:from_dexpi: the document is the compiler's own commit format, so
it feeds ProceduralBuilder.from_dict or to_excel for inspection and hand-editing before
anything is built, and the catalog's .get is already a valid equipment_resolver. See
:func:ada.cadit.dexpi.read.to_procedural.dexpi_to_procedural_doc for the arguments.
from_acis(sat_file, source_units=Units.M, split=False, limit=None, cad_config=None)
¶
Create an Assembly object from an ACIS SAT file.
Args: sat_file: Path to ACIS SAT file source_units: Units of the SAT file split: If True, split shells into individual AdvancedFace objects limit: Limit the number of geometries to export (useful for debugging) cad_config: Optional CAD/tessellation config attached to the returned assembly
Returns: Assembly object with parsed geometry
from_dexpi(path, *, spec=None, name=None, flavour=None, definitions=None, inline_components='metadata', strict=False)
¶
Build a 3D model from a DEXPI P&ID -- either flavour, sniffed from the root tag.
The one-call path, and a composition of two steps you can take separately:
:meth:ada.SystemModel.from_dexpi reads the P&ID into the adapy-native model of the plant, and
:meth:~ada.api.systems.model.SystemModel.to_assembly builds it. Reach for the two-step form to
look at what the P&ID resolved to before committing to a build, to vary the build rules without
re-reading, or to write the model back out:
.. code-block:: python
model = ada.SystemModel.from_dexpi("unit.xml")
print(sorted(eq.name for eq in model.equipment))
assembly = model.to_assembly(ProceduralBuildSpec(layout=LayoutRules(deck_height=5.0)))
model.to_dexpi("out.xml")
spec is the :class:~ada.topo_model.build_spec.ProceduralBuildSpec -- deck bounds, design
ruleset, whether to route, whether to feed routing failures back into the layout.
definitions and inline_components are read arguments: they decide what the P&ID
resolves to and what exists, not where any of it stands.
The layout is generated, not designed. Shelf packing on physical size has no process sense whatsoever: a pump can land at the far end of a deck from the vessel it feeds. Expect to move things, and note that DEXPI's own 2D coordinates are drawing millimetres, never plant coordinates.
from_fem(fem_file, fem_format=None, name=None, source_units=Units.M, fem_converter='default', create_concept_objects=False, convert_skip_plates=False, convert_skip_beams=False, cad_config=None, report_file=None)
¶
Create an Assembly object from a FEM file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report_file
|
str | Path | None
|
Write what the reader could not carry into the model -- keywords it has
no reader for, references it could not resolve, constructs it left out -- to this path
as a JSON conversion report ( |
None
|
from_genie_xml(xml_path, ifc_schema='IFC4', name=None, extract_joints=False, cad_config=None, build_topology_store=False)
¶
Create an Assembly object from a Genie XML file.
A .gnx workspace is accepted too, and handed to :func:from_gnx.
With build_topology_store the source ACIS body is also read into a neutral
:class:~ada.geom.brep.BRepStore and attached, so a subsequent
to_genie_xml(embed_sat=True) re-exports the exact source topology (1 lump,
every shared edge) instead of re-welding the plate outlines — which keeps every
beam referenced and avoids Genie re-imprinting on import. Off by default (it
reads the SAT a second time).
from_gnx(gnx_path, ifc_schema='IFC4', name=None, extract_joints=False, cad_config=None, build_topology_store=False)
¶
Create an Assembly object from a Genie workspace file (.gnx).
The mirror of :meth:ada.Assembly.to_gnx, and the arguments are
:func:from_genie_xml's because a workspace is a concept XML: the same
DNV_structure_concept_protocol document zipped together with its ACIS body as a
separate member. It is unpacked into a self-contained XML (the body embedded back
in) in a temporary directory and read from there.
That temporary directory is the reason this is a function rather than two lines at
the call site: the SAT the reader writes beside the unpacked XML — the one
build_topology_store reads a second time — exists only while the directory does.
Everything that touches it happens inside the with.
from_ifc(ifc_file, units=Units.M, name='Ada', cad_config=None, reader=None)
¶
Create an Assembly object from an IFC file.
reader="native" uses adacpp's pure-C++ IFC reader (no ifcopenshell/OCC) to build a
geometry-shapes tree — pairs with Assembly.to_ifc(writer="native") for a fully native
round-trip. Default (ifcopenshell) is the full typed reader (Beam/Plate/Pipe/...).
from_pickle(pickle_file)
¶
Load an Assembly previously written with :meth:Assembly.to_pickle.
Round-trips the parametric model so a source parsed once can be reused for many exports without re-reading/re-parsing it. Each call returns a fresh deep copy (downstream mutation of one export can't leak into another).
from_step(step_file, source_units=Units.M, cad_config=None, name=None, scale=None, transform=None, rotate=None, colour=None, opacity=1.0, include_shells=False, reader=None, product_tree=False)
¶
Create an Assembly object from a STEP file.
The read path defaults to cad_config.step_reader (StepReader.AUTO out of the box:
constant-memory streaming with an OCC fallback for out-of-scope files — the most
memory-efficient + robust choice). Pass a cad_config with a different step_reader to
override, or set reader= to force one for this call. product_tree=True reconstructs the
STEP assembly tree as nested Parts (default: a flat list of Shapes).
iter_from_step(step_file, *, reader='auto')
¶
Stream a STEP file solid-by-solid as ada.geom.Geometry — bounded memory,
one solid resident at a time. The streaming counterpart to :func:from_step
(which materialises the whole Assembly): the per-solid foundation the kernel-free
exporters (STEP→IFC/STEP/OBJ/STL) and the cross-format validation pass build on,
so a multi-GB assembly never has to fit in memory.
Each yielded Geometry carries id, geometry (analytic ada.geom),
color, transforms (per-instance world matrices) and instance_paths
(the STEP product/assembly breadcrumb, root-first).
reader selects the parse path:
"auto"(default) — the native adacpp C++ NGEOM parser when it decodes cleanly, else the pure-Python stream reader for that file (lossless fallback)."native"— force the adacpp C++ parser (raises if it is unavailable)."stream"— the pure-Python streaming parser (bottom-up, constant memory)."tolerant"— pure-Python, skipping unsupported solids instead of raising.