Code Documentation¶
ADA¶
The main library.
- class ada.Assembly(name='Ada', project='AdaProject', user: User = User(user_id='AdaUser', given_name=None, family_name=None, middle_names=None, prefix_titles=None, suffix_titles=None, org_id='ADA', org_name='Assembly For Design and Analysis', org_description=None, role='Engineer'), schema='IFC4X3_add2', metadata=None, units: Units | str = Units.M, ifc_class: SpatialTypes = SpatialTypes.IfcSite, cad_config=None)¶
The Assembly object. A top level container of parts, beams, plates, shapes and FEM.
- property cad_config¶
CAD backend + tessellation-path config (
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_fem(fem_file: str | os.PathLike, fem_format: FEATypes | str = None, name: str = None, fem_converter: FemConverters | str = 'default')¶
Import a Finite Element model. Currently supported FEM formats: Abaqus, Sesam and Calculix
- read_ifc(ifc_file: str | os.PathLike | ifcopenshell.file, data_only=False, elements2part=None, reader: Literal['ifcopenshell', 'native'] | None = 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.
- to_fem(name: str, fem_format: FEATypes | str, 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) FEAResult | 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 – Name of FEM analysis input deck
fem_format – Desired fem format
scratch_dir – Output directory for analysis input deck
metadata – Parse additional commands to FEM solver not supported by the generalized classes
execute – Execute analysis on complete
run_ext – Run analysis externally or wait for complete
cpus – Number of cpus for running the analysis
gpus – Number of gpus for running the analysis (wherever relevant)
overwrite – Overwrite existing input file deck
fem_converter – Set desired fem converter. Use either ‘default’ or ‘meshio’.
exit_on_complete
run_in_shell
make_zip_file
return_fea_results – Automatically import the result mesh into
model_data_only – Only write the model data (nodes, elements, etc.) to the FEM file
write_input_files_only –
Only write the input files, do not execute the analysis
Note! Meshio implementation currently only supports reading & writing elements and nodes.
Abaqus Metadata:
‘ecc_to_mpc’: Runs the method
convert_ecc_to_mpc(). Default is True ‘hinges_to_coupling’: Runs the methodconvert_hinges_2_couplings(). Default is TrueImportant 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.
- to_genie_xml(destination_xml, writer_postprocessor: Callable[[ET.Element, Part], None] = None, embed_sat: bool | None = None, streaming: bool = False, merge_strategy=None)¶
Write a Genie (DNV) concept XML.
embed_satembeds 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 (seeCadBackend.imprint_planar_faces).Defaults to
None= on whenever it can be produced. It can’t be withmerge_strategy, which sources plates from the FEM-shell face engine without ever materialising thePlateobjects the SAT body is built from; asking for both explicitly is contradictory and raises rather than quietly dropping one.streamingemits 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 withembed_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: Callable[[ET.Element, Part], None] = None, streaming: bool = False, merge_strategy=None)¶
Write a Genie (DNV) workspace file (
.gnx).The workspace is the concept XML
to_genie_xmlwrites 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 isembed_sat=True; a workspace has no polygon-only mode because Genie stores the body beside the XML, never rebuilds it).streaming/merge_strategytake the streaming XML writer’s route through a temporary XML and repack it, for large FEM-derived models.
- to_pickle(pickle_file: str | Path) Path¶
Serialize this Assembly to a pickle file (round-trips via
ada.from_pickle()).adapy objects are kept picklable on purpose — backend CAD bodies live in the transient
_occ_cacheslot, 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.
- class ada.Beam(name, n1: Node | Iterable, n2: Node | Iterable, sec: str | Section, mat: str | Material = None, up=None, angle=0.0, e1=None, e2=None, units=Units.M, hi1: BeamHinge = None, hi2: BeamHinge = None, justification: Justification | str = Justification.NA, **kwargs)¶
The base Beam object
- Parameters:
n1 – Start position of beam. List or Node object
n2 – End position of beam. List or Node object
sec – Section definition. Str or Section Object
mat – Material. Str or Material object. String: [‘S355’ & ‘S420’] (default is ‘S355’ if None is parsed)
name – Name of beam
- static array_from_list_of_coords(list_of_coords: list[tuple | Point], sec: Section | str, mat: Material | str = None, name_gen: Iterable = None, make_closed=False) list[Beam]¶
Create an array of beams from a list of coordinates
- axis_global() tuple[ndarray, ndarray]¶
This beam’s (start, end) in global coordinates.
The nodes are expressed in the beam’s own frame, so a beam inside a placed
Parthas 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.
- bbox() BoundingBox¶
Bounding Box of beam
- copy_to(name: str = None, p1=None, p2=None, rotation_axis: Iterable[float] = None, rotation_angle: float = None) Beam¶
Copy beam to new position
- get_cog_and_mass() tuple[Point, float]¶
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_fraction(fraction: float) Node¶
Returns node as a fraction of the beam length from n1-node.
- property length: float¶
Returns the length of the beam
- line_occ()¶
Wire/edge body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- property ori¶
Get the x-vector, y-vector and z-vector of a given beam
- property orientation: Placement¶
This is the local orientation and position of the Beam within the local placement object
- shell_occ() ShapeHandle¶
Shell/face body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.BeamCurved(name: str, n1, n2, curve3d: CURVE_GEOM_TYPES, sec: str | Section, up=None, **kwargs)¶
A beam whose axis is an arbitrary 3D curve, carried natively.
Where
BeamRevolvemodels a circular arc (revolve of the section) and a plainBeama straight chord,BeamCurvedholds the exact ngeom curve its axis follows — e.g. theBSplineCurveWithKnotsa 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() Geometry¶
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
directrixis the exact 3D curve, so the swept solid follows the real arc, not the chord.
- class ada.BeamHinge(name: 'str', dofs: 'list[BeamHingeDofType]')¶
- class ada.BeamHingeDofType(dof: 'DofType', constraint_type: 'BeamHingeConstraintType', spring_stiffness: 'float' = 0.0)¶
- class ada.BeamRevolve(name: str, curve: CurveRevolve, sec: str | Section, up=None, **kwargs)¶
- solid_geom() Geometry[RevolvedAreaSolid]¶
Revolve the section profile around the curve’s rotation axis.
The profile is placed perpendicular to the arc at
p1and 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
axisis in global coordinates — the convention both CAD backends build from. The IFC writer converts it to the Position-local frame thatIfcRevolvedAreaSolid.Axisrequires.
- class ada.BeamSweep(name: str, curve: CurveOpen2d, sec: str | Section, **kwargs)¶
- class ada.BeamTapered(name, n1: Iterable, n2: Iterable, sec: str | Section, tap: str | Section = None, taper_type: TaperTypes | str = TaperTypes.CENTERED, **kwargs)¶
- class ada.Bolts(name, p1, p2, normal, members, parent=None)¶
TODO: Create a bolt class based on the IfcMechanicalFastener concept.
Which in turn should likely be inside another element components class
- class ada.BoolHalfSpace(origin: Point | Iterable[float], normal: Direction | Iterable[float], flip=False, name: str = None, plane_geo_width: float = 1.0, **kwargs)¶
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.Boolean(primitive, bool_op: BoolOpEnum = BoolOpEnum.DIFFERENCE, metadata=None, parent=None, units=Units.M, guid=None)¶
- class ada.CableSystem(name: str, medium: str | None = None, metadata: dict | None = None, tray_width: float = 0.3, tray_height: float = 0.1, wall: float = 0.003, bend_radius: float | None = None, strict: bool = False)¶
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.
- bend_radius¶
Fixed centreline bend radius for the tray’s fittings;
Nonederives it from the section size. Seestrict.
- strict¶
When True, emit only regular straight + fixed-radius bend segments and raise (naming the offending points) if the route is too tight to fit a real bend, instead of deforming the geometry to make it fit.
- class ada.Connection(name: str, spec_name: str | None = None, spec_inputs: dict[str, Any] | None = None, **kwargs: Any)¶
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.
- class ada.ConstraintConceptCurve(name: 'str', start_pos: 'Iterable | Point', end_pos: 'Iterable | Point', dof_constraints: 'list[ConstraintConceptDofType]')¶
- class ada.ConstraintConceptDofType(dof: 'DofType', constraint_type: 'ConstraintType', spring_stiffness: 'float' = 0.0)¶
- static encastre(dof_type: Literal['fixed', 'free', 'spring', 'prescribed', 'dependent', 'super'] = 'fixed') list[ConstraintConceptDofType]¶
All 6 dofs are fixed
- static pinned() list[ConstraintConceptDofType]¶
All 3 translational dofs are fixed, and all 3 rotational dofs are free.
- class ada.ConstraintConceptPoint(name: 'str', position: 'Point | Iterable', dof_constraints: 'list[ConstraintConceptDofType]')¶
- class ada.ConstraintConceptRigidLink(name: 'str', master_point: 'Iterable | Point', influence_region: 'RigidLinkRegion', dof_constraints: 'list[ConstraintConceptDofType]', rotation_dependent: 'bool' = True, include_all_edges: 'bool' = True)¶
- class ada.CurvePoly2d(points2d, origin: Iterable | Point = None, normal: Iterable | Direction = None, xdir: Iterable | Direction = None, tol=0.001, parent=None, orientation: Placement = None)¶
A closed curve defined by a list of 2d points represented by line and arc segments.
- static build_edge_segments(points3d, edge_curves=None) list[LineSegment]¶
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) orSplineSegment, otherwise aLineSegment. Endpoint match is winding-agnostic; an unmatched spec (e.g. a corner pruned as collinear) just leaves that edge straight. Feed the result tofrom_segments()/Plate.from_segments.
- classmethod from_fem_shell(points3d, tol=0.001, parent=None) CurvePoly2d¶
Fast constructor for flat FEM shell elements (3- or 4-gon, no arcs/radii).
Geometrically equivalent to
from_3d_pointsfor a radius-free polygon, but it (a) computes the orientation once and injects it viaPlacement.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 runningbuild_polycurve/SegCreator. Used only by the FEM shell -> Plate conversion; the generalfrom_3d_pointspath (arcs, fillets, radii) is unchanged.
- classmethod from_fem_shells_batch(pts: ndarray, parent=None, tol=0.001) list[CurvePoly2d | None]¶
Vectorized
from_fem_shell()formsame-arity k-gons (m, k, 3).The per-element orientation/projection math runs once over arrays (
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) returnNone— the caller runs those throughfrom_fem_shell.
- classmethod from_segments(segments, tol=0.001, parent=None, xdir=None, flip_n=False) CurvePoly2d¶
Construct directly from an ordered, closed loop of 3D segments (line/arc/spline).
Unlike
from_3d_points()this neither samples nor rebuilds the boundary viabuild_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 likefrom_3d_points()(Placement.from_co_linear_points), so a segments-built plate sits in the same frame a points-built one would.
- class ada.Direction(*coords: float | int | Iterable[float])¶
- class ada.DuctSystem(name: str, medium: str | None = None, metadata: dict | None = None, duct_width: float = 0.4, duct_height: float = 0.3, wall: float = 0.002, bend_radius: float | None = None, strict: bool = False)¶
Routed HVAC/process ducting. Rendered as a rectangular duct (a BOX cross-section swept along the route), not a round pipe.
- bend_radius¶
Fixed centreline bend radius for the run’s fittings.
Nonederives it from the section size. Real ducting comes in straight sections + standard bends, sostrictuses this radius exactly (seestrict).
- strict¶
When True, emit only regular straight + fixed-radius bend segments and raise (naming the offending points) if the route is too tight to fit a real bend, instead of deforming the geometry to make it fit.
- class ada.ElectricalSystem(name: str, medium: str | None = None, metadata: dict | None = None, voltage: Voltage = Voltage.LV_400, tray_width: float = 0.3, tray_height: float = 0.1, wall: float = 0.003, bend_radius: float | None = None, strict: bool = False)¶
Cable system carrying electrical power at a given supply voltage. Shares the cable-tray geometry of
CableSystem.
- class ada.Equipment(name: str, mass: float, cog: Iterable[float] | Point, origin: Iterable[float] | Point, lx: float, ly: float, lz: float, eq_repr: EquipRepr = EquipRepr.AS_IS, load_case_ref: str | LoadConceptCase = None, moment_equilibrium: bool = True, footprint: list[tuple[float, float]] = None, ports: list[Port] | None = None, ifc_element_class: str = 'IfcBuildingElementProxy', tag: str | None = None, metadata: dict | None = None, guid: str | None = None)¶
-
- all_ports(include_nested: bool = True) list[Port]¶
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=Falsereturnsportsunchanged.
- class ada.FEM(name: 'str', metadata: 'Dict' = <factory>, parent: 'Part' = None, nodes: 'Nodes' = <factory>, ref_points: 'Nodes' = <factory>, ref_sets: 'FemSets' = <factory>, elements: 'FemElements' = <factory>, sets: 'FemSets' = <factory>, sections: 'FemSections' = <factory>, initial_state: 'PredefinedField' = None, subroutine: 'str' = None)¶
- add_interface_nodes(interface_nodes: List[Node | InterfaceNode])¶
Nodes used for interfacing between other parts. Pass a custom Constraint if specific coupling is needed
- add_set(fem_set: FemSet, p=None, vol_box=None, vol_cyl=None, single_member=False, tol=0.0001) FemSet¶
- Parameters:
fem_set – A fem set object
p – Single point (x,y,z)
vol_box – Search by a box volume. Where p is (xmin, ymin, zmin) and vol_box is (xmax, ymax, zmax)
vol_cyl – Search by cylindrical volume. Used together with p to find nodes within cylinder inputted by [radius, height, thickness]
single_member – Set True if you wish to keep only a single member
tol – Point Tolerances. Default is 1e-4
- add_step(step: _step_types) _step_types¶
Add an analysis step to the assembly
- create_fem_elem_from_obj(obj, el_type=None) Elem¶
Converts structural object to FEM elements. Currently only BEAM is supported
- get_all_bcs() Iterable[Bc]¶
Get all the boundary conditions in the entire assembly
- get_all_masses() Iterable[Mass]¶
Get all the Masses in the entire assembly
- property springs: Dict[str, Spring]¶
Spring elements keyed by name — a view over
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
add_spring().
- class ada.Group(name: 'str', members: 'list[Part | Beam | Plate | Wall | Pipe | Shape]', parent: 'Part | Assembly', description: 'str' = '', guid: 'str' = <factory>, change_type: 'ChangeAction' = <ChangeAction.NOTDEFINED: 'NOTDEFINED'>)¶
- class ada.Instance(instance_ref: "Union['Part', 'BackendGeom']", placements: 'List[Placement]' = <factory>)¶
- class ada.IntermittentSpec(pitch: float, length_on: float, length_off: float | None = None)¶
Intermittent weld pattern: weld for length_on, skip length_off, repeat with pitch centre-to-centre.
- class ada.LoadConceptAccelerationField(name: 'str', acceleration: 'tuple[float, float, float]', include_self_weight: 'bool' = True, rotational_field: 'RotationalAccelerationField' = None)¶
- class ada.LoadConceptCase(name: 'str', loads: 'list[LoadConceptLine | LoadConceptPoint | LoadConceptSurface | LoadConceptAccelerationField]' = <factory>, design_condition: 'DesignCondition' = <DesignCondition.OPERATING: 'operating'>, fem_loadcase_number: 'int' = 1, complex_type: "Literal['static']" = 'static', invalidated: 'bool' = True, include_self_weight: 'bool' = False, mesh_loads_as_mass: 'bool' = False)¶
- class ada.LoadConceptCaseCombination(name: 'str', load_cases: 'list[LoadConceptCaseFactored]', design_condition: "DesignCondition | Literal['operating']" = <DesignCondition.OPERATING: 'operating'>, complex_type: "Literal['static']" = 'static', invalidated: 'bool' = True, convert_load_to_mass: 'bool' = False, global_scale_factor: 'float' = 1.0, equipments_type: "Literal['line_load']" = 'line_load')¶
- class ada.LoadConceptCaseFactored(load_case: 'LoadConceptCase', factor: 'float', phase: 'int' = 0)¶
- class ada.LoadConceptLine(name: 'str', start_point: 'Point | Iterable', end_point: 'Point | Iterable', intensity_start: 'tuple[float, float, float]', intensity_end: 'tuple[float, float, float]', system: "Literal['local', 'global']" = 'local')¶
- class ada.LoadConceptPoint(name: 'str', position: 'Point | Iterable', force: 'tuple[float, float, float]', moment: 'tuple[float, float, float]', system: "Literal['local', 'global']" = 'local')¶
- class ada.LoadConceptSurface(name: 'str', plate_ref: 'Plate' = None, points: 'list[Iterable]' = None, pressure: 'float' = None, side: "Literal['front', 'back']" = 'front', system: "Literal['local', 'global']" = 'local')¶
- class ada.MassPoint(name: str, p: Point | Iterable[numeric, numeric, numeric], mass: float, radius=0.2, placement: Placement = None)¶
Concept mass point object, added to handle export to genie xml without needing to use fem-object
- class ada.Material(name, mat_model: Metal | CarbonSteel = None, mat_id=None, parent=None, metadata=None, units=Units.M, guid=None, ifc_store: IfcStore = None)¶
The base material class. Currently only supports Metals. Default material model is S355 carbon steel
- class ada.Node(p: Iterable[numeric, numeric, numeric] | Point, nid=None, r=None, parent=None, units=Units.M, refs=None)¶
Base node object
- Parameters:
p – 3D coordinates of the node
nid – node id
bc – boundary condition of the node
- property has_refs: bool¶
Returns if node is valid, i.e. has objects in refs
- class ada.Part(name, color=None, placement=None, fem: FEM = None, metadata=None, parent=None, units: Units = Units.M, guid=None, ifc_store: IfcStore = None, ifc_class: SpatialTypes = SpatialTypes.IfcBuildingStorey)¶
A Part superclass design to host all relevant information for cad and FEM modelling.
- IFC_CLASSES¶
alias of
SpatialTypes
- add_joint(joint: JointBase) JointBase¶
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.
- add_materials_in_batch(mats: Iterable[Material]) dict[Material, Material]¶
Add each unique material exactly once. Returns a map original_material -> container_material.
- add_objects_in_batch(objects: Iterable[Beam | Plate], add_to_layer: str = None) list[Beam | Plate]¶
Batch-add beams and plates. Returns the list of added (or existing) objects. Only supports Beam/BeamTapered and Plate for now.
- add_sections_in_batch(secs: Iterable[Section]) dict[Section, Section]¶
Add each unique section exactly once. Returns a map original_section -> container_section.
- 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:
margins – Add margins to the volume box (equal in all directions). Input is in meters. Can be negative.
- Returns:
A map generator for the list of beams and resulting intersecting beams
- property concept_fem: ConceptFEM¶
Returns the ConceptFEM object associated with this Part.
- consolidate_sections(include_self=True)¶
Moves all sections from all sub-parts to this part
- copy_to(name: str = None, position: list[float] | Point = None, rotation_axis: Iterable[float] = None, rotation_angle: float = None, add_object_copy_suffix: bool = True) Part¶
Copy the part and all its sub_parts to a new part. Optionally add translation and/or rotation to the new part
- create_objects_from_fem(skip_plates=False, skip_beams=False, merge=False, reconstruct_surfaces=False) None¶
Build Beams and Plates from the contents of the local FEM object.
mergefolds 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 whenmergeis on). Beams are unaffected.
- get_all_welds() Iterable[Weld]¶
Single source of truth for iterating welds across the part tree.
Welds live in
Part._welds— a container intentionally separate fromget_all_physical_objectsbecause the IFC / FEM / GXML writers can’t process them (no .material, nosolid_geomuntil 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 scatteringinclude_welds=Falseopt-outs across every non-GLB caller.
- get_by_name(name) Part | Plate | Beam | Shape | Material | Pipe | None¶
Get element of any type by its name.
- iter_objects_from_fem(beams: bool = True, plates: bool = True, detached: bool = True, mat_cache: dict | None = None, merge_strategy=None) Iterable[Beam | Plate]¶
Lazily build concept objects from this part’s FEM mesh.
Streaming sibling of
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_strategyselects 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 (ada.fem.formats.mesh_faces.faces_from_fem()) and wraps each merged face in a single transientPlate. 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 →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-consolidationmaterials.addwould mint a fresh copy).
- 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: Literal['occ', 'stream', 'auto', 'tolerant', 'native'] | None = None, product_tree: bool = False)¶
- Parameters:
step_path – Can be path to stp file or path to directory of step files.
name – Desired name of destination Shape object
scale – Scale the step content upon import
transform – Transform the step content upon import
rotate – Rotate step content upon import
colour – Assign a specific colour upon import
opacity – Assign Opacity upon import
source_units – Unit of the imported STEP file. Default is ‘m’
reader – STEP read path.
None(default) resolves from the activeCadConfig.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.
- render_offscreen(camera: Camera | None = None, *, backend: Literal['pygfx', 'chromium'] = 'pygfx', preset: dict | None = None, size: tuple[int, int] = (640, 480)) Image¶
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-builtCamera). WhenNone, both backends route through the embed’sapplyCameraPresetmath 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.camerais ignored by chromium; passpresetto override the embed’sCameraPreset.- preset
Camera preset dict (azimuth_deg, elevation_deg, fov_deg, distance, margin, …). Honored by both backends when
camerais None — same field names asparadoc.camera.presets.CameraPresetso the three render paths read from a single source of truth.- size
Viewport size (also the output PNG size at DPR=1).
- to_trimesh_scene(render_override: dict[str, GeomRepr | str] = None, filter_by_guids=None, merge_meshes=True, stream_from_ifc=False, params: RenderParams = None, include_ada_ext: bool = False) trimesh.Scene¶
Create a Trimesh.Scene from ada.Part.
- property welds: list[Weld]¶
Welds at or below the topmost ancestor that include this object as a member.
Walks to the root of the parent chain (Assembly, Part, or standalone Connection) rather than insisting on an Assembly specifically — a standalone Connection used for previews has no Assembly parent but still owns welds in its _welds list.
- class ada.Pipe(name, points, sec, mat='S355', content=None, metadata=None, color=None, units: Units = Units.M, guid=None, place: Placement = None)¶
- class ada.PipeSegElbow(name, start, midpoint, end, bend_radius, section, material=None, parent=None, guid=None, metadata=None, units=Units.M, color=None, arc_seg=None)¶
- line_occ()¶
Wire/edge body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- shell_occ()¶
Shell/face body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- solid_occ()¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.PipeSegStraight(name, p1, p2, section, material, parent=None, guid=None, metadata=None, units=Units.M, color=None)¶
- line_occ()¶
Wire/edge body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- shell_occ()¶
Shell/face body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- solid_occ()¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.PipingSystem(name: str, medium: str | None = None, metadata: dict | None = None, pipe_radius: float = 0.05, pipe_wt: float = 0.005)¶
- class ada.Plate(name: str, points: CurvePoly2d | CoordinateSequence, t: float, mat: str | Material = 'S420', origin: Iterable | Point = None, xdir: Iterable | Direction = None, normal: Iterable | Direction = None, orientation: Placement = None, pl_id=None, tol=None, detached: bool = False, **kwargs)¶
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 – Name of plate
points – List of 2D point coordinates (or a PolyCurve) that make up the plate. Each point is (x, y, optional [radius])
t – Thickness of plate
mat – Material. Can be either Material object or built-in materials (‘S420’ or ‘S355’)
origin – Explicitly define origin of plate. If not set
xdir – Explicitly define x direction of plate. If not set
normal – Explicitly define normal direction of plate. If not set
- bbox() BoundingBox¶
Bounding Box of plate
- static from_fem_shell(name, points, t, mat='S420', color=None, metadata=None, parent=None, detached=False, **kwargs) Plate¶
Fast Plate constructor for flat FEM shell elements (no arcs/radii).
Equivalent geometry to
from_3d_pointsbut routed throughCurvePoly2d.from_fem_shell, which skipsbuild_polycurveand the computed-placement LRU. SeeCurvePoly2d.from_fem_shell().detachedyields a transient plate (no material back-reference) for streaming exporters that build, emit and discard it — seeada.Part.iter_objects_from_fem().
- static from_segments(name, segments, t, mat='S420', color=None, metadata=None, flip_normal=False, **kwargs) Plate¶
Build a plate whose outline is an ordered list of
LineSegment/ArcSegment/SplineSegment.Use this instead of
from_3d_pointswhen 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.
- get_cog() Point¶
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.
If plate has a non-identity placement, we: - rotate xdir and normal by placement rotation - translate origin by placement translation (origin = place_abs.origin + poly.origin)
- line_occ()¶
Wire/edge body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- outline_global() tuple[ndarray, Direction]¶
This plate’s outline and normal in global coordinates.
poly.points3dis expressed in the plate’s own frame, so a plate that sits inside a placedParthas 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).
- shell_occ()¶
Shell/face body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- property t: float¶
Plate thickness
- class ada.PlateCurved(name, face_geom: Geometry, t: float, mat: str | Material = 'S420', extrude_as_solid: bool = False, **kwargs)¶
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
Geometrydirectly; rendering paths convert it viaada.occ.geom.geom_to_occ_geom()and the GLB tessellator’s PlateCurved branch.Quacks like
Platefor the parts of the Part-attachment contract thatadd_plateexercises: exposesnodes(derived from the face’s outer wire), accepts a same-valueunitsre-assignment, and inheritschange_typefromRoot. Cross-unit conversion isn’t implemented yet — set the right units before constructing the plate.- extruded_solid_occ() ShapeHandle¶
Prism-extrude the curved face by
talong its normal so the rendered plate carries thickness like a planarPlate.from_3d_pointsdoes.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.
- classmethod from_occ_face(name: str, occ_face, t: float, mat: str | Material = 'S420', **kwargs) PlateCurved¶
Construct a PlateCurved from a raw OCC
TopoDS_Face.Bypasses the
Geometry→AdvancedFaceround-trip that the regular__init__path relies on. The loft tool uses this when it already has the OCC face fromBRepOffsetAPI_ThruSections— going via AdvancedFace would only re-decode the same surface back into OCC, and theocc_face_to_ada_face→make_face_from_geomround-trip currently has a bounds-structure mismatch (the STEP reader emits raw curve types asAdvancedFace.boundswhile the OCC builder expectsFaceBoundwrappers around ``EdgeLoop``s).Behaviour:
solid_occreturns the wrapped face directly;extruded_solid_occextrudes it along its normal;nodeswalks the face’s outer wire. Thegeom/solid_geomaccessors returnNone— callers that need an adapyGeometrymust use the__init__constructor instead.
- gxml_sense_flag() bool¶
The gxml
curved_shellsense flag (does the desired shell normal agree with the wrapped face’s own normal). Authored data preserved by the gxml reader inmetadata["props"]["gxml_sense_flag"]; defaults to True.
- property nodes: list[Node]¶
Boundary nodes from the outer wire of the wrapped face.
Part.add_plateregisters 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 planarPlate.nodeswould. 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.
- solid_geom() Geometry¶
The plate’s SOLID geometry: a thickness-
tanalytic ClosedShell (built kernel-free byada.geom.primitive_brep.face_to_thick_shell(), honouringConfig().geom_thickness_anchor) whenConfig().geom_thicken_curved_shellsis on and the face is thickenable — else the bare face Geometry as before.
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.Point(*coords: float | int | Iterable[float | int])¶
- class ada.Port(name: 'str', position: 'Point | Iterable[float]', direction_vector: 'Direction | Iterable[float]', direction: 'PortDirection' = <PortDirection.INOUT: 'INOUT'>, category: 'PortCategory' = 'process', parent: 'Equipment | None' = None, connected_system: 'System | None' = None, is_site: 'bool' = False, guid: 'str' = <factory>, tag: 'str | None' = None, nominal_diameter: 'float | None' = None, spec: 'str | None' = None, metadata: 'dict' = <factory>)¶
- get_global_position() Point¶
World position of the port. Note: adds the parent’s origin only — equipment rotation is not modeled (Equipment carries no Placement frame).
- nominal_diameter: float | None = None¶
Nominal diameter in metres, adapy’s base unit; a DN value in millimetres must be converted by the caller.
- spec: str | None = None¶
Piping class / specification code governing the connection.
- tag: str | None = None¶
Process identity of the connection as the source document knows it.
namestays adapy’s identifier — it is whatadd_port/get_portkey on and what the IFC writer emits — so an imported nozzle keeps its own tag here.
- class ada.PortDirection(*values)¶
- class ada.PrimBox(name, p1, p2, origin=None, placement=None, material: Material | Literal['S355', 'S420'] = None, **kwargs)¶
Primitive Box. Length, width & height are local x, y and z respectively
- copy_to(name: str = None, position: list[float] | Point = None, rotation_axis: Iterable[float] = None, rotation_angle: float = None) PrimBox¶
Copy the box to a new position and/or rotation.
- solid_occ()¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.PrimCone(name, p1, p2, r, **kwargs)¶
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.PrimCyl(name, p1, p2, r, **kwargs)¶
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.PrimExtrude(name, curve2d: list[tuple], h, normal=None, origin=None, xdir=None, tol=0.001, **kwargs)¶
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.PrimRevolve(name, points, rev_angle, origin=None, xdir=None, normal=None, tol=0.001, **kwargs)¶
Revolved Primitive
- property revolve_angle: float¶
Revolve angle in degrees
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.PrimSweep(name, sweep_curve: Iterable[Iterable[float]] | CurveOpen3d, profile_curve_outer: Iterable[Iterable[float]] | CurvePoly2d, profile_xdir=None, profile_normal=None, profile_ydir=None, origin=None, derived_reference=False, tol=0.001, radiis: dict[int, float] = None, **kwargs)¶
- class ada.RigidLinkRegion(lower_corner: 'Iterable | Point', upper_corner: 'Iterable | Point')¶
- class ada.RotationalAccelerationField(rotational_point: 'tuple[float, float, float] | Point', rotational_axis: 'tuple[float, float, float] | Direction', angular_acceleration: 'float', angular_velocity: 'float')¶
- class ada.Shape(name, geom: Geometry | list[Geometry] | None = None, color=None, opacity=1.0, mass: float = None, cog: Iterable = None, material: Material | Literal['S355', 'S420'] = None, units=Units.M, metadata=None, guid=None, placement=None, ifc_store: IfcStore = None, ifc_class: ShapeTypes = ShapeTypes.IfcBuildingElementProxy, parent=None)¶
- IFC_CLASSES¶
alias of
ShapeTypes
- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.Surface(name: str, points: CurvePoly2d | CoordinateSequence, mat: str | Material = 'S420', origin: Iterable | Point = None, xdir: Iterable | Direction = None, normal: Iterable | Direction = None, orientation: Placement = None, pl_id=None, tol=None, **kwargs)¶
Planar surface —
Platewithout 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_occis overridden to return the planar face shape instead of attempting a zero-thickness prism extrusion (which would otherwise crash inBRepPrimAPI_MakePrism).- solid_occ() ShapeHandle¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.SurfaceCurved(name: str, face_geom: Geometry, mat: str | Material = 'S420', **kwargs)¶
Non-planar surface —
PlateCurvedwithout 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(inextruded_solid_occ), so subclassing with a forced zero thickness is the entire change.- classmethod from_occ_face(name: str, occ_face, mat: str | Material = 'S420', **kwargs) SurfaceCurved¶
Construct a thickness-less curved surface from a raw OCC face.
Mirrors
PlateCurved.from_occ_face()but pins thickness to zero so downstream rendering emits the bare face.
- class ada.System(name: str, medium: str | None = None, metadata: dict | None = None)¶
Base system; subclasses fix the service
categoryports must match.- add_leg(name: str, start: Port | tuple[Equipment, str], end: Port | tuple[Equipment, str]) System¶
Connect one branch leg – a from/to port pair – as a named
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_systemdetects that shape and routes every leg instead of justports[0]/ports[-1].start/endeach take either shapeconnect()does – aPortobject, or an(equipment, port_name)pair – and may mix (one as aPort, the other by name). Returnsselfso 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(equipment_or_port: Equipment | Port, port_name: str | None = None) System¶
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, noport_name– takes thePortobject directly, e.g. whateverequipment.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 whatadd_leg()accepts too; the name-lookup form stays for the common case of wiring against equipment you didn’t just construct yourself. Returnsselfso connections chain fluently.
- connect_port(port: Port) System¶
Connect this system directly to an already-built
Port– the piececonnect()andadd_leg()share. Same validation as the name-lookup form ofconnect(): the port’s category must match this system’s, and it must not already belong to another system.
- connect_site(name: str, position: Point | Iterable[float], direction: PortDirection = PortDirection.INOUT, direction_vector: Iterable[float] = (0, 0, 1)) System¶
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, …).
positionis a world-space point;directionmust beIN(into the site) orOUT(out of the site). Returnsselfso it chains fluently withconnect().
- route(grid: CellGrid, rules: RoutingRules | None = None) list¶
Route this system through
gridand generate its geometry. Convenience wrapper overada.topology.routing— returnsself.route_geometry.
- route_warnings: list¶
Bend-artifact warnings from the last geometry build (see
system_route_to_geometry): corners the route left sharp because they were too cramped to round. Each names the spot and a respacing fix.
- segments: list[SystemSegment]¶
Optional breakdown of the run into named segments with their in-line components (see
SystemSegment). Empty unless a producer fills it in.Two or more segments whose ports share a common junction equipment (three or more runs meeting at a fitting – a T or a wye) turn this system into a branch:
route_system/system_route_to_geometrydetect that shape and route/model each leg separately instead of readingports[0]/ports[-1]. A single segment, or several that don’t share a junction, is plain round-trip detail and routing ignores it, exactly as before.
- class ada.SystemModel(name: str = 'SystemModel', equipment: list['Equipment'] = <factory>, systems: list['System'] = <factory>, catalog: dict = <factory>, report: Any = None, metadata: dict = <factory>, source_document: DexpiDocument | None = None, procedural_factory: Callable[[ProceduralBuildSpec], tuple[dict, dict]] | None = None)¶
Equipment, ports and the systems joining them – what a P&ID actually says.
Built by a reader (today
ada.from_dexpi()), consumed byto_assembly()to produce a 3D model and byto_dexpi()to write one back out. Both directions start here; neither needs the other to have run.- catalog: dict¶
{slug: equipment document}. A dict’s.getis a validequipment_resolver, so this feeds the procedural compiler directly.
- equipment: list['Equipment']¶
The equipment this model describes, with their ports. Unplaced: every one sits at the origin because the source says nothing about placement, and inventing a coordinate here would be a claim the P&ID does not support.
- classmethod from_dexpi(path, *, name: str | None = None, flavour: str | None = None, definitions=None, inline_components: str = 'metadata', strict: bool = False) SystemModel¶
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
PipingNetworkSegmentand 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.definitionsis 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
reportand is summarised in one warning;strict=Trueraises instead. Gaps found while building are a different failure with a different fix, and are reported separately on the assembly the build produces.
- metadata: dict¶
Provenance and anything the reader wants to carry (source path, wire flavour, warnings).
- name: str = 'SystemModel'¶
Human-readable name, used for the built assembly and as the exported document’s project.
- ports() Iterable¶
Every port on every piece of equipment, in equipment order.
- procedural_factory: Callable[[ProceduralBuildSpec], tuple[dict, dict]] | None = None¶
How to turn this model into the procedural compiler’s input, given the build’s own rules.
spec -> (procedural document, equipment catalog).A callable rather than a stored document, and that is the point of the whole split: the generated decks and the equipment coordinates are products of the build, not properties of the P&ID, so they cannot be computed until the build’s
LayoutRulesare known. Storing a finished procedural document here would bake one layout into the model and make a second build with different deck bounds impossible.Keeping it a callable also keeps this class free of any wire format: the reader supplies it, and nothing here knows or cares that DEXPI produced this model.
- report: Any = None¶
What the read could not carry through, never silently dropped. Read-stage gaps only – the build reports its own on the assembly it produces.
- source_document: DexpiDocument | None = None¶
The document this model was read from, kept so
to_dexpi()can merge into it rather than regenerate.Nonefor a model that was not read from a DEXPI file.
- systems: list['System']¶
The systems joining that equipment’s ports – piping, duct, cable, electrical.
- to_assembly(spec: ProceduralBuildSpec | None = None) Assembly¶
Build a 3D model: generate decks, place the equipment on them, route the systems.
spec(aProceduralBuildSpec) 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: str | PathLike, *, flavour: str = 'proteus', from_scratch: bool = False) Path¶
Write this model out as a DEXPI P&ID.
The default is a merge, not a regeneration: it starts from
source_documentand 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=Truewrites 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.
- class ada.SystemSegment(name: 'str', from_port: 'Port | None' = None, to_port: 'Port | None' = None, components: 'list[dict]' = <factory>, metadata: 'dict' = <factory>, routed_path: 'list[Point] | None' = None)¶
- components: list[dict]¶
In-line components along the run, each an untyped dict so a producer can carry whatever its source describes (class, tag, order, attributes).
- routed_path: list[Point] | None = None¶
This leg’s own routed centreline, once routed. Only meaningful for a branched system (two or more segments meeting at a shared junction equipment) – see
ada.topology.routing.route_system, which routes each leg independently and stores its polyline here rather than on the single system-widerouted_path.Nonefor a segment carried only for round-trip detail (a normal two-port system’sSystem.segmentsis usually empty, and routing never touches this field in that case).
- class ada.Transform(translation: 'np.ndarray' = None, rotation: 'Rotation' = None)¶
- class ada.Units(*values)¶
- class ada.User(user_id: str = 'AdaUser', given_name: str = None, family_name: str = None, middle_names: str = None, prefix_titles: str = None, suffix_titles: str = None, org_id: str = 'ADA', org_name: str = 'Assembly For Design and Analysis', org_description: str = None, role: str = 'Engineer')¶
- class ada.Voltage(*values)¶
Typical industrial voltage levels; value in volts.
- class ada.Wall(name, points, height, thickness, placement=None, offset='CENTER', metadata=None, color=None, units=Units.M, guid=None, opacity=1.0)¶
- TYPES_JUSL¶
alias of
WallJustification
- shell_occ()¶
Shell/face body as an opaque CAD
ShapeHandle(seesolid_occ()for the handle contract).
- solid_occ()¶
Solid body for this object as an opaque CAD
ShapeHandle.The handle is the cross-subsystem lingua franca (tessellation, IFC, clash, bbox, FEM all consume it). Treat it as opaque — its concrete type is backend-private (a
TopoDS_Solid/TopoDS_Compoundunder the default OCC backend). Operate on it via the CAD backend verbs, not by importing kernel types. See the internal design notes the internal design notes (Phase 2).
- class ada.Weld(name, p1=None, p2=None, weld_type: WeldType | str = WeldType.FILLET, members=(), profile: list[tuple] | None = None, xdir: tuple | None = None, groove: list[tuple] | None = None, parent=None, *, throat: float | None = None, leg1: float | None = None, leg2: float | None = None, groove_angle: float | None = None, root_gap: float | None = None, root_face: float | None = None, sided: Literal['one', 'two'] = 'one', intermittent: IntermittentSpec | None = None, sweep_curve: CurveOpen3d | Any | None = None, profile_normal: tuple | None = None, profile_ydir: tuple | None = None)¶
First-class weld object.
Geometric placement is always required:
p1/p2(linear extrude) orsweep_curve(curved sweep).xdiris 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 + throatand optionallyleg1/2/groove_angle/root_gap/root_face) viabuild_profile.
- class ada.WeldType(*values)¶
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.
- ada.deprecated(reason: str)¶
A decorator to mark functions or classes as deprecated. Emits a warning when the function or class is used, including the module path.
- Parameters:
reason – Explanation of why the function/class is deprecated.
- ada.dexpi_to_procedural(path: str | Path, *, flavour: str | None = None, definitions=None, layout=None, base_doc: dict | None = None, inline_components: Literal['metadata', 'equipment'] = 'metadata') tuple[dict, dict]¶
Read a DEXPI P&ID and return
(procedural document, equipment catalog).The useful seam under
from_dexpi(): the document is the compiler’s own commit format, so it feedsProceduralBuilder.from_dictorto_excelfor inspection and hand-editing before anything is built, and the catalog’s.getis already a validequipment_resolver. Seeada.cadit.dexpi.read.to_procedural.dexpi_to_procedural_doc()for the arguments.
- ada.from_acis(sat_file: str | pathlib.Path, source_units=Units.M, split: bool = False, limit: int = None, cad_config: CadConfig | None = None) Assembly¶
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
- ada.from_dexpi(path: str | Path, *, spec=None, name: str | None = None, flavour: str | None = None, definitions=None, inline_components: Literal['metadata', 'equipment'] = 'metadata', strict: bool = False) Assembly¶
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:
ada.SystemModel.from_dexpi()reads the P&ID into the adapy-native model of the plant, andto_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: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")
specis theProceduralBuildSpec– deck bounds, design ruleset, whether to route, whether to feed routing failures back into the layout.definitionsandinline_componentsare 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.
- ada.from_fem(fem_file: str | list | pathlib.Path, fem_format: str | list = None, name: str | list = None, source_units=Units.M, fem_converter='default', create_concept_objects=False, convert_skip_plates=False, convert_skip_beams=False, cad_config: CadConfig | None = None) Assembly¶
Create an Assembly object from a FEM file.
- ada.from_genie_xml(xml_path, ifc_schema='IFC4', name: str = None, extract_joints=False, cad_config: CadConfig | None = None, build_topology_store: bool = False) Assembly¶
Create an Assembly object from a Genie XML file.
With
build_topology_storethe source ACIS body is also read into a neutralBRepStoreand attached, so a subsequentto_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).
- ada.from_ifc(ifc_file: os.PathLike | ifcopenshell.file, units=Units.M, name='Ada', cad_config: CadConfig | None = None, reader: Literal['ifcopenshell', 'native'] | None = None) Assembly¶
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 withAssembly.to_ifc(writer="native")for a fully native round-trip. Default (ifcopenshell) is the full typed reader (Beam/Plate/Pipe/…).
- ada.from_pickle(pickle_file: str | PathLike) Assembly¶
Load an Assembly previously written with
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).
- ada.from_step(step_file: str | pathlib.Path, source_units=Units.M, cad_config: CadConfig | None = None, name: str | None = None, scale: float | None = None, transform=None, rotate=None, colour=None, opacity: float = 1.0, include_shells: bool = False, reader: Literal['occ', 'stream', 'auto', 'tolerant', 'native'] | None = None, product_tree: bool = False) Assembly¶
Create an Assembly object from a STEP file.
The read path defaults to
cad_config.step_reader(StepReader.AUTOout of the box: constant-memory streaming with an OCC fallback for out-of-scope files — the most memory-efficient + robust choice). Pass acad_configwith a differentstep_readerto override, or setreader=to force one for this call.product_tree=Truereconstructs the STEP assembly tree as nested Parts (default: a flat list of Shapes).
- ada.iter_from_step(step_file: str | pathlib.Path, *, reader: Literal['auto', 'native', 'stream', 'tolerant'] = 'auto') Iterator[Geometry]¶
Stream a STEP file solid-by-solid as
ada.geom.Geometry— bounded memory, one solid resident at a time. The streaming counterpart tofrom_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
Geometrycarriesid,geometry(analyticada.geom),color,transforms(per-instance world matrices) andinstance_paths(the STEP product/assembly breadcrumb, root-first).readerselects 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.