Skip to content

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

encastre(name, dof_type='fixed') staticmethod

All 6 dofs are fixed

pinned(name) staticmethod

All 3 translational dofs are fixed, and all 3 rotational dofs are free.

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

Bases: LineSegment

s_normal property writable

Start normal

e_normal property writable

End normal

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

Base node object

Parameters:

Name Type Description Default
p Iterable[numeric, numeric, numeric] | Point

3D coordinates of the node

required
nid

node id

None
bc

boundary condition of the node

required

has_refs property

Returns if node is valid, i.e. has objects in refs

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

Bases: Shape

Primitive Box. Length, width & height are local x, y and z respectively

copy_to(name=None, position=None, rotation_axis=None, rotation_angle=None)

Copy the box to a new position and/or rotation.

PrimRevolve

Bases: Shape

Revolved Primitive

revolve_angle property

Revolve angle in degrees

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 (ada.fem.formats.conversion_report). Always written.

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 (ada.fem.formats.conversion_report). Always written, also when nothing was lost.

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 Formulation, a (family, name) pair, a bare source type name, or the element shape), a function fn(elem, source, target_format) -> type name | None, or a list of them tried in order. See ada.fem.formulations.

Note! Meshio implementation currently only supports reading & writing elements and nodes.

Abaqus Metadata:

'ecc_to_mpc': Runs the method :func:`~ada.fem.FEM.convert_ecc_to_mpc` . Default is True
'hinges_to_coupling': Runs the method :func:`~ada.fem.FEM.convert_hinges_2_couplings` . Default is True

Important Note! The ecc_to_mpc and hinges_to_coupling will make permanent modifications to the model.
If this proves to create issues regarding performance this should be evaluated further.
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 (default) resolves from the active CadConfig.step_reader ("auto" out of the box). "occ" reads via the OpenCASCADE STEPControl_Reader. "stream" uses the kernel-free streaming reader (constant-memory parse, yields adapy geometry directly — see ada.cadit.step.read.stream_reader); "auto" tries the streaming reader first and falls back to OCC if the file uses any entity outside its scope; "tolerant" reads every supported solid kernel-free and skips the unsupported ones (no whole-file OCC fallback) — best for large mixed CAD that would OOM the OCC reader.

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.

Voltage

Bases: Enum

Typical industrial voltage levels; value in volts.

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

encastre(dof_type='fixed') staticmethod

All 6 dofs are fixed

pinned() staticmethod

All 3 translational dofs are fixed, and all 3 rotational dofs are free.

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 (ada.fem.formats.conversion_report). Always written, also when nothing was lost.

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.