{
 "schema": "apeGmsh-api-flows/1",
 "generated": "2026-05-18",
 "scope": "whole library — public entry API: model-building, FEMData/resolution, OpenSees bridge, Results, cuts/sweeps/drift, ground motions, viewers (entry), viz, FEM-theory helpers",
 "groups": [
  {
   "id": "session",
   "label": "Session"
  },
  {
   "id": "geometry",
   "label": "Geometry / CAD"
  },
  {
   "id": "naming",
   "label": "Naming & Resolution"
  },
  {
   "id": "defs",
   "label": "Definitions (pre-mesh)"
  },
  {
   "id": "mesh",
   "label": "Mesh"
  },
  {
   "id": "resolve",
   "label": "Extraction & Resolvers"
  },
  {
   "id": "femtheory",
   "label": "FEM theory"
  },
  {
   "id": "broker",
   "label": "Broker — FEMData"
  },
  {
   "id": "io",
   "label": "Persistence / IO"
  },
  {
   "id": "opensees",
   "label": "OpenSees bridge"
  },
  {
   "id": "results",
   "label": "Results post-processing"
  },
  {
   "id": "cuts",
   "label": "Cuts / Sweeps / Drift"
  },
  {
   "id": "groundmotion",
   "label": "Ground motions"
  },
  {
   "id": "viewers",
   "label": "Viewers"
  },
  {
   "id": "viz",
   "label": "Viz (inspect/plot)"
  },
  {
   "id": "toplevel",
   "label": "Top-level package"
  },
  {
   "id": "external",
   "label": "External / libs"
  }
 ],
 "nodes": [
  {
   "id": "session",
   "label": "apeGmsh session",
   "group": "session"
  },
  {
   "id": "model.geometry",
   "label": "model.geometry",
   "group": "geometry"
  },
  {
   "id": "model.boolean",
   "label": "model.boolean",
   "group": "geometry"
  },
  {
   "id": "model.transforms",
   "label": "model.transforms",
   "group": "geometry"
  },
  {
   "id": "model.io",
   "label": "model.io",
   "group": "geometry"
  },
  {
   "id": "model.queries",
   "label": "model.queries",
   "group": "geometry"
  },
  {
   "id": "part",
   "label": "Part (isolated)",
   "group": "geometry"
  },
  {
   "id": "parts",
   "label": "parts",
   "group": "geometry"
  },
  {
   "id": "parts.fragmentation",
   "label": "parts.fragmentation",
   "group": "geometry"
  },
  {
   "id": "sections",
   "label": "sections",
   "group": "geometry"
  },
  {
   "id": "labels",
   "label": "labels (Tier 1)",
   "group": "naming"
  },
  {
   "id": "physical",
   "label": "physical (Tier 2)",
   "group": "naming"
  },
  {
   "id": "selection",
   "label": "selection / resolution",
   "group": "naming"
  },
  {
   "id": "constraints",
   "label": "constraints",
   "group": "defs"
  },
  {
   "id": "loads",
   "label": "loads",
   "group": "defs"
  },
  {
   "id": "masses",
   "label": "masses",
   "group": "defs"
  },
  {
   "id": "constraint.defs",
   "label": "constraint defs",
   "group": "defs"
  },
  {
   "id": "load.defs",
   "label": "load defs",
   "group": "defs"
  },
  {
   "id": "mass.defs",
   "label": "mass defs",
   "group": "defs"
  },
  {
   "id": "mesh.generation",
   "label": "mesh.generation",
   "group": "mesh"
  },
  {
   "id": "mesh.sizing",
   "label": "mesh.sizing",
   "group": "mesh"
  },
  {
   "id": "mesh.field",
   "label": "mesh.field",
   "group": "mesh"
  },
  {
   "id": "mesh.structured",
   "label": "mesh.structured",
   "group": "mesh"
  },
  {
   "id": "mesh.editing",
   "label": "mesh.editing",
   "group": "mesh"
  },
  {
   "id": "mesh.queries",
   "label": "mesh.queries",
   "group": "mesh"
  },
  {
   "id": "mesh.partitioning",
   "label": "mesh.partitioning",
   "group": "mesh"
  },
  {
   "id": "mesh_selection",
   "label": "mesh_selection",
   "group": "mesh"
  },
  {
   "id": "fem.extract",
   "label": "fem.extract engine",
   "group": "resolve"
  },
  {
   "id": "constraint.resolver",
   "label": "constraint.resolver",
   "group": "resolve"
  },
  {
   "id": "load.resolver",
   "label": "load.resolver",
   "group": "resolve"
  },
  {
   "id": "mass.resolver",
   "label": "mass.resolver",
   "group": "resolve"
  },
  {
   "id": "femdata",
   "label": "FEMData broker",
   "group": "broker"
  },
  {
   "id": "fem.nodes",
   "label": "fem.nodes",
   "group": "broker"
  },
  {
   "id": "fem.elements",
   "label": "fem.elements",
   "group": "broker"
  },
  {
   "id": "fem.info",
   "label": "fem.info",
   "group": "broker"
  },
  {
   "id": "fem.inspect",
   "label": "fem.inspect",
   "group": "broker"
  },
  {
   "id": "records",
   "label": "resolved records",
   "group": "broker"
  },
  {
   "id": "femdata.h5",
   "label": "FEMData/model HDF5",
   "group": "io"
  },
  {
   "id": "loader",
   "label": "loader (.msh)",
   "group": "io"
  },
  {
   "id": "gmsh",
   "label": "Gmsh API",
   "group": "external"
  },
  {
   "id": "viewer",
   "label": "viewer internals",
   "group": "viewers"
  },
  {
   "id": "external",
   "label": "external library",
   "group": "external"
  },
  {
   "id": "fem.quadrature",
   "label": "quadrature",
   "group": "femtheory"
  },
  {
   "id": "fem.shape",
   "label": "shape functions",
   "group": "femtheory"
  },
  {
   "id": "fem.hrz",
   "label": "HRZ lumping",
   "group": "femtheory"
  },
  {
   "id": "apesees",
   "label": "apeSees(fem)",
   "group": "opensees"
  },
  {
   "id": "os.sections",
   "label": "os sections",
   "group": "opensees"
  },
  {
   "id": "os.elements",
   "label": "os elements",
   "group": "opensees"
  },
  {
   "id": "os.transform",
   "label": "os geomTransf",
   "group": "opensees"
  },
  {
   "id": "os.node",
   "label": "os node / fix",
   "group": "opensees"
  },
  {
   "id": "os.pattern",
   "label": "os pattern",
   "group": "opensees"
  },
  {
   "id": "os.time_series",
   "label": "os timeSeries",
   "group": "opensees"
  },
  {
   "id": "os.recorder",
   "label": "os recorder",
   "group": "opensees"
  },
  {
   "id": "os.analysis",
   "label": "os analysis stack",
   "group": "opensees"
  },
  {
   "id": "os.registry",
   "label": "os tag registry",
   "group": "opensees"
  },
  {
   "id": "os.build",
   "label": "os build",
   "group": "opensees"
  },
  {
   "id": "os.emit",
   "label": "os emit / export",
   "group": "opensees"
  },
  {
   "id": "results",
   "label": "Results facade",
   "group": "results"
  },
  {
   "id": "results.bind",
   "label": "results bind",
   "group": "results"
  },
  {
   "id": "results.nodes",
   "label": "results.nodes",
   "group": "results"
  },
  {
   "id": "results.elements",
   "label": "results.elements",
   "group": "results"
  },
  {
   "id": "results.inspect",
   "label": "results.inspect",
   "group": "results"
  },
  {
   "id": "results.slabs",
   "label": "result slabs",
   "group": "results"
  },
  {
   "id": "results.chain",
   "label": "results select chain",
   "group": "results"
  },
  {
   "id": "results.plot",
   "label": "results.plot",
   "group": "results"
  },
  {
   "id": "results.capture",
   "label": "DomainCapture",
   "group": "results"
  },
  {
   "id": "results.spec",
   "label": "results spec",
   "group": "results"
  },
  {
   "id": "results.readers",
   "label": "results readers",
   "group": "results"
  },
  {
   "id": "results.writers",
   "label": "results writers",
   "group": "results"
  },
  {
   "id": "results.transcode",
   "label": "results transcode",
   "group": "results"
  },
  {
   "id": "results.live",
   "label": "results live",
   "group": "results"
  },
  {
   "id": "results.schema",
   "label": "results schema",
   "group": "results"
  },
  {
   "id": "cuts",
   "label": "cut defs",
   "group": "cuts"
  },
  {
   "id": "cuts.planes",
   "label": "cut planes",
   "group": "cuts"
  },
  {
   "id": "cuts.polygons",
   "label": "cut polygons",
   "group": "cuts"
  },
  {
   "id": "cuts.sweeps",
   "label": "section sweeps",
   "group": "cuts"
  },
  {
   "id": "cuts.drift",
   "label": "drift / drift sweep",
   "group": "cuts"
  },
  {
   "id": "cuts.h5",
   "label": "cuts HDF5 io",
   "group": "cuts"
  },
  {
   "id": "cuts.stko",
   "label": "STKO tag bridge",
   "group": "cuts"
  },
  {
   "id": "gm",
   "label": "GroundMotion",
   "group": "groundmotion"
  },
  {
   "id": "gm.parsers",
   "label": "gm parsers",
   "group": "groundmotion"
  },
  {
   "id": "gm.sniffer",
   "label": "gm sniffer",
   "group": "groundmotion"
  },
  {
   "id": "viewer.model",
   "label": "model viewer",
   "group": "viewers"
  },
  {
   "id": "viewer.mesh",
   "label": "mesh viewer",
   "group": "viewers"
  },
  {
   "id": "viewer.results",
   "label": "results viewer",
   "group": "viewers"
  },
  {
   "id": "viewer.animation",
   "label": "animation export",
   "group": "viewers"
  },
  {
   "id": "viewer.geomtransf",
   "label": "geomTransf viewer",
   "group": "viewers"
  },
  {
   "id": "viz.inspect",
   "label": "g.inspect",
   "group": "viz"
  },
  {
   "id": "viz.plot",
   "label": "g.plot",
   "group": "viz"
  },
  {
   "id": "viz.preview",
   "label": "notebook preview",
   "group": "viz"
  },
  {
   "id": "viz.selection",
   "label": "viz selection",
   "group": "viz"
  },
  {
   "id": "viz.vtkexport",
   "label": "VTK export",
   "group": "viz"
  },
  {
   "id": "apegmsh",
   "label": "apeGmsh package",
   "group": "toplevel"
  },
  {
   "id": "chain",
   "label": "generic chain base",
   "group": "toplevel"
  },
  {
   "id": "types",
   "label": "_types",
   "group": "toplevel"
  },
  {
   "id": "vocabulary",
   "label": "_vocabulary",
   "group": "toplevel"
  }
 ],
 "edges": [
  {
   "from": "apegmsh",
   "to": "session",
   "methods": [
    "apegmsh.exports",
    "apegmsh.workdir"
   ]
  },
  {
   "from": "apesees",
   "to": "femdata",
   "methods": [
    "apeSees",
    "apeSees.fem"
   ]
  },
  {
   "from": "apesees",
   "to": "gm",
   "methods": [
    "gm.GroundMotion.to_time_series"
   ]
  },
  {
   "from": "apesees",
   "to": "os.build",
   "methods": [
    "Node.fix",
    "Node.mass",
    "NodeSet.fix",
    "NodeSet.mass",
    "apeSees.analyze",
    "apeSees.export.h5",
    "apeSees.export.py",
    "apeSees.export.tcl",
    "apeSees.fix",
    "apeSees.h5",
    "apeSees.live.analyze",
    "apeSees.live.run",
    "apeSees.mass",
    "apeSees.model",
    "apeSees.py",
    "apeSees.run",
    "apeSees.tcl"
   ]
  },
  {
   "from": "apesees",
   "to": "os.registry",
   "methods": [
    "apeSees",
    "apeSees.analysis.AMD",
    "apeSees.analysis.ArcLength",
    "apeSees.analysis.BFGS",
    "apeSees.analysis.BandGeneral",
    "apeSees.analysis.BandSPD",
    "apeSees.analysis.Broyden",
    "apeSees.analysis.CentralDifference",
    "apeSees.analysis.DisplacementControl",
    "apeSees.analysis.EnergyIncr",
    "apeSees.analysis.ExplicitDifference",
    "apeSees.analysis.FixedNumIter",
    "apeSees.analysis.FullGeneral",
    "apeSees.analysis.HHT",
    "apeSees.analysis.KrylovNewton",
    "apeSees.analysis.Lagrange",
    "apeSees.analysis.Linear",
    "apeSees.analysis.LoadControl",
    "apeSees.analysis.ModifiedNewton",
    "apeSees.analysis.Mumps",
    "apeSees.analysis.Newmark",
    "apeSees.analysis.Newton",
    "apeSees.analysis.NewtonLineSearch",
    "apeSees.analysis.NormDispIncr",
    "apeSees.analysis.NormUnbalance",
    "apeSees.analysis.Penalty",
    "apeSees.analysis.PlainConstraints",
    "apeSees.analysis.PlainNumberer",
    "apeSees.analysis.ProfileSPD",
    "apeSees.analysis.RCM",
    "apeSees.analysis.RelativeNormDispIncr",
    "apeSees.analysis.SparseGeneral",
    "apeSees.analysis.Static",
    "apeSees.analysis.Transformation",
    "apeSees.analysis.Transient",
    "apeSees.analysis.UmfPack",
    "apeSees.analysis.VariableTransient",
    "apeSees.beamIntegration.HingeEndpoint",
    "apeSees.beamIntegration.HingeMidpoint",
    "apeSees.beamIntegration.HingeRadau",
    "apeSees.beamIntegration.HingeRadauTwo",
    "apeSees.beamIntegration.Legendre",
    "apeSees.beamIntegration.Lobatto",
    "apeSees.beamIntegration.NewtonCotes",
    "apeSees.beamIntegration.Radau",
    "apeSees.beamIntegration.Trapezoidal",
    "apeSees.build",
    "apeSees.element.ASDShellQ4",
    "apeSees.element.ASDShellT3",
    "apeSees.element.CorotTruss",
    "apeSees.element.ElasticTimoshenkoBeam",
    "apeSees.element.FourNodeQuad",
    "apeSees.element.FourNodeTetrahedron",
    "apeSees.element.InertiaTruss",
    "apeSees.element.ShellDKGQ",
    "apeSees.element.ShellMITC3",
    "apeSees.element.ShellMITC4",
    "apeSees.element.TenNodeTetrahedron",
    "apeSees.element.Tri31",
    "apeSees.element.Truss",
    "apeSees.element.ZeroLength",
    "apeSees.element.ZeroLengthSection",
    "apeSees.element.dispBeamColumn",
    "apeSees.element.elasticBeamColumn",
    "apeSees.element.forceBeamColumn",
    "apeSees.element.stdBrick",
    "apeSees.geomTransf.Corotational",
    "apeSees.geomTransf.Linear",
    "apeSees.geomTransf.PDelta",
    "apeSees.nDMaterial.DruckerPrager",
    "apeSees.nDMaterial.ElasticIsotropic",
    "apeSees.nDMaterial.J2Plasticity",
    "apeSees.pattern.Plain",
    "apeSees.pattern.UniformExcitation",
    "apeSees.recorder.Element",
    "apeSees.recorder.MPCO",
    "apeSees.recorder.Node",
    "apeSees.recorder.RecorderDeclaration",
    "apeSees.register",
    "apeSees.section.ElasticMembranePlateSection",
    "apeSees.section.ElasticSection",
    "apeSees.section.Fiber",
    "apeSees.section.LayeredShell",
    "apeSees.section.LayeredShellFiberSection",
    "apeSees.tag_for",
    "apeSees.time_series.Constant",
    "apeSees.time_series.Linear",
    "apeSees.time_series.Path",
    "apeSees.time_series.Pulse",
    "apeSees.time_series.Trig",
    "apeSees.uniaxialMaterial.ASDSteel1D",
    "apeSees.uniaxialMaterial.Concrete01",
    "apeSees.uniaxialMaterial.Concrete02",
    "apeSees.uniaxialMaterial.ENT",
    "apeSees.uniaxialMaterial.ElasticMaterial",
    "apeSees.uniaxialMaterial.Hysteretic",
    "apeSees.uniaxialMaterial.Steel01",
    "apeSees.uniaxialMaterial.Steel02"
   ]
  },
  {
   "from": "apesees",
   "to": "results.capture",
   "methods": [
    "apeSees.domain_capture"
   ]
  },
  {
   "from": "apesees",
   "to": "types",
   "methods": [
    "apeSees.recorder.RecorderRecord"
   ]
  },
  {
   "from": "chain",
   "to": "selection",
   "methods": [
    "chain.SelectionChain"
   ]
  },
  {
   "from": "constraint.defs",
   "to": "constraint.resolver",
   "methods": [
    "g.constraints.bc",
    "g.constraints.embedded",
    "g.constraints.equal_dof",
    "g.constraints.kinematic_coupling",
    "g.constraints.node_to_surface",
    "g.constraints.node_to_surface_spring",
    "g.constraints.penalty",
    "g.constraints.resolve",
    "g.constraints.rigid_body",
    "g.constraints.rigid_diaphragm",
    "g.constraints.rigid_link",
    "g.constraints.tie",
    "g.constraints.tied_contact"
   ]
  },
  {
   "from": "constraint.resolver",
   "to": "fem.extract",
   "methods": [
    "fem.FEMData.from_gmsh"
   ]
  },
  {
   "from": "constraint.resolver",
   "to": "load.resolver",
   "methods": [
    "g.constraints.resolve_bcs"
   ]
  },
  {
   "from": "constraint.resolver",
   "to": "records",
   "methods": [
    "g.constraints.bc",
    "g.constraints.embedded",
    "g.constraints.equal_dof",
    "g.constraints.kinematic_coupling",
    "g.constraints.node_to_surface",
    "g.constraints.node_to_surface_spring",
    "g.constraints.penalty",
    "g.constraints.resolve",
    "g.constraints.resolve_bcs",
    "g.constraints.rigid_body",
    "g.constraints.rigid_diaphragm",
    "g.constraints.rigid_link",
    "g.constraints.tie",
    "g.constraints.tied_contact"
   ]
  },
  {
   "from": "constraints",
   "to": "constraint.defs",
   "methods": [
    "g.constraints.bc",
    "g.constraints.clear",
    "g.constraints.embedded",
    "g.constraints.equal_dof",
    "g.constraints.kinematic_coupling",
    "g.constraints.node_to_surface",
    "g.constraints.node_to_surface_spring",
    "g.constraints.penalty",
    "g.constraints.rigid_body",
    "g.constraints.rigid_diaphragm",
    "g.constraints.rigid_link",
    "g.constraints.tie",
    "g.constraints.tied_contact"
   ]
  },
  {
   "from": "cuts",
   "to": "cuts.planes",
   "methods": [
    "apeGmsh.cuts.SectionCutDef"
   ]
  },
  {
   "from": "cuts",
   "to": "cuts.stko",
   "methods": [
    "apeGmsh.cuts.SectionCutDef"
   ]
  },
  {
   "from": "cuts.drift",
   "to": "fem.nodes",
   "methods": [
    "apeGmsh.cuts.DriftDef",
    "apeGmsh.cuts.DriftSweepDef"
   ]
  },
  {
   "from": "cuts.h5",
   "to": "cuts",
   "methods": [
    "apeGmsh.cuts.persist_to_h5"
   ]
  },
  {
   "from": "cuts.h5",
   "to": "femdata.h5",
   "methods": [
    "apeGmsh.cuts.persist_to_h5"
   ]
  },
  {
   "from": "cuts.planes",
   "to": "fem.nodes",
   "methods": [
    "apeGmsh.cuts._planes"
   ]
  },
  {
   "from": "cuts.polygons",
   "to": "cuts.planes",
   "methods": [
    "apeGmsh.cuts.bounding_polygon_from_physical_surface"
   ]
  },
  {
   "from": "cuts.polygons",
   "to": "fem.nodes",
   "methods": [
    "apeGmsh.cuts.bounding_polygon_from_physical_surface"
   ]
  },
  {
   "from": "cuts.stko",
   "to": "cuts",
   "methods": [
    "apeGmsh.cuts.FemToOpsTagMap"
   ]
  },
  {
   "from": "cuts.stko",
   "to": "femdata.h5",
   "methods": [
    "apeGmsh.cuts.FemToOpsTagMap"
   ]
  },
  {
   "from": "cuts.sweeps",
   "to": "cuts",
   "methods": [
    "apeGmsh.cuts.SectionSweepDef"
   ]
  },
  {
   "from": "cuts.sweeps",
   "to": "cuts.stko",
   "methods": [
    "apeGmsh.cuts.SectionSweepDef"
   ]
  },
  {
   "from": "external",
   "to": "femdata",
   "methods": [
    "fem.FEMData.from_mpco_model",
    "fem.FEMData.snapshot_id"
   ]
  },
  {
   "from": "external",
   "to": "femdata.h5",
   "methods": [
    "h5_reader.open"
   ]
  },
  {
   "from": "external",
   "to": "gm.parsers",
   "methods": [
    "gm.from_obspy"
   ]
  },
  {
   "from": "external",
   "to": "results.capture",
   "methods": [
    "results.capture.DomainCapture.capture_modes",
    "results.capture.DomainCapture.step"
   ]
  },
  {
   "from": "fem.elements",
   "to": "femdata",
   "methods": [
    "g.constraints.embedded",
    "g.constraints.tie",
    "g.constraints.tied_contact"
   ]
  },
  {
   "from": "fem.elements",
   "to": "gmsh",
   "methods": [
    "fem.elements.get"
   ]
  },
  {
   "from": "fem.elements",
   "to": "labels",
   "methods": [
    "fem.elements.labels"
   ]
  },
  {
   "from": "fem.elements",
   "to": "physical",
   "methods": [
    "fem.elements.get",
    "fem.elements.physical"
   ]
  },
  {
   "from": "fem.elements",
   "to": "records",
   "methods": [
    "fem.elements.constraints",
    "fem.elements.constraints.by_kind",
    "fem.elements.constraints.couplings",
    "fem.elements.constraints.interpolations",
    "fem.elements.constraints.iter_index",
    "fem.elements.constraints.summary",
    "fem.elements.loads",
    "fem.elements.loads.by_kind",
    "fem.elements.loads.by_pattern",
    "fem.elements.loads.iter",
    "fem.elements.loads.patterns",
    "fem.elements.loads.summary"
   ]
  },
  {
   "from": "fem.elements",
   "to": "results.readers",
   "methods": [
    "results.elements.get"
   ]
  },
  {
   "from": "fem.elements",
   "to": "selection",
   "methods": [
    "fem.elements.select",
    "selection.ElementChain.in_box",
    "selection.ElementChain.in_sphere",
    "selection.ElementChain.nearest_to",
    "selection.ElementChain.on_plane",
    "selection.ElementChain.result",
    "selection.ElementChain.where"
   ]
  },
  {
   "from": "fem.extract",
   "to": "constraint.resolver",
   "methods": [
    "fem.FEMData.from_gmsh"
   ]
  },
  {
   "from": "fem.extract",
   "to": "femdata",
   "methods": [
    "MshLoader.load",
    "fem.FEMData.from_gmsh",
    "g.loader.from_msh",
    "g.mesh.queries.get_fem_data"
   ]
  },
  {
   "from": "fem.extract",
   "to": "gmsh",
   "methods": [
    "fem.FEMData.from_gmsh"
   ]
  },
  {
   "from": "fem.extract",
   "to": "loader",
   "methods": [
    "fem.FEMData.from_msh"
   ]
  },
  {
   "from": "fem.extract",
   "to": "viz.vtkexport",
   "methods": [
    "viz.vtkexport.VTKExport"
   ]
  },
  {
   "from": "fem.hrz",
   "to": "fem.quadrature",
   "methods": [
    "fem.hrz.hrz_weights",
    "fem.hrz.reference_quadrature"
   ]
  },
  {
   "from": "fem.hrz",
   "to": "fem.shape",
   "methods": [
    "fem.hrz.hrz_weights"
   ]
  },
  {
   "from": "fem.hrz",
   "to": "mass.resolver",
   "methods": [
    "fem.hrz.volume_code"
   ]
  },
  {
   "from": "fem.inspect",
   "to": "fem.elements",
   "methods": [
    "fem.inspect.element_table"
   ]
  },
  {
   "from": "fem.inspect",
   "to": "fem.nodes",
   "methods": [
    "fem.inspect.node_table"
   ]
  },
  {
   "from": "fem.inspect",
   "to": "labels",
   "methods": [
    "fem.inspect.label_table"
   ]
  },
  {
   "from": "fem.inspect",
   "to": "physical",
   "methods": [
    "fem.inspect.physical_table"
   ]
  },
  {
   "from": "fem.inspect",
   "to": "records",
   "methods": [
    "fem.inspect.constraint_summary",
    "fem.inspect.load_summary",
    "fem.inspect.mass_summary"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "femdata",
   "methods": [
    "g.constraints.bc",
    "g.constraints.equal_dof",
    "g.constraints.kinematic_coupling",
    "g.constraints.node_to_surface",
    "g.constraints.node_to_surface_spring",
    "g.constraints.penalty",
    "g.constraints.resolve",
    "g.constraints.rigid_body",
    "g.constraints.rigid_diaphragm",
    "g.constraints.rigid_link",
    "g.loads.body",
    "g.loads.face_load",
    "g.loads.face_sp",
    "g.loads.gravity",
    "g.loads.line",
    "g.loads.point",
    "g.loads.point_closest",
    "g.loads.resolve",
    "g.loads.surface",
    "g.masses.line",
    "g.masses.point",
    "g.masses.resolve",
    "g.masses.surface",
    "g.masses.volume"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "gmsh",
   "methods": [
    "fem.nodes.get"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "labels",
   "methods": [
    "fem.nodes.labels"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "os.node",
   "methods": [
    "apeSees.nodes.get",
    "apeSees.nodes.summary"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "physical",
   "methods": [
    "fem.nodes.get",
    "fem.nodes.physical"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "records",
   "methods": [
    "fem.nodes.constraints",
    "fem.nodes.constraints.by_kind",
    "fem.nodes.constraints.equal_dofs",
    "fem.nodes.constraints.iter_index",
    "fem.nodes.constraints.node_to_surfaces",
    "fem.nodes.constraints.pairs",
    "fem.nodes.constraints.phantom_nodes",
    "fem.nodes.constraints.rigid_diaphragms",
    "fem.nodes.constraints.rigid_link_groups",
    "fem.nodes.constraints.stiff_beam_groups",
    "fem.nodes.constraints.summary",
    "fem.nodes.loads",
    "fem.nodes.loads.by_kind",
    "fem.nodes.loads.by_pattern",
    "fem.nodes.loads.iter",
    "fem.nodes.loads.patterns",
    "fem.nodes.loads.summary",
    "fem.nodes.masses",
    "fem.nodes.masses.by_kind",
    "fem.nodes.masses.by_node",
    "fem.nodes.masses.iter",
    "fem.nodes.masses.summary",
    "fem.nodes.masses.total_mass",
    "fem.nodes.sp",
    "fem.nodes.sp.by_kind",
    "fem.nodes.sp.by_node",
    "fem.nodes.sp.homogeneous",
    "fem.nodes.sp.iter",
    "fem.nodes.sp.prescribed",
    "records.NodeConstraintSet.phantom_nodes"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "results.plot",
   "methods": [
    "results.plot.loads"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "results.readers",
   "methods": [
    "results.nodes.get"
   ]
  },
  {
   "from": "fem.nodes",
   "to": "selection",
   "methods": [
    "fem.nodes.select",
    "selection.NodeChain.in_box",
    "selection.NodeChain.in_sphere",
    "selection.NodeChain.nearest_to",
    "selection.NodeChain.on_plane",
    "selection.NodeChain.result",
    "selection.NodeChain.where"
   ]
  },
  {
   "from": "fem.quadrature",
   "to": "fem.hrz",
   "methods": [
    "fem.quadrature.gauss_hex_3d",
    "fem.quadrature.gauss_legendre_1d",
    "fem.quadrature.gauss_quad_2d",
    "fem.quadrature.gauss_tet",
    "fem.quadrature.gauss_tri",
    "fem.quadrature.gauss_wedge"
   ]
  },
  {
   "from": "fem.quadrature",
   "to": "mass.resolver",
   "methods": [
    "fem.hrz.hrz_weights",
    "fem.hrz.reference_quadrature"
   ]
  },
  {
   "from": "fem.shape",
   "to": "fem.extract",
   "methods": [
    "fem.shape.compute_jacobian_dets",
    "fem.shape.compute_physical_coords",
    "fem.shape.element_primitives",
    "fem.shape.get_shape_functions"
   ]
  },
  {
   "from": "fem.shape",
   "to": "fem.hrz",
   "methods": [
    "fem.hrz.hrz_weights"
   ]
  },
  {
   "from": "femdata",
   "to": "external",
   "methods": [
    "fem.FEMData.from_mpco_model",
    "fem.FEMData.snapshot_id"
   ]
  },
  {
   "from": "femdata",
   "to": "fem.elements",
   "methods": [
    "fem.FEMData.__init__",
    "fem.FEMData.elements"
   ]
  },
  {
   "from": "femdata",
   "to": "fem.extract",
   "methods": [
    "fem.FEMData.from_gmsh"
   ]
  },
  {
   "from": "femdata",
   "to": "fem.info",
   "methods": [
    "fem.FEMData.info"
   ]
  },
  {
   "from": "femdata",
   "to": "fem.inspect",
   "methods": [
    "fem.FEMData.__repr__",
    "fem.FEMData.inspect"
   ]
  },
  {
   "from": "femdata",
   "to": "fem.nodes",
   "methods": [
    "fem.FEMData.nodes",
    "fem.FEMData.partitions"
   ]
  },
  {
   "from": "femdata",
   "to": "femdata.h5",
   "methods": [
    "fem.FEMData.from_h5",
    "fem.FEMData.from_native_h5",
    "fem.FEMData.to_h5",
    "fem.FEMData.to_native_h5"
   ]
  },
  {
   "from": "femdata",
   "to": "loader",
   "methods": [
    "fem.FEMData.from_msh"
   ]
  },
  {
   "from": "femdata",
   "to": "mesh.queries",
   "methods": [
    "g.mesh.queries.get_fem_data"
   ]
  },
  {
   "from": "femdata",
   "to": "results.spec",
   "methods": [
    "results.spec.DomainCaptureSpec.resolve"
   ]
  },
  {
   "from": "femdata.h5",
   "to": "external",
   "methods": [
    "h5_reader.open"
   ]
  },
  {
   "from": "femdata.h5",
   "to": "femdata",
   "methods": [
    "fem.FEMData.from_h5",
    "fem.FEMData.from_native_h5"
   ]
  },
  {
   "from": "femdata.h5",
   "to": "results.readers",
   "methods": [
    "results.writers.NativeWriter"
   ]
  },
  {
   "from": "femdata.h5",
   "to": "results.spec",
   "methods": [
    "results.spec.ResolvedRecorderSpec.from_manifest_h5"
   ]
  },
  {
   "from": "gm",
   "to": "apesees",
   "methods": [
    "gm.GroundMotion.to_time_series"
   ]
  },
  {
   "from": "gm",
   "to": "gm.parsers",
   "methods": [
    "gm.from_file"
   ]
  },
  {
   "from": "gm",
   "to": "gm.sniffer",
   "methods": [
    "gm.from_file"
   ]
  },
  {
   "from": "gm.parsers",
   "to": "external",
   "methods": [
    "gm.from_obspy"
   ]
  },
  {
   "from": "gm.parsers",
   "to": "gm",
   "methods": [
    "gm.from_file",
    "gm.from_itaca",
    "gm.from_obspy",
    "gm.from_obspy_trace",
    "gm.from_one_column",
    "gm.from_peer_at2",
    "gm.from_two_column"
   ]
  },
  {
   "from": "gm.parsers",
   "to": "gm.sniffer",
   "methods": [
    "gm.sniff_format"
   ]
  },
  {
   "from": "gm.sniffer",
   "to": "gm",
   "methods": [
    "gm.from_file",
    "gm.sniff_format"
   ]
  },
  {
   "from": "gm.sniffer",
   "to": "gm.parsers",
   "methods": [
    "gm.sniff_format"
   ]
  },
  {
   "from": "gmsh",
   "to": "labels",
   "methods": [
    "g.model.boolean.fragment",
    "g.model.geometry.add_axis_cutting_plane",
    "g.model.geometry.cut_by_plane",
    "g.model.transforms.sweep"
   ]
  },
  {
   "from": "gmsh",
   "to": "model.geometry",
   "methods": [
    "g.model.geometry.add_arc",
    "g.model.geometry.add_bezier",
    "g.model.geometry.add_box",
    "g.model.geometry.add_bspline",
    "g.model.geometry.add_circle",
    "g.model.geometry.add_cone",
    "g.model.geometry.add_curve_loop",
    "g.model.geometry.add_cutting_plane",
    "g.model.geometry.add_cylinder",
    "g.model.geometry.add_ellipse",
    "g.model.geometry.add_imperfect_line",
    "g.model.geometry.add_line",
    "g.model.geometry.add_plane_surface",
    "g.model.geometry.add_point",
    "g.model.geometry.add_rectangle",
    "g.model.geometry.add_sphere",
    "g.model.geometry.add_spline",
    "g.model.geometry.add_surface_filling",
    "g.model.geometry.add_torus",
    "g.model.geometry.add_wedge",
    "g.model.geometry.add_wire",
    "g.model.geometry.cut_by_surface",
    "g.model.geometry.replace_line",
    "g.model.geometry.slice",
    "g.model.geometry.sweep"
   ]
  },
  {
   "from": "gmsh",
   "to": "model.io",
   "methods": [
    "g.model.io.heal_shapes",
    "g.model.io.load_geo",
    "g.model.io.load_iges",
    "g.model.io.load_msh",
    "g.model.io.load_step",
    "g.model.io.save_dxf",
    "g.model.io.save_iges",
    "g.model.io.save_msh",
    "g.model.io.save_step"
   ]
  },
  {
   "from": "gmsh",
   "to": "model.queries",
   "methods": [
    "g.model.gui",
    "g.model.launch_picker",
    "g.model.queries.adjacencies",
    "g.model.queries.boundary",
    "g.model.queries.boundary_curves",
    "g.model.queries.boundary_points",
    "g.model.queries.entities_in_bounding_box",
    "g.model.queries.remove",
    "g.model.queries.remove_duplicates",
    "g.model.queries.select",
    "g.model.sync"
   ]
  },
  {
   "from": "gmsh",
   "to": "model.transforms",
   "methods": [
    "g.model.transforms.copy",
    "g.model.transforms.extrude",
    "g.model.transforms.mirror",
    "g.model.transforms.revolve",
    "g.model.transforms.rotate",
    "g.model.transforms.scale",
    "g.model.transforms.thru_sections",
    "g.model.transforms.translate"
   ]
  },
  {
   "from": "gmsh",
   "to": "physical",
   "methods": [
    "g.model.io.load_dxf",
    "g.model.queries.make_conformal"
   ]
  },
  {
   "from": "gmsh",
   "to": "selection",
   "methods": [
    "GeometryChain.in_box",
    "GeometryChain.in_sphere",
    "GeometryChain.nearest_to",
    "GeometryChain.on_plane",
    "GeometryChain.where",
    "Selection.normal_along",
    "Selection.parallel_to",
    "Selection.partition_by",
    "Selection.select",
    "g.model.queries.select_all",
    "g.model.queries.select_all_curves",
    "g.model.queries.select_all_points",
    "g.model.queries.select_all_surfaces",
    "g.model.queries.select_all_volumes"
   ]
  },
  {
   "from": "gmsh",
   "to": "session",
   "methods": [
    "viz.selection.Selection.centers",
    "viz.selection.Selection.masses",
    "viz.selection.Selection.to_mesh_elements",
    "viz.selection.Selection.to_mesh_nodes"
   ]
  },
  {
   "from": "gmsh",
   "to": "viz.inspect",
   "methods": [
    "viz.inspect.get_geometry_info",
    "viz.inspect.get_mesh_info",
    "viz.inspect.print_summary"
   ]
  },
  {
   "from": "gmsh",
   "to": "viz.plot",
   "methods": [
    "viz.plot.geometry",
    "viz.plot.label_elements",
    "viz.plot.label_entities",
    "viz.plot.label_nodes",
    "viz.plot.mesh",
    "viz.plot.physical_groups",
    "viz.plot.physical_groups_mesh",
    "viz.plot.quality"
   ]
  },
  {
   "from": "gmsh",
   "to": "viz.selection",
   "methods": [
    "viz.selection.Selection.bbox",
    "viz.selection.Selection.sorted_by",
    "viz.selection.Selection.to_dataframe",
    "viz.selection.SelectionComposite.adjacent_to",
    "viz.selection.SelectionComposite.boundary_of",
    "viz.selection.SelectionComposite.closest_to",
    "viz.selection.SelectionComposite.select_all",
    "viz.selection.SelectionComposite.select_curves",
    "viz.selection.SelectionComposite.select_points",
    "viz.selection.SelectionComposite.select_surfaces",
    "viz.selection.SelectionComposite.select_volumes"
   ]
  },
  {
   "from": "labels",
   "to": "gmsh",
   "methods": [
    "g.labels.add",
    "g.labels.entities",
    "g.labels.get_all",
    "g.labels.labels_for_entity",
    "g.labels.promote_to_physical",
    "g.labels.remove",
    "g.labels.rename",
    "g.labels.reverse_map",
    "g.labels.summary",
    "g.model.geometry.add_arc",
    "g.model.geometry.add_bezier",
    "g.model.geometry.add_bspline",
    "g.model.geometry.add_curve_loop",
    "g.model.geometry.add_imperfect_line",
    "g.model.geometry.add_line",
    "g.model.geometry.add_plane_surface",
    "g.model.geometry.add_spline",
    "g.model.geometry.add_surface_filling",
    "g.model.geometry.add_wire",
    "g.model.queries.boundary",
    "g.model.queries.boundary_curves",
    "g.model.queries.boundary_points",
    "g.model.transforms.copy",
    "g.model.transforms.extrude",
    "g.model.transforms.mirror",
    "g.model.transforms.revolve",
    "g.model.transforms.rotate",
    "g.model.transforms.scale",
    "g.model.transforms.sweep",
    "g.model.transforms.translate"
   ]
  },
  {
   "from": "labels",
   "to": "model.geometry",
   "methods": [
    "g.model.geometry.cut_by_plane"
   ]
  },
  {
   "from": "labels",
   "to": "model.queries",
   "methods": [
    "g.model.queries.bounding_box",
    "g.model.queries.center_of_mass",
    "g.model.queries.mass",
    "g.model.queries.registry"
   ]
  },
  {
   "from": "labels",
   "to": "physical",
   "methods": [
    "Selection.to_label",
    "g.labels.promote_to_physical",
    "g.model.boolean.cut",
    "g.model.boolean.fuse",
    "g.model.boolean.intersect",
    "g.model.geometry.add_box",
    "g.model.geometry.add_imperfect_line",
    "g.model.geometry.add_point",
    "g.model.geometry.cut_by_surface",
    "g.model.geometry.sweep"
   ]
  },
  {
   "from": "labels",
   "to": "selection",
   "methods": [
    "g.model.queries.select",
    "g.model.select"
   ]
  },
  {
   "from": "load.defs",
   "to": "load.resolver",
   "methods": [
    "g.loads.body",
    "g.loads.face_load",
    "g.loads.face_sp",
    "g.loads.gravity",
    "g.loads.line",
    "g.loads.pattern",
    "g.loads.point",
    "g.loads.point_closest",
    "g.loads.resolve",
    "g.loads.surface"
   ]
  },
  {
   "from": "load.resolver",
   "to": "constraint.resolver",
   "methods": [
    "g.constraints.resolve_bcs"
   ]
  },
  {
   "from": "load.resolver",
   "to": "fem.extract",
   "methods": [
    "fem.FEMData.from_gmsh"
   ]
  },
  {
   "from": "load.resolver",
   "to": "records",
   "methods": [
    "g.loads.body",
    "g.loads.face_load",
    "g.loads.face_sp",
    "g.loads.gravity",
    "g.loads.line",
    "g.loads.pattern",
    "g.loads.point",
    "g.loads.point_closest",
    "g.loads.resolve",
    "g.loads.surface"
   ]
  },
  {
   "from": "loader",
   "to": "fem.extract",
   "methods": [
    "MshLoader.load",
    "g.loader.from_msh"
   ]
  },
  {
   "from": "loader",
   "to": "femdata",
   "methods": [
    "fem.FEMData.from_msh"
   ]
  },
  {
   "from": "loader",
   "to": "gmsh",
   "methods": [
    "fem.FEMData.from_msh",
    "g.loader.from_msh"
   ]
  },
  {
   "from": "loads",
   "to": "load.defs",
   "methods": [
    "g.loads.body",
    "g.loads.face_load",
    "g.loads.face_sp",
    "g.loads.gravity",
    "g.loads.line",
    "g.loads.pattern",
    "g.loads.point",
    "g.loads.point_closest",
    "g.loads.surface",
    "g.loads.validate_pre_mesh"
   ]
  },
  {
   "from": "mass.defs",
   "to": "mass.resolver",
   "methods": [
    "g.masses.line",
    "g.masses.point",
    "g.masses.resolve",
    "g.masses.surface",
    "g.masses.volume"
   ]
  },
  {
   "from": "mass.resolver",
   "to": "fem.extract",
   "methods": [
    "fem.FEMData.from_gmsh"
   ]
  },
  {
   "from": "mass.resolver",
   "to": "records",
   "methods": [
    "g.masses.line",
    "g.masses.point",
    "g.masses.resolve",
    "g.masses.surface",
    "g.masses.volume"
   ]
  },
  {
   "from": "masses",
   "to": "mass.defs",
   "methods": [
    "g.masses.line",
    "g.masses.point",
    "g.masses.surface",
    "g.masses.validate_pre_mesh",
    "g.masses.volume"
   ]
  },
  {
   "from": "mesh.editing",
   "to": "gmsh",
   "methods": [
    "g.mesh.editing.affine_transform",
    "g.mesh.editing.classify_surfaces",
    "g.mesh.editing.clear",
    "g.mesh.editing.crack",
    "g.mesh.editing.create_geometry",
    "g.mesh.editing.embed",
    "g.mesh.editing.import_stl",
    "g.mesh.editing.relocate_nodes",
    "g.mesh.editing.remove_duplicate_elements",
    "g.mesh.editing.remove_duplicate_nodes",
    "g.mesh.editing.reverse",
    "g.mesh.editing.set_periodic"
   ]
  },
  {
   "from": "mesh.editing",
   "to": "physical",
   "methods": [
    "g.mesh.editing.crack"
   ]
  },
  {
   "from": "mesh.field",
   "to": "gmsh",
   "methods": [
    "g.mesh.field.add",
    "g.mesh.field.boundary_layer",
    "g.mesh.field.box",
    "g.mesh.field.distance",
    "g.mesh.field.math_eval",
    "g.mesh.field.minimum",
    "g.mesh.field.set_background",
    "g.mesh.field.set_boundary_layer_field",
    "g.mesh.field.set_number",
    "g.mesh.field.set_numbers",
    "g.mesh.field.set_string",
    "g.mesh.field.threshold"
   ]
  },
  {
   "from": "mesh.generation",
   "to": "gmsh",
   "methods": [
    "g.mesh.generation.generate",
    "g.mesh.generation.optimize",
    "g.mesh.generation.refine",
    "g.mesh.generation.set_algorithm",
    "g.mesh.generation.set_order"
   ]
  },
  {
   "from": "mesh.generation",
   "to": "loads",
   "methods": [
    "g.mesh.generation.generate"
   ]
  },
  {
   "from": "mesh.partitioning",
   "to": "fem.extract",
   "methods": [
    "g.mesh.partitioning.renumber"
   ]
  },
  {
   "from": "mesh.partitioning",
   "to": "gmsh",
   "methods": [
    "g.mesh.partitioning.entity_table",
    "g.mesh.partitioning.n_partitions",
    "g.mesh.partitioning.partition",
    "g.mesh.partitioning.partition_explicit",
    "g.mesh.partitioning.renumber",
    "g.mesh.partitioning.save",
    "g.mesh.partitioning.unpartition"
   ]
  },
  {
   "from": "mesh.queries",
   "to": "external",
   "methods": [
    "g.mesh.preview",
    "g.mesh.results_viewer",
    "g.mesh.viewer"
   ]
  },
  {
   "from": "mesh.queries",
   "to": "fem.extract",
   "methods": [
    "g.mesh.queries.get_fem_data"
   ]
  },
  {
   "from": "mesh.queries",
   "to": "gmsh",
   "methods": [
    "g.mesh.options.get_algorithm_2d",
    "g.mesh.options.get_algorithm_3d",
    "g.mesh.options.get_element_order",
    "g.mesh.options.get_recombination_algorithm",
    "g.mesh.options.get_smoothing",
    "g.mesh.options.get_subdivision_algorithm",
    "g.mesh.options.set_algorithm_2d",
    "g.mesh.options.set_algorithm_3d",
    "g.mesh.options.set_element_order",
    "g.mesh.options.set_recombination_algorithm",
    "g.mesh.options.set_smoothing",
    "g.mesh.options.set_subdivision_algorithm",
    "g.mesh.queries.get_element_properties",
    "g.mesh.queries.get_element_qualities",
    "g.mesh.queries.get_elements",
    "g.mesh.queries.get_nodes",
    "g.mesh.queries.quality_report"
   ]
  },
  {
   "from": "mesh.sizing",
   "to": "gmsh",
   "methods": [
    "g.mesh.sizing.set_global_size",
    "g.mesh.sizing.set_size",
    "g.mesh.sizing.set_size_all_points",
    "g.mesh.sizing.set_size_callback",
    "g.mesh.sizing.set_size_global",
    "g.mesh.sizing.set_size_sources"
   ]
  },
  {
   "from": "mesh.sizing",
   "to": "physical",
   "methods": [
    "g.mesh.sizing.set_size_by_physical"
   ]
  },
  {
   "from": "mesh.structured",
   "to": "external",
   "methods": [
    "g.mesh.structured.set_transfinite"
   ]
  },
  {
   "from": "mesh.structured",
   "to": "gmsh",
   "methods": [
    "g.mesh.structured.recombine",
    "g.mesh.structured.remove_constraints",
    "g.mesh.structured.set_compound",
    "g.mesh.structured.set_recombine",
    "g.mesh.structured.set_smoothing",
    "g.mesh.structured.set_transfinite_automatic",
    "g.mesh.structured.set_transfinite_curve",
    "g.mesh.structured.set_transfinite_surface",
    "g.mesh.structured.set_transfinite_volume"
   ]
  },
  {
   "from": "mesh.structured",
   "to": "model.queries",
   "methods": [
    "g.mesh.structured.set_transfinite",
    "g.mesh.structured.set_transfinite_box"
   ]
  },
  {
   "from": "mesh_selection",
   "to": "gmsh",
   "methods": [
    "MeshSelectionChain.result",
    "g.mesh_selection.add",
    "g.mesh_selection.add_elements",
    "g.mesh_selection.add_nodes",
    "g.mesh_selection.from_physical",
    "g.mesh_selection.select"
   ]
  },
  {
   "from": "model.boolean",
   "to": "gmsh",
   "methods": [
    "g.model.boolean.fragment"
   ]
  },
  {
   "from": "model.boolean",
   "to": "labels",
   "methods": [
    "g.model.boolean.cut",
    "g.model.boolean.fuse",
    "g.model.boolean.intersect"
   ]
  },
  {
   "from": "model.boolean",
   "to": "physical",
   "methods": [
    "g.model.boolean.fragment"
   ]
  },
  {
   "from": "model.geometry",
   "to": "gmsh",
   "methods": [
    "g.model.geometry.add_axis_cutting_plane",
    "g.model.geometry.add_box",
    "g.model.geometry.add_circle",
    "g.model.geometry.add_cone",
    "g.model.geometry.add_cutting_plane",
    "g.model.geometry.add_cylinder",
    "g.model.geometry.add_ellipse",
    "g.model.geometry.add_point",
    "g.model.geometry.add_rectangle",
    "g.model.geometry.add_sphere",
    "g.model.geometry.add_torus",
    "g.model.geometry.add_wedge",
    "g.model.geometry.cut_by_plane",
    "g.model.geometry.replace_line",
    "g.model.geometry.slice",
    "g.model.geometry.sweep"
   ]
  },
  {
   "from": "model.geometry",
   "to": "labels",
   "methods": [
    "g.model.geometry.add_arc",
    "g.model.geometry.add_bezier",
    "g.model.geometry.add_box",
    "g.model.geometry.add_bspline",
    "g.model.geometry.add_circle",
    "g.model.geometry.add_cone",
    "g.model.geometry.add_curve_loop",
    "g.model.geometry.add_cutting_plane",
    "g.model.geometry.add_cylinder",
    "g.model.geometry.add_ellipse",
    "g.model.geometry.add_imperfect_line",
    "g.model.geometry.add_line",
    "g.model.geometry.add_plane_surface",
    "g.model.geometry.add_point",
    "g.model.geometry.add_rectangle",
    "g.model.geometry.add_sphere",
    "g.model.geometry.add_spline",
    "g.model.geometry.add_surface_filling",
    "g.model.geometry.add_torus",
    "g.model.geometry.add_wedge",
    "g.model.geometry.add_wire",
    "g.model.geometry.cut_by_plane",
    "g.model.geometry.cut_by_surface",
    "g.model.geometry.slice",
    "g.model.geometry.sweep"
   ]
  },
  {
   "from": "model.io",
   "to": "gmsh",
   "methods": [
    "g.model.io.heal_shapes",
    "g.model.io.load_dxf",
    "g.model.io.load_geo",
    "g.model.io.load_iges",
    "g.model.io.load_msh",
    "g.model.io.load_step",
    "g.model.io.save_dxf",
    "g.model.io.save_iges",
    "g.model.io.save_msh",
    "g.model.io.save_step"
   ]
  },
  {
   "from": "model.io",
   "to": "labels",
   "methods": [
    "g.model.io.load_iges",
    "g.model.io.load_step"
   ]
  },
  {
   "from": "model.queries",
   "to": "gmsh",
   "methods": [
    "g.model.gui",
    "g.model.launch_picker",
    "g.model.queries.adjacencies",
    "g.model.queries.bounding_box",
    "g.model.queries.center_of_mass",
    "g.model.queries.entities_in_bounding_box",
    "g.model.queries.make_conformal",
    "g.model.queries.mass",
    "g.model.queries.remove",
    "g.model.queries.remove_duplicates",
    "g.model.queries.select_all",
    "g.model.queries.select_all_curves",
    "g.model.queries.select_all_points",
    "g.model.queries.select_all_surfaces",
    "g.model.queries.select_all_volumes",
    "g.model.sync"
   ]
  },
  {
   "from": "model.queries",
   "to": "labels",
   "methods": [
    "g.model.queries.boundary",
    "g.model.queries.boundary_curves",
    "g.model.queries.boundary_points",
    "g.model.queries.bounding_box",
    "g.model.queries.center_of_mass",
    "g.model.queries.mass",
    "g.model.queries.registry",
    "g.model.queries.remove",
    "g.model.queries.remove_duplicates",
    "g.model.queries.select",
    "g.model.select"
   ]
  },
  {
   "from": "model.queries",
   "to": "physical",
   "methods": [
    "g.model.queries.fragment_all"
   ]
  },
  {
   "from": "model.queries",
   "to": "selection",
   "methods": [
    "g.model.queries.line",
    "g.model.queries.plane",
    "g.model.viewer"
   ]
  },
  {
   "from": "model.queries",
   "to": "session",
   "methods": [
    "g.model.preview"
   ]
  },
  {
   "from": "model.transforms",
   "to": "gmsh",
   "methods": [
    "g.model.transforms.thru_sections"
   ]
  },
  {
   "from": "model.transforms",
   "to": "labels",
   "methods": [
    "g.model.transforms.copy",
    "g.model.transforms.extrude",
    "g.model.transforms.mirror",
    "g.model.transforms.revolve",
    "g.model.transforms.rotate",
    "g.model.transforms.scale",
    "g.model.transforms.sweep",
    "g.model.transforms.thru_sections",
    "g.model.transforms.translate"
   ]
  },
  {
   "from": "os.analysis",
   "to": "os.emit",
   "methods": [
    "apeSees.analysis.AMD",
    "apeSees.analysis.ArcLength",
    "apeSees.analysis.BFGS",
    "apeSees.analysis.BandGeneral",
    "apeSees.analysis.BandSPD",
    "apeSees.analysis.Broyden",
    "apeSees.analysis.CentralDifference",
    "apeSees.analysis.DisplacementControl",
    "apeSees.analysis.EnergyIncr",
    "apeSees.analysis.ExplicitDifference",
    "apeSees.analysis.FixedNumIter",
    "apeSees.analysis.FullGeneral",
    "apeSees.analysis.HHT",
    "apeSees.analysis.KrylovNewton",
    "apeSees.analysis.Lagrange",
    "apeSees.analysis.Linear",
    "apeSees.analysis.LoadControl",
    "apeSees.analysis.ModifiedNewton",
    "apeSees.analysis.Mumps",
    "apeSees.analysis.Newmark",
    "apeSees.analysis.Newton",
    "apeSees.analysis.NewtonLineSearch",
    "apeSees.analysis.NormDispIncr",
    "apeSees.analysis.NormUnbalance",
    "apeSees.analysis.Penalty",
    "apeSees.analysis.PlainConstraints",
    "apeSees.analysis.PlainNumberer",
    "apeSees.analysis.ProfileSPD",
    "apeSees.analysis.RCM",
    "apeSees.analysis.RelativeNormDispIncr",
    "apeSees.analysis.SparseGeneral",
    "apeSees.analysis.Static",
    "apeSees.analysis.Transformation",
    "apeSees.analysis.Transient",
    "apeSees.analysis.UmfPack",
    "apeSees.analysis.VariableTransient"
   ]
  },
  {
   "from": "os.build",
   "to": "os.emit",
   "methods": [
    "BuiltModel",
    "apeSees.analyze",
    "apeSees.export.h5",
    "apeSees.export.py",
    "apeSees.export.tcl",
    "apeSees.fix",
    "apeSees.h5",
    "apeSees.live.analyze",
    "apeSees.live.run",
    "apeSees.mass",
    "apeSees.pattern.Plain",
    "apeSees.pattern.UniformExcitation",
    "apeSees.py",
    "apeSees.recorder.Element",
    "apeSees.recorder.MPCO",
    "apeSees.recorder.Node",
    "apeSees.recorder.RecorderDeclaration",
    "apeSees.run",
    "apeSees.tcl"
   ]
  },
  {
   "from": "os.emit",
   "to": "external",
   "methods": [
    "BuiltModel.emit",
    "apeSees.analyze",
    "apeSees.beamIntegration.HingeEndpoint",
    "apeSees.beamIntegration.HingeMidpoint",
    "apeSees.beamIntegration.HingeRadau",
    "apeSees.beamIntegration.HingeRadauTwo",
    "apeSees.beamIntegration.Legendre",
    "apeSees.beamIntegration.Lobatto",
    "apeSees.beamIntegration.NewtonCotes",
    "apeSees.beamIntegration.Radau",
    "apeSees.beamIntegration.Trapezoidal",
    "apeSees.element.ASDShellQ4",
    "apeSees.element.ASDShellT3",
    "apeSees.element.CorotTruss",
    "apeSees.element.ElasticTimoshenkoBeam",
    "apeSees.element.FourNodeQuad",
    "apeSees.element.FourNodeTetrahedron",
    "apeSees.element.InertiaTruss",
    "apeSees.element.ShellDKGQ",
    "apeSees.element.ShellMITC3",
    "apeSees.element.ShellMITC4",
    "apeSees.element.TenNodeTetrahedron",
    "apeSees.element.Tri31",
    "apeSees.element.Truss",
    "apeSees.element.ZeroLength",
    "apeSees.element.ZeroLengthSection",
    "apeSees.element.dispBeamColumn",
    "apeSees.element.elasticBeamColumn",
    "apeSees.element.forceBeamColumn",
    "apeSees.element.stdBrick",
    "apeSees.export.py",
    "apeSees.export.tcl",
    "apeSees.geomTransf.Corotational",
    "apeSees.geomTransf.Linear",
    "apeSees.geomTransf.PDelta",
    "apeSees.live.analyze",
    "apeSees.live.run",
    "apeSees.nDMaterial.DruckerPrager",
    "apeSees.nDMaterial.ElasticIsotropic",
    "apeSees.nDMaterial.J2Plasticity",
    "apeSees.py",
    "apeSees.section.ElasticMembranePlateSection",
    "apeSees.section.ElasticSection",
    "apeSees.section.Fiber",
    "apeSees.section.LayeredShell",
    "apeSees.section.LayeredShellFiberSection",
    "apeSees.tcl",
    "apeSees.uniaxialMaterial.ASDSteel1D",
    "apeSees.uniaxialMaterial.Concrete01",
    "apeSees.uniaxialMaterial.Concrete02",
    "apeSees.uniaxialMaterial.ENT",
    "apeSees.uniaxialMaterial.ElasticMaterial",
    "apeSees.uniaxialMaterial.Hysteretic",
    "apeSees.uniaxialMaterial.Steel01",
    "apeSees.uniaxialMaterial.Steel02"
   ]
  },
  {
   "from": "os.emit",
   "to": "fem.elements",
   "methods": [
    "AlongBeam"
   ]
  },
  {
   "from": "os.emit",
   "to": "fem.nodes",
   "methods": [
    "BuiltModel.emit"
   ]
  },
  {
   "from": "os.emit",
   "to": "femdata.h5",
   "methods": [
    "apeSees.export.h5",
    "apeSees.h5"
   ]
  },
  {
   "from": "os.emit",
   "to": "os.registry",
   "methods": [
    "BuiltModel.emit"
   ]
  },
  {
   "from": "os.emit",
   "to": "results.capture",
   "methods": [
    "results.capture.DomainCapture.from_h5"
   ]
  },
  {
   "from": "os.emit",
   "to": "results.live",
   "methods": [
    "results.live.LiveMPCO",
    "results.live.LiveRecorders.begin_stage"
   ]
  },
  {
   "from": "os.node",
   "to": "apesees",
   "methods": [
    "Node",
    "Node.fix",
    "Node.mass",
    "NodeSet",
    "NodeSet.fix",
    "NodeSet.mass"
   ]
  },
  {
   "from": "os.node",
   "to": "fem.nodes",
   "methods": [
    "apeSees.nodes.get",
    "apeSees.nodes.summary"
   ]
  },
  {
   "from": "os.pattern",
   "to": "os.build",
   "methods": [
    "apeSees.pattern.Plain",
    "apeSees.pattern.UniformExcitation"
   ]
  },
  {
   "from": "os.recorder",
   "to": "os.build",
   "methods": [
    "apeSees.recorder.Element",
    "apeSees.recorder.MPCO",
    "apeSees.recorder.Node",
    "apeSees.recorder.RecorderDeclaration"
   ]
  },
  {
   "from": "os.registry",
   "to": "apesees",
   "methods": [
    "apeSees.tag_for"
   ]
  },
  {
   "from": "os.registry",
   "to": "os.build",
   "methods": [
    "apeSees.beamIntegration.HingeEndpoint",
    "apeSees.beamIntegration.HingeMidpoint",
    "apeSees.beamIntegration.HingeRadau",
    "apeSees.beamIntegration.HingeRadauTwo",
    "apeSees.beamIntegration.Legendre",
    "apeSees.beamIntegration.Lobatto",
    "apeSees.beamIntegration.NewtonCotes",
    "apeSees.beamIntegration.Radau",
    "apeSees.beamIntegration.Trapezoidal",
    "apeSees.build",
    "apeSees.element.ASDShellQ4",
    "apeSees.element.ASDShellT3",
    "apeSees.element.CorotTruss",
    "apeSees.element.ElasticTimoshenkoBeam",
    "apeSees.element.FourNodeQuad",
    "apeSees.element.FourNodeTetrahedron",
    "apeSees.element.InertiaTruss",
    "apeSees.element.ShellDKGQ",
    "apeSees.element.ShellMITC3",
    "apeSees.element.ShellMITC4",
    "apeSees.element.TenNodeTetrahedron",
    "apeSees.element.Tri31",
    "apeSees.element.Truss",
    "apeSees.element.ZeroLength",
    "apeSees.element.ZeroLengthSection",
    "apeSees.element.dispBeamColumn",
    "apeSees.element.elasticBeamColumn",
    "apeSees.element.forceBeamColumn",
    "apeSees.element.stdBrick",
    "apeSees.geomTransf.Corotational",
    "apeSees.geomTransf.Linear",
    "apeSees.geomTransf.PDelta",
    "apeSees.nDMaterial.DruckerPrager",
    "apeSees.nDMaterial.ElasticIsotropic",
    "apeSees.nDMaterial.J2Plasticity",
    "apeSees.register",
    "apeSees.section.ElasticMembranePlateSection",
    "apeSees.section.ElasticSection",
    "apeSees.section.Fiber",
    "apeSees.section.LayeredShell",
    "apeSees.section.LayeredShellFiberSection",
    "apeSees.uniaxialMaterial.ASDSteel1D",
    "apeSees.uniaxialMaterial.Concrete01",
    "apeSees.uniaxialMaterial.Concrete02",
    "apeSees.uniaxialMaterial.ENT",
    "apeSees.uniaxialMaterial.ElasticMaterial",
    "apeSees.uniaxialMaterial.Hysteretic",
    "apeSees.uniaxialMaterial.Steel01",
    "apeSees.uniaxialMaterial.Steel02"
   ]
  },
  {
   "from": "os.time_series",
   "to": "os.emit",
   "methods": [
    "apeSees.time_series.Constant",
    "apeSees.time_series.Linear",
    "apeSees.time_series.Path",
    "apeSees.time_series.Pulse",
    "apeSees.time_series.Trig"
   ]
  },
  {
   "from": "os.transform",
   "to": "os.emit",
   "methods": [
    "Cartesian"
   ]
  },
  {
   "from": "part",
   "to": "external",
   "methods": [
    "Part.plot"
   ]
  },
  {
   "from": "part",
   "to": "gmsh",
   "methods": [
    "part.edit.affine",
    "part.edit.delete",
    "part.edit.dilate",
    "part.edit.mirror",
    "part.edit.rotate",
    "part.edit.translate"
   ]
  },
  {
   "from": "part",
   "to": "labels",
   "methods": [
    "Part.end",
    "Part.labels",
    "Part.save",
    "part.edit.align_to",
    "part.edit.align_to_point"
   ]
  },
  {
   "from": "part",
   "to": "model.geometry",
   "methods": [
    "Part.model"
   ]
  },
  {
   "from": "part",
   "to": "model.io",
   "methods": [
    "Part.__enter__",
    "Part.end",
    "Part.save",
    "part.edit.copy"
   ]
  },
  {
   "from": "part",
   "to": "model.queries",
   "methods": [
    "Part.inspect"
   ]
  },
  {
   "from": "part",
   "to": "parts",
   "methods": [
    "Part.properties"
   ]
  },
  {
   "from": "part",
   "to": "physical",
   "methods": [
    "Part.physical"
   ]
  },
  {
   "from": "part",
   "to": "session",
   "methods": [
    "Part.__enter__",
    "Part.__init__",
    "Part.begin",
    "Part.end"
   ]
  },
  {
   "from": "parts",
   "to": "gmsh",
   "methods": [
    "g.parts.add",
    "g.parts.build_face_map",
    "g.parts.from_model",
    "g.parts.import_step",
    "g.parts.part",
    "inst.edit.affine",
    "inst.edit.copy",
    "inst.edit.delete",
    "inst.edit.dilate",
    "inst.edit.mirror",
    "inst.edit.rotate",
    "inst.edit.translate"
   ]
  },
  {
   "from": "parts",
   "to": "labels",
   "methods": [
    "g.parts.add",
    "g.parts.register",
    "inst.edit.align_to",
    "inst.edit.align_to_point",
    "inst.edit.copy"
   ]
  },
  {
   "from": "parts",
   "to": "model.boolean",
   "methods": [
    "g.model.boolean.cut",
    "g.model.boolean.fuse",
    "g.model.boolean.intersect"
   ]
  },
  {
   "from": "parts",
   "to": "model.geometry",
   "methods": [
    "g.parts.part"
   ]
  },
  {
   "from": "parts",
   "to": "part",
   "methods": [
    "g.parts.add"
   ]
  },
  {
   "from": "parts",
   "to": "physical",
   "methods": [
    "g.parts.register"
   ]
  },
  {
   "from": "parts.fragmentation",
   "to": "gmsh",
   "methods": [
    "g.parts.fragment_all"
   ]
  },
  {
   "from": "parts.fragmentation",
   "to": "labels",
   "methods": [
    "g.parts.fragment_all",
    "g.parts.fragment_pair",
    "g.parts.fuse_group"
   ]
  },
  {
   "from": "parts.fragmentation",
   "to": "model.boolean",
   "methods": [
    "g.parts.fragment_all",
    "g.parts.fragment_pair",
    "g.parts.fuse_group"
   ]
  },
  {
   "from": "parts.fragmentation",
   "to": "parts",
   "methods": [
    "g.parts.fragment_all",
    "g.parts.fragment_pair",
    "g.parts.fuse_group"
   ]
  },
  {
   "from": "physical",
   "to": "fem.elements",
   "methods": [
    "fem.elements.get"
   ]
  },
  {
   "from": "physical",
   "to": "fem.nodes",
   "methods": [
    "fem.nodes.get"
   ]
  },
  {
   "from": "physical",
   "to": "gmsh",
   "methods": [
    "g.model.geometry.cut_by_surface",
    "g.physical.add",
    "g.physical.entities",
    "g.physical.get_all",
    "g.physical.get_entities",
    "g.physical.get_groups_for_entity",
    "g.physical.get_name",
    "g.physical.get_nodes",
    "g.physical.get_tag",
    "g.physical.remove",
    "g.physical.remove_all",
    "g.physical.remove_name",
    "g.physical.set_name",
    "g.physical.summary"
   ]
  },
  {
   "from": "physical",
   "to": "labels",
   "methods": [
    "g.physical.add",
    "g.physical.from_label",
    "g.physical.from_labels"
   ]
  },
  {
   "from": "physical",
   "to": "model.io",
   "methods": [
    "g.model.io.load_dxf"
   ]
  },
  {
   "from": "physical",
   "to": "parts",
   "methods": [
    "g.model.queries.fragment_all",
    "g.model.queries.make_conformal"
   ]
  },
  {
   "from": "physical",
   "to": "session",
   "methods": [
    "viz.selection.Selection.to_physical"
   ]
  },
  {
   "from": "records",
   "to": "constraints",
   "methods": [
    "g.constraints.list_records"
   ]
  },
  {
   "from": "records",
   "to": "external",
   "methods": [
    "fem.elements.constraints.by_kind",
    "fem.elements.constraints.couplings",
    "fem.elements.constraints.interpolations",
    "fem.elements.constraints.iter_index",
    "fem.elements.constraints.summary",
    "fem.elements.loads.by_kind",
    "fem.elements.loads.by_pattern",
    "fem.elements.loads.iter",
    "fem.elements.loads.patterns",
    "fem.elements.loads.summary",
    "fem.nodes.constraints.Kind",
    "fem.nodes.constraints.by_kind",
    "fem.nodes.constraints.equal_dofs",
    "fem.nodes.constraints.iter_index",
    "fem.nodes.constraints.node_to_surfaces",
    "fem.nodes.constraints.pairs",
    "fem.nodes.constraints.phantom_nodes",
    "fem.nodes.constraints.rigid_diaphragms",
    "fem.nodes.constraints.rigid_link_groups",
    "fem.nodes.constraints.stiff_beam_groups",
    "fem.nodes.constraints.summary",
    "fem.nodes.loads.by_kind",
    "fem.nodes.loads.by_pattern",
    "fem.nodes.loads.iter",
    "fem.nodes.loads.patterns",
    "fem.nodes.loads.summary",
    "fem.nodes.masses.by_kind",
    "fem.nodes.masses.by_node",
    "fem.nodes.masses.iter",
    "fem.nodes.masses.summary",
    "fem.nodes.masses.total_mass",
    "fem.nodes.sp.by_kind",
    "fem.nodes.sp.by_node",
    "fem.nodes.sp.homogeneous",
    "fem.nodes.sp.iter",
    "fem.nodes.sp.prescribed"
   ]
  },
  {
   "from": "records",
   "to": "fem.elements",
   "methods": [
    "g.constraints.embedded",
    "g.constraints.tie",
    "g.constraints.tied_contact"
   ]
  },
  {
   "from": "records",
   "to": "fem.nodes",
   "methods": [
    "g.constraints.bc",
    "g.constraints.equal_dof",
    "g.constraints.kinematic_coupling",
    "g.constraints.node_to_surface",
    "g.constraints.node_to_surface_spring",
    "g.constraints.penalty",
    "g.constraints.resolve",
    "g.constraints.resolve_bcs",
    "g.constraints.rigid_body",
    "g.constraints.rigid_diaphragm",
    "g.constraints.rigid_link",
    "g.loads.body",
    "g.loads.face_load",
    "g.loads.face_sp",
    "g.loads.gravity",
    "g.loads.line",
    "g.loads.pattern",
    "g.loads.point",
    "g.loads.point_closest",
    "g.loads.resolve",
    "g.loads.surface",
    "g.masses.line",
    "g.masses.point",
    "g.masses.resolve",
    "g.masses.surface",
    "g.masses.volume",
    "records.NodeConstraintSet.phantom_nodes"
   ]
  },
  {
   "from": "results",
   "to": "external",
   "methods": [
    "Results.eigenvalue",
    "Results.fem",
    "Results.frequency_hz",
    "Results.kind",
    "Results.mode_index",
    "Results.modes",
    "Results.n_steps",
    "Results.name",
    "Results.period_s",
    "Results.viewer"
   ]
  },
  {
   "from": "results",
   "to": "results.elements",
   "methods": [
    "Results.elements"
   ]
  },
  {
   "from": "results",
   "to": "results.inspect",
   "methods": [
    "Results.__repr__",
    "Results.inspect",
    "results.inspect.summary"
   ]
  },
  {
   "from": "results",
   "to": "results.nodes",
   "methods": [
    "Results.nodes",
    "results.readers.MPCOReader",
    "results.readers.NativeReader"
   ]
  },
  {
   "from": "results",
   "to": "results.plot",
   "methods": [
    "Results.plot",
    "results.plot"
   ]
  },
  {
   "from": "results",
   "to": "results.readers",
   "methods": [
    "Results.__enter__",
    "Results.close",
    "Results.from_mpco",
    "Results.from_native",
    "Results.from_recorders",
    "Results.stages",
    "Results.time",
    "results.from_mpco",
    "results.from_native",
    "results.from_recorders"
   ]
  },
  {
   "from": "results",
   "to": "results.transcode",
   "methods": [
    "Results.from_recorders"
   ]
  },
  {
   "from": "results",
   "to": "results.writers",
   "methods": [
    "results.from_recorders"
   ]
  },
  {
   "from": "results",
   "to": "viewer.results",
   "methods": [
    "Results.viewer",
    "results.viewer"
   ]
  },
  {
   "from": "results.bind",
   "to": "fem.elements",
   "methods": [
    "results.elements.get"
   ]
  },
  {
   "from": "results.bind",
   "to": "fem.nodes",
   "methods": [
    "results.nodes.get"
   ]
  },
  {
   "from": "results.bind",
   "to": "results",
   "methods": [
    "Results.bind",
    "Results.from_mpco",
    "Results.from_native"
   ]
  },
  {
   "from": "results.bind",
   "to": "results.chain",
   "methods": [
    "ResultChain.in_box",
    "ResultChain.in_sphere",
    "ResultChain.nearest_to",
    "ResultChain.on_plane",
    "ResultChain.where",
    "results.elements.gauss.select",
    "results.elements.select",
    "results.nodes.select"
   ]
  },
  {
   "from": "results.bind",
   "to": "results.elements",
   "methods": [
    "results.elements.in_box",
    "results.elements.in_sphere",
    "results.elements.nearest_to",
    "results.elements.on_plane"
   ]
  },
  {
   "from": "results.bind",
   "to": "results.nodes",
   "methods": [
    "results.nodes.in_box",
    "results.nodes.in_sphere",
    "results.nodes.nearest_to",
    "results.nodes.on_plane"
   ]
  },
  {
   "from": "results.bind",
   "to": "results.readers",
   "methods": [
    "results.elements.fibers.get",
    "results.elements.gauss.get",
    "results.elements.layers.get",
    "results.elements.line_stations.get",
    "results.elements.springs.get"
   ]
  },
  {
   "from": "results.capture",
   "to": "external",
   "methods": [
    "results.capture.DomainCapture.capture_modes",
    "results.capture.DomainCapture.step"
   ]
  },
  {
   "from": "results.capture",
   "to": "os.emit",
   "methods": [
    "results.capture.DomainCapture.from_h5"
   ]
  },
  {
   "from": "results.capture",
   "to": "results.spec",
   "methods": [
    "results.capture.DomainCapture.from_h5",
    "results.capture.DomainCaptureSpec"
   ]
  },
  {
   "from": "results.capture",
   "to": "results.writers",
   "methods": [
    "results.capture.DomainCapture.capture_modes",
    "results.capture.DomainCapture.close",
    "results.capture.DomainCapture.end_stage"
   ]
  },
  {
   "from": "results.chain",
   "to": "external",
   "methods": [
    "ResultChain.iter",
    "ResultChain.result",
    "results.elements.gauss.select",
    "results.elements.select",
    "results.nodes.select"
   ]
  },
  {
   "from": "results.chain",
   "to": "results.bind",
   "methods": [
    "ResultChain.in_box",
    "ResultChain.in_sphere",
    "ResultChain.nearest_to",
    "ResultChain.on_plane",
    "ResultChain.where"
   ]
  },
  {
   "from": "results.chain",
   "to": "results.nodes",
   "methods": [
    "ResultChain.get"
   ]
  },
  {
   "from": "results.elements",
   "to": "results",
   "methods": [
    "results.elements.available_components",
    "results.elements.fibers.available_components",
    "results.elements.fibers.get",
    "results.elements.gauss.available_components",
    "results.elements.gauss.get",
    "results.elements.get",
    "results.elements.layers.available_components",
    "results.elements.layers.get",
    "results.elements.line_stations.available_components",
    "results.elements.line_stations.get",
    "results.elements.springs.available_components",
    "results.elements.springs.get"
   ]
  },
  {
   "from": "results.elements",
   "to": "results.bind",
   "methods": [
    "results.elements.fibers.get",
    "results.elements.gauss.get",
    "results.elements.gauss.select",
    "results.elements.get",
    "results.elements.in_box",
    "results.elements.in_sphere",
    "results.elements.layers.get",
    "results.elements.line_stations.get",
    "results.elements.nearest_to",
    "results.elements.on_plane",
    "results.elements.select",
    "results.elements.springs.get"
   ]
  },
  {
   "from": "results.elements",
   "to": "results.plot",
   "methods": [
    "results.plot.line_force"
   ]
  },
  {
   "from": "results.elements",
   "to": "results.slabs",
   "methods": [
    "results.elements.in_box",
    "results.elements.in_sphere",
    "results.elements.nearest_to",
    "results.elements.on_plane"
   ]
  },
  {
   "from": "results.inspect",
   "to": "external",
   "methods": [
    "Results.__repr__",
    "results.inspect.diagnose",
    "results.inspect.summary"
   ]
  },
  {
   "from": "results.inspect",
   "to": "results",
   "methods": [
    "results.inspect.components",
    "results.inspect.diagnose",
    "results.inspect.summary"
   ]
  },
  {
   "from": "results.live",
   "to": "external",
   "methods": [
    "results.live.LiveMPCO",
    "results.live.LiveRecorders",
    "results.live.LiveRecorders.begin_stage",
    "results.live.LiveRecorders.end_stage"
   ]
  },
  {
   "from": "results.live",
   "to": "os.emit",
   "methods": [
    "results.live.LiveMPCO",
    "results.live.LiveRecorders.begin_stage"
   ]
  },
  {
   "from": "results.nodes",
   "to": "results",
   "methods": [
    "results.nodes.available_components",
    "results.nodes.get"
   ]
  },
  {
   "from": "results.nodes",
   "to": "results.bind",
   "methods": [
    "results.nodes.get",
    "results.nodes.in_box",
    "results.nodes.in_sphere",
    "results.nodes.nearest_to",
    "results.nodes.on_plane",
    "results.nodes.select"
   ]
  },
  {
   "from": "results.nodes",
   "to": "results.plot",
   "methods": [
    "results.plot.contour",
    "results.plot.deformed",
    "results.plot.history",
    "results.plot.vector_glyph"
   ]
  },
  {
   "from": "results.nodes",
   "to": "results.slabs",
   "methods": [
    "ResultChain.get",
    "results.nodes.in_box",
    "results.nodes.in_sphere",
    "results.nodes.nearest_to",
    "results.nodes.on_plane"
   ]
  },
  {
   "from": "results.plot",
   "to": "external",
   "methods": [
    "Results.plot"
   ]
  },
  {
   "from": "results.plot",
   "to": "fem.nodes",
   "methods": [
    "results.plot.loads"
   ]
  },
  {
   "from": "results.plot",
   "to": "femdata",
   "methods": [
    "results.plot.line_force",
    "results.plot.loads",
    "results.plot.mesh"
   ]
  },
  {
   "from": "results.plot",
   "to": "results.elements",
   "methods": [
    "results.plot.line_force"
   ]
  },
  {
   "from": "results.plot",
   "to": "results.nodes",
   "methods": [
    "results.plot.contour",
    "results.plot.deformed",
    "results.plot.history",
    "results.plot.reactions",
    "results.plot.vector_glyph"
   ]
  },
  {
   "from": "results.plot",
   "to": "viz.plot",
   "methods": [
    "results.plot.contour",
    "results.plot.deformed",
    "results.plot.history",
    "results.plot.line_force",
    "results.plot.loads",
    "results.plot.mesh",
    "results.plot.vector_glyph"
   ]
  },
  {
   "from": "results.readers",
   "to": "external",
   "methods": [
    "Results.stages",
    "Results.time",
    "results.elements.available_components",
    "results.elements.fibers.available_components",
    "results.elements.gauss.available_components",
    "results.elements.layers.available_components",
    "results.elements.line_stations.available_components",
    "results.elements.springs.available_components",
    "results.inspect.components",
    "results.nodes.available_components"
   ]
  },
  {
   "from": "results.readers",
   "to": "results",
   "methods": [
    "results.from_mpco",
    "results.from_native",
    "results.readers.MPCOReader",
    "results.readers.NativeReader"
   ]
  },
  {
   "from": "results.readers",
   "to": "results.bind",
   "methods": [
    "Results.from_mpco",
    "Results.from_native"
   ]
  },
  {
   "from": "results.readers",
   "to": "results.inspect",
   "methods": [
    "results.inspect.diagnose"
   ]
  },
  {
   "from": "results.readers",
   "to": "results.slabs",
   "methods": [
    "results.elements.fibers.get",
    "results.elements.gauss.get",
    "results.elements.get",
    "results.elements.layers.get",
    "results.elements.line_stations.get",
    "results.elements.springs.get",
    "results.nodes.get"
   ]
  },
  {
   "from": "results.schema",
   "to": "results.writers",
   "methods": [
    "results.schema.PARSER_VERSION",
    "results.schema.SCHEMA_VERSION"
   ]
  },
  {
   "from": "results.slabs",
   "to": "external",
   "methods": [
    "ElementSlab",
    "FiberSlab",
    "GaussSlab",
    "LayerSlab",
    "LineStationSlab",
    "NodeSlab",
    "ResultChain.get",
    "SpringSlab",
    "results.nodes.get"
   ]
  },
  {
   "from": "results.spec",
   "to": "apesees",
   "methods": [
    "results.spec.DomainCaptureSpec.resolve"
   ]
  },
  {
   "from": "results.spec",
   "to": "chain",
   "methods": [
    "results.spec.DomainCaptureSpec.categories",
    "results.spec.DomainCaptureSpec.components_for"
   ]
  },
  {
   "from": "results.spec",
   "to": "femdata",
   "methods": [
    "results.spec.DomainCaptureSpec.resolve"
   ]
  },
  {
   "from": "results.spec",
   "to": "femdata.h5",
   "methods": [
    "results.spec.ResolvedRecorderSpec.from_manifest_h5",
    "results.spec.ResolvedRecorderSpec.to_manifest_h5"
   ]
  },
  {
   "from": "results.spec",
   "to": "os.emit",
   "methods": [
    "results.spec.ResolvedRecorderSpec.to_mpco_python_command",
    "results.spec.ResolvedRecorderSpec.to_mpco_tcl_command",
    "results.spec.ResolvedRecorderSpec.to_python_commands",
    "results.spec.ResolvedRecorderSpec.to_tcl_commands"
   ]
  },
  {
   "from": "results.spec",
   "to": "results.capture",
   "methods": [
    "results.capture.DomainCapture.from_h5",
    "results.spec.DomainCaptureSpec.resolve"
   ]
  },
  {
   "from": "results.spec",
   "to": "results.live",
   "methods": [
    "results.spec.ResolvedRecorderSpec.emit_mpco",
    "results.spec.ResolvedRecorderSpec.emit_recorders"
   ]
  },
  {
   "from": "results.spec",
   "to": "vocabulary",
   "methods": [
    "results.spec.DomainCaptureSpec.shorthands_for"
   ]
  },
  {
   "from": "results.transcode",
   "to": "os.emit",
   "methods": [
    "results.transcode.RecorderTranscoder.run"
   ]
  },
  {
   "from": "results.transcode",
   "to": "results.writers",
   "methods": [
    "Results.from_recorders",
    "results.from_recorders",
    "results.transcode.RecorderTranscoder.run"
   ]
  },
  {
   "from": "results.writers",
   "to": "femdata.h5",
   "methods": [
    "results.capture.DomainCapture.capture_modes",
    "results.capture.DomainCapture.close",
    "results.capture.DomainCapture.end_stage",
    "results.transcode.RecorderTranscoder.run",
    "results.writers.NativeWriter"
   ]
  },
  {
   "from": "results.writers",
   "to": "results",
   "methods": [
    "Results.from_recorders"
   ]
  },
  {
   "from": "results.writers",
   "to": "results.schema",
   "methods": [
    "results.writers.NativeWriter"
   ]
  },
  {
   "from": "results.writers",
   "to": "results.transcode",
   "methods": [
    "results.from_recorders"
   ]
  },
  {
   "from": "sections",
   "to": "labels",
   "methods": [
    "apeGmsh.sections.W_profile",
    "apeGmsh.sections.W_solid",
    "g.sections.W_solid",
    "g.sections.angle_solid",
    "g.sections.channel_solid",
    "g.sections.tee_solid"
   ]
  },
  {
   "from": "sections",
   "to": "model.boolean",
   "methods": [
    "apeGmsh.sections.W_profile",
    "apeGmsh.sections.angle_solid",
    "apeGmsh.sections.channel_solid",
    "apeGmsh.sections.pipe_hollow",
    "apeGmsh.sections.rect_hollow",
    "apeGmsh.sections.tee_solid",
    "g.sections.W_solid",
    "g.sections.angle_solid",
    "g.sections.channel_solid",
    "g.sections.pipe_hollow",
    "g.sections.rect_hollow",
    "g.sections.tee_solid"
   ]
  },
  {
   "from": "sections",
   "to": "model.geometry",
   "methods": [
    "apeGmsh.sections.W_shell",
    "apeGmsh.sections.W_solid",
    "apeGmsh.sections.pipe_solid",
    "apeGmsh.sections.rect_solid",
    "g.sections.W_shell",
    "g.sections.W_solid",
    "g.sections.pipe_solid",
    "g.sections.rect_solid"
   ]
  },
  {
   "from": "sections",
   "to": "model.io",
   "methods": [
    "apeGmsh.sections.W_profile",
    "apeGmsh.sections.W_solid"
   ]
  },
  {
   "from": "sections",
   "to": "part",
   "methods": [
    "apeGmsh.sections.W_shell",
    "apeGmsh.sections.W_solid",
    "apeGmsh.sections.angle_solid",
    "apeGmsh.sections.channel_solid",
    "apeGmsh.sections.pipe_hollow",
    "apeGmsh.sections.pipe_solid",
    "apeGmsh.sections.rect_hollow",
    "apeGmsh.sections.rect_solid",
    "apeGmsh.sections.tee_solid"
   ]
  },
  {
   "from": "sections",
   "to": "parts",
   "methods": [
    "g.sections.W_shell",
    "g.sections.W_solid",
    "g.sections.angle_solid",
    "g.sections.channel_solid",
    "g.sections.pipe_hollow",
    "g.sections.pipe_solid",
    "g.sections.rect_hollow",
    "g.sections.rect_solid",
    "g.sections.tee_solid"
   ]
  },
  {
   "from": "selection",
   "to": "fem.elements",
   "methods": [
    "selection.ElementChain.in_box",
    "selection.ElementChain.in_sphere",
    "selection.ElementChain.nearest_to",
    "selection.ElementChain.on_plane",
    "selection.ElementChain.result",
    "selection.ElementChain.where"
   ]
  },
  {
   "from": "selection",
   "to": "fem.nodes",
   "methods": [
    "selection.NodeChain.in_box",
    "selection.NodeChain.in_sphere",
    "selection.NodeChain.nearest_to",
    "selection.NodeChain.on_plane",
    "selection.NodeChain.result",
    "selection.NodeChain.where"
   ]
  },
  {
   "from": "selection",
   "to": "gmsh",
   "methods": [
    "GeometryChain.in_box",
    "GeometryChain.in_sphere",
    "GeometryChain.nearest_to",
    "GeometryChain.on_plane",
    "GeometryChain.where",
    "Selection.normal_along",
    "Selection.parallel_to",
    "Selection.partition_by",
    "Selection.select",
    "g.model.queries.select"
   ]
  },
  {
   "from": "selection",
   "to": "labels",
   "methods": [
    "Selection.to_label"
   ]
  },
  {
   "from": "selection",
   "to": "mesh_selection",
   "methods": [
    "g.mesh_selection.from_geometric"
   ]
  },
  {
   "from": "selection",
   "to": "model.queries",
   "methods": [
    "GeometryChain.result",
    "g.model.queries.line",
    "g.model.queries.plane",
    "g.model.queries.select_all",
    "g.model.queries.select_all_curves",
    "g.model.queries.select_all_points",
    "g.model.queries.select_all_surfaces",
    "g.model.queries.select_all_volumes"
   ]
  },
  {
   "from": "selection",
   "to": "physical",
   "methods": [
    "Selection.to_physical"
   ]
  },
  {
   "from": "selection",
   "to": "session",
   "methods": [
    "g.model.select",
    "g.model.viewer"
   ]
  },
  {
   "from": "session",
   "to": "model.queries",
   "methods": [
    "g.model.preview"
   ]
  },
  {
   "from": "types",
   "to": "os.elements",
   "methods": [
    "apeSees.element.ZeroLengthMatDir"
   ]
  },
  {
   "from": "types",
   "to": "os.recorder",
   "methods": [
    "apeSees.recorder.RecorderRecord"
   ]
  },
  {
   "from": "types",
   "to": "os.sections",
   "methods": [
    "apeSees.section.FiberPoint",
    "apeSees.section.RectPatch",
    "apeSees.section.ShellLayer",
    "apeSees.section.StraightLayer"
   ]
  },
  {
   "from": "types",
   "to": "os.transform",
   "methods": [
    "AlongBeam",
    "Cartesian",
    "Cylindrical",
    "Spherical"
   ]
  },
  {
   "from": "viewer.animation",
   "to": "external",
   "methods": [
    "apeGmsh.viewers.export_animation"
   ]
  },
  {
   "from": "viewer.animation",
   "to": "viewer",
   "methods": [
    "apeGmsh.viewers.export_animation"
   ]
  },
  {
   "from": "viewer.geomtransf",
   "to": "external",
   "methods": [
    "apeGmsh.viewers.GeomTransfViewer"
   ]
  },
  {
   "from": "viewer.geomtransf",
   "to": "viewer",
   "methods": [
    "apeGmsh.viewers.GeomTransfViewer"
   ]
  },
  {
   "from": "viewer.mesh",
   "to": "viz.preview",
   "methods": [
    "viz.preview.preview_mesh"
   ]
  },
  {
   "from": "viewer.model",
   "to": "physical",
   "methods": [
    "viz.selection.SelectionComposite.picker"
   ]
  },
  {
   "from": "viewer.model",
   "to": "viz.preview",
   "methods": [
    "viz.preview.preview_model"
   ]
  },
  {
   "from": "viewer.results",
   "to": "external",
   "methods": [
    "Results.viewer"
   ]
  },
  {
   "from": "viewer.results",
   "to": "results.readers",
   "methods": [
    "python -m apeGmsh.viewers"
   ]
  },
  {
   "from": "viewer.results",
   "to": "viewer",
   "methods": [
    "python -m apeGmsh.viewers",
    "viewer.results.show"
   ]
  },
  {
   "from": "viewer.results",
   "to": "viewer.animation",
   "methods": [
    "viewer.results.export_animation"
   ]
  },
  {
   "from": "viz.inspect",
   "to": "gmsh",
   "methods": [
    "viz.inspect.get_geometry_info",
    "viz.inspect.get_mesh_info",
    "viz.inspect.print_summary"
   ]
  },
  {
   "from": "viz.inspect",
   "to": "session",
   "methods": [
    "viz.inspect.get_geometry_info",
    "viz.inspect.get_mesh_info",
    "viz.inspect.print_summary"
   ]
  },
  {
   "from": "viz.plot",
   "to": "external",
   "methods": [
    "viz.plot.geometry",
    "viz.plot.label_elements",
    "viz.plot.label_entities",
    "viz.plot.label_nodes",
    "viz.plot.mesh",
    "viz.plot.physical_groups",
    "viz.plot.physical_groups_mesh",
    "viz.plot.quality",
    "viz.plot.savefig",
    "viz.plot.show"
   ]
  },
  {
   "from": "viz.plot",
   "to": "gmsh",
   "methods": [
    "viz.plot.geometry",
    "viz.plot.label_elements",
    "viz.plot.label_entities",
    "viz.plot.label_nodes",
    "viz.plot.mesh",
    "viz.plot.physical_groups",
    "viz.plot.physical_groups_mesh",
    "viz.plot.quality"
   ]
  },
  {
   "from": "viz.preview",
   "to": "session",
   "methods": [
    "viz.preview.preview",
    "viz.preview.preview_mesh",
    "viz.preview.preview_model"
   ]
  },
  {
   "from": "viz.preview",
   "to": "viewer.mesh",
   "methods": [
    "viz.preview.preview_mesh"
   ]
  },
  {
   "from": "viz.preview",
   "to": "viewer.model",
   "methods": [
    "viz.preview.preview_model"
   ]
  },
  {
   "from": "viz.selection",
   "to": "gmsh",
   "methods": [
    "viz.selection.Selection.bbox",
    "viz.selection.Selection.centers",
    "viz.selection.Selection.masses",
    "viz.selection.Selection.sorted_by",
    "viz.selection.Selection.to_dataframe",
    "viz.selection.Selection.to_mesh_elements",
    "viz.selection.Selection.to_mesh_nodes",
    "viz.selection.SelectionComposite.adjacent_to",
    "viz.selection.SelectionComposite.boundary_of",
    "viz.selection.SelectionComposite.closest_to",
    "viz.selection.SelectionComposite.select_all",
    "viz.selection.SelectionComposite.select_curves",
    "viz.selection.SelectionComposite.select_points",
    "viz.selection.SelectionComposite.select_surfaces",
    "viz.selection.SelectionComposite.select_volumes"
   ]
  },
  {
   "from": "viz.selection",
   "to": "physical",
   "methods": [
    "viz.selection.Selection.to_physical"
   ]
  },
  {
   "from": "viz.selection",
   "to": "session",
   "methods": [
    "viz.selection.Selection.bbox",
    "viz.selection.Selection.filter",
    "viz.selection.Selection.limit",
    "viz.selection.Selection.set_algebra",
    "viz.selection.Selection.sorted_by",
    "viz.selection.Selection.to_dataframe",
    "viz.selection.Selection.to_list",
    "viz.selection.Selection.to_tags",
    "viz.selection.SelectionComposite.adjacent_to",
    "viz.selection.SelectionComposite.boundary_of",
    "viz.selection.SelectionComposite.closest_to",
    "viz.selection.SelectionComposite.picker",
    "viz.selection.SelectionComposite.select_all",
    "viz.selection.SelectionComposite.select_curves",
    "viz.selection.SelectionComposite.select_points",
    "viz.selection.SelectionComposite.select_surfaces",
    "viz.selection.SelectionComposite.select_volumes"
   ]
  },
  {
   "from": "viz.selection",
   "to": "viewer.model",
   "methods": [
    "viz.selection.SelectionComposite.picker"
   ]
  },
  {
   "from": "viz.vtkexport",
   "to": "external",
   "methods": [
    "viz.vtkexport.VTKExport.write",
    "viz.vtkexport.VTKExport.write_mode_series",
    "viz.vtkexport.write_vtu",
    "viz.vtkexport.write_vtu_series"
   ]
  },
  {
   "from": "viz.vtkexport",
   "to": "fem.extract",
   "methods": [
    "viz.vtkexport.VTKExport"
   ]
  }
 ],
 "methods": [
  {
   "id": "AlongBeam",
   "label": "AlongBeam",
   "composite": "os.transform",
   "signature": "AlongBeam(reference_pg: str)",
   "summary": "Tangent-derived orientation whose e3 follows the nearest reference-PG line tangent; passed as orientation= to a geomTransf.",
   "file": "src/apeGmsh/opensees/_orientation.py:289",
   "flow": [
    {
     "node": "types",
     "action": "hold reference_pg name; bridge calls bind_fem(fem) before fan-out to cache reference segments",
     "passes": "AlongBeam orientation field + reference PG name",
     "to": "os.transform"
    },
    {
     "node": "os.emit",
     "action": "bind_fem reads fem.elements.get(pg)+fem.nodes.coords; triad_at projects element point onto nearest segment for per-element vecxz",
     "passes": "reference segment tangents → (e1,e2,e3)",
     "to": "fem.elements"
    }
   ],
   "inputs": "reference_pg:str (must contain 2-node line elements on the FEM snapshot)",
   "outputs": "AlongBeam orientation (bind_fem(fem) + triad_at(p))",
   "reads": [
    "fem.elements",
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "BuiltModel",
   "label": "BuiltModel",
   "composite": "os.build",
   "signature": "BuiltModel(primitives, tag_for, ndm, ndf, fem, fix_records, mass_records)",
   "summary": "Immutable frozen snapshot of declared primitives + tag map + FEMData that emitters consume.",
   "file": "src/apeGmsh/opensees/apesees.py:160",
   "flow": [
    {
     "node": "os.build",
     "action": "hold frozen primitive tuple + id→tag map + FEMData ref",
     "passes": "BuiltModel fields",
     "to": "os.emit"
    }
   ],
   "inputs": "primitives:tuple[Primitive,...], tag_for:dict[int,int], ndm:int, ndf:int, fem:FEMData, fix_records, mass_records",
   "outputs": "frozen dataclass",
   "reads": [
    "femdata"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "BuiltModel.emit",
   "label": "emit",
   "composite": "os.emit",
   "signature": "emit(emitter: Emitter) -> int",
   "summary": "Drive an emitter over the whole model in topological order (materials/sections→transforms→elements→patterns); returns 0.",
   "file": "src/apeGmsh/opensees/apesees.py:194",
   "flow": [
    {
     "node": "os.emit",
     "action": "seed TagAllocator + install base tag resolver on emitter",
     "passes": "_base_resolver closure",
     "to": "os.registry"
    },
    {
     "node": "os.emit",
     "action": "emit model() then every fem.nodes id/coords as emitter.node()",
     "passes": "node tag + xyz floats",
     "to": "fem.nodes"
    },
    {
     "node": "os.emit",
     "action": "topological_order primitives, _emit each material/section/integration/transform, fan elements over PG, fix/mass/broker-loads, patterns/recorders",
     "passes": "per-primitive tag ints + per-element node tuples",
     "to": "external"
    }
   ],
   "inputs": "emitter:Emitter (Tcl/Py/Live/H5)",
   "outputs": "int (analyze exit value, 0 if none)",
   "reads": [
    "femdata",
    "fem.nodes",
    "fem.elements",
    "os.registry"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "Cartesian",
   "label": "Cartesian",
   "composite": "os.transform",
   "signature": "Cartesian(reference_axis=(0.0,0.0,1.0))",
   "summary": "Constant Cartesian orientation triad (default Z-up) passed as orientation= to a geomTransf.",
   "file": "src/apeGmsh/opensees/_orientation.py:82",
   "flow": [
    {
     "node": "types",
     "action": "compute orthonormal triad from reference_axis at construction",
     "passes": "Cartesian orientation field",
     "to": "os.transform"
    },
    {
     "node": "os.transform",
     "action": "transform.orientation triad_at() consumed by emit_transform_specs to derive vecxz",
     "passes": "(e1,e2,e3) triad",
     "to": "os.emit"
    }
   ],
   "inputs": "reference_axis:3-vector",
   "outputs": "Cartesian orientation (triad_at(p) -> (e1,e2,e3)); also the bridge default_orientation",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "Cylindrical",
   "label": "Cylindrical",
   "composite": "os.transform",
   "signature": "Cylindrical(origin=(0,0,0), axis=(0,0,1))",
   "summary": "Cylindrical orientation triad about an axis of revolution passed as orientation= to a geomTransf.",
   "file": "src/apeGmsh/opensees/_orientation.py:140",
   "flow": [
    {
     "node": "types",
     "action": "store origin+unit axis; triad_at computes radial/circumferential/axial per point",
     "passes": "Cylindrical orientation field",
     "to": "os.transform"
    },
    {
     "node": "os.emit",
     "action": "emit_transform_specs calls triad_at(element-point) to derive per-element vecxz",
     "passes": "(e_r,e_theta,axis) triad",
     "to": "os.emit"
    }
   ],
   "inputs": "origin:3-vector, axis:3-vector",
   "outputs": "Cylindrical orientation (triad_at(p))",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "ElementGroup.__iter__",
   "label": "iter(ElementGroup)",
   "composite": "fem.elements",
   "signature": "__iter__() -> Iterator[tuple[int, tuple[int, ...]]]",
   "summary": "Yields (eid, conn_row) pairs with plain Python ints — the solver-emission loop (ops.element(type, eid, *conn, mat)).",
   "file": "src/apeGmsh/mesh/_element_types.py:211",
   "flow": [
    {
     "node": "fem.elements",
     "action": "yield (int(ids[i]), tuple(int(n) for n in connectivity[i]))",
     "passes": "(eid:int, conn:tuple[int])",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of (eid, conn_row) pairs",
   "reads": [
    "ElementGroup.ids",
    "ElementGroup.connectivity"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.__len__",
   "label": "len(ElementGroup)",
   "composite": "fem.elements",
   "signature": "__len__() -> int",
   "summary": "Number of elements in the block.",
   "file": "src/apeGmsh/mesh/_element_types.py:208",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return len(self.ids)",
     "passes": "int",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "element count int",
   "reads": [
    "ElementGroup.ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.connectivity",
   "label": "connectivity",
   "composite": "fem.elements",
   "signature": "attribute connectivity -> ndarray",
   "summary": "Node connectivity for this block, ndarray(N, npe) int64.",
   "file": "src/apeGmsh/mesh/_element_types.py:186",
   "flow": [
    {
     "node": "fem.elements",
     "action": "expose stored connectivity (int64 at __init__)",
     "passes": "ndarray(N,npe) int64",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,npe) int64 connectivity",
   "reads": [
    "ElementGroup.connectivity"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.dim",
   "label": "dim",
   "composite": "fem.elements",
   "signature": "property dim -> int",
   "summary": "Topological dimension (0–3) of the element type.",
   "file": "src/apeGmsh/mesh/_element_types.py:198",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return self.element_type.dim",
     "passes": "int",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "int dimension",
   "reads": [
    "ElementGroup.element_type.dim"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.element_type",
   "label": "element_type",
   "composite": "fem.elements",
   "signature": "attribute element_type -> ElementTypeInfo",
   "summary": "Type metadata for this homogeneous element block.",
   "file": "src/apeGmsh/mesh/_element_types.py:184",
   "flow": [
    {
     "node": "fem.elements",
     "action": "expose stored ElementTypeInfo",
     "passes": "ElementTypeInfo",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ElementTypeInfo",
   "reads": [
    "ElementGroup.element_type"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.ids",
   "label": "ids",
   "composite": "fem.elements",
   "signature": "attribute ids -> ndarray",
   "summary": "Element IDs for this block, ndarray(N,) int64.",
   "file": "src/apeGmsh/mesh/_element_types.py:185",
   "flow": [
    {
     "node": "fem.elements",
     "action": "expose stored ids (int64 at __init__)",
     "passes": "ndarray(N,) int64",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,) int64 element IDs",
   "reads": [
    "ElementGroup.ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.npe",
   "label": "npe",
   "composite": "fem.elements",
   "signature": "property npe -> int",
   "summary": "Nodes per element for this type.",
   "file": "src/apeGmsh/mesh/_element_types.py:202",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return self.element_type.npe",
     "passes": "int",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "int nodes-per-element",
   "reads": [
    "ElementGroup.element_type.npe"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.type_code",
   "label": "type_code",
   "composite": "fem.elements",
   "signature": "property type_code -> int",
   "summary": "Gmsh element type code (the primary key).",
   "file": "src/apeGmsh/mesh/_element_types.py:194",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return self.element_type.code",
     "passes": "int",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "int Gmsh type code",
   "reads": [
    "ElementGroup.element_type.code"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementGroup.type_name",
   "label": "type_name",
   "composite": "fem.elements",
   "signature": "property type_name -> str",
   "summary": "Short alias of the element type (e.g. 'tet4', 'hex8').",
   "file": "src/apeGmsh/mesh/_element_types.py:190",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return self.element_type.name",
     "passes": "str",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "str type alias",
   "reads": [
    "ElementGroup.element_type.name"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "ElementSlab",
   "label": "ElementSlab",
   "composite": "results.slabs",
   "signature": "@dataclass(frozen=True) ElementSlab(component: str, values: ndarray (T,E,npe), element_ids: ndarray (E,), time: ndarray (T,))",
   "summary": "Immutable per-element-node slab (globalForce / localForce) returned by results.elements.get and element-level ResultChain.get. values is (T,E,npe). Exported from apeGmsh.results.",
   "file": "src/apeGmsh/results/_slabs.py:44",
   "flow": [
    {
     "node": "results.slabs",
     "action": "constructed by ResultsReader.read_elements",
     "passes": "ElementSlab",
     "to": "external"
    }
   ],
   "inputs": "(constructed by the reader)",
   "outputs": "frozen ElementSlab dataclass (.component/.values/.element_ids/.time)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "FiberSlab",
   "label": "FiberSlab",
   "composite": "results.slabs",
   "signature": "@dataclass(frozen=True) FiberSlab(component, values: ndarray (T,sum_F), element_index, gp_index, y, z, area, material_tag: each ndarray (sum_F,), time: ndarray (T,))",
   "summary": "Immutable fiber-level slab returned by results.elements.fibers.get. One row per fiber within a fiber-section GP; location columns are parent element_index/gp_index plus section-local y/z, area and material_tag. Exported from apeGmsh.results.",
   "file": "src/apeGmsh/results/_slabs.py:91",
   "flow": [
    {
     "node": "results.slabs",
     "action": "constructed by ResultsReader.read_fibers",
     "passes": "FiberSlab",
     "to": "external"
    }
   ],
   "inputs": "(constructed by the reader)",
   "outputs": "frozen FiberSlab dataclass (.component/.values/.element_index/.gp_index/.y/.z/.area/.material_tag/.time)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "GaussSlab",
   "label": "GaussSlab",
   "composite": "results.slabs",
   "signature": "@dataclass(frozen=True) GaussSlab(component, values: ndarray (T,sum_GP), element_index: ndarray (sum_GP,), natural_coords: ndarray (sum_GP,dim), local_axes_quaternion: Optional[ndarray] (E,4), time: ndarray (T,)) ; global_coords(fem) -> ndarray (sum_GP,3)",
   "summary": "Immutable continuum Gauss-point slab returned by results.elements.gauss.get. natural_coords are parent-space [-1,+1]; local_axes_quaternion is (E,4) for shells else None. The public method global_coords(fem) maps per-GP natural coords to (sum_GP,3) world coords via element shape functions (hex8/quad4) with a centroid+bbox fallback. Exported from apeGmsh.results.",
   "file": "src/apeGmsh/results/_slabs.py:63",
   "flow": [
    {
     "node": "results.slabs",
     "action": "constructed by ResultsReader.read_gauss",
     "passes": "GaussSlab",
     "to": "external"
    },
    {
     "node": "results.slabs",
     "action": "global_coords(fem) → compute_global_coords(self, fem) interpolates through the bound FEMData's shape functions",
     "passes": "world_coords:ndarray(sum_GP,3)",
     "to": "external"
    }
   ],
   "inputs": "(constructed by the reader); global_coords takes a bound fem",
   "outputs": "frozen GaussSlab dataclass + global_coords(fem) world-coord ndarray",
   "reads": [
    "src/apeGmsh/results/_gauss_world_coords.py:compute_global_coords (uses FEMData element shape functions)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "GeometryChain.difference",
   "label": "difference",
   "composite": "model.queries",
   "signature": "GeometryChain.difference(other) -> GeometryChain  # also a - b",
   "summary": "Set difference of two compatible GeometryChains (self minus other).",
   "file": "src/apeGmsh/_chain.py:192",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); drop=set(other._items); filter self._items not in drop",
     "passes": "other chain",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap(filtered)",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "other: another GeometryChain bound to the same engine",
   "outputs": "a new GeometryChain = self − other; operator a - b",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.in_box",
   "label": "in_box",
   "composite": "model.queries",
   "signature": "GeometryChain.in_box(lo, hi, **kw) -> GeometryChain",
   "summary": "Refine the chain to entities whose BRep bbox lies in [lo,hi]; inclusive= forbidden.",
   "file": "src/apeGmsh/core/_selection.py:836",
   "flow": [
    {
     "node": "selection",
     "action": "reject any kw (inclusive= inexpressible for entity family); _spatial_box",
     "passes": "lo,hi: 3-seq",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getEntitiesInBoundingBox per distinct dim; intersect with chain atoms preserving order",
     "passes": "refined atoms",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap -> new GeometryChain (same engine)",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "lo,hi: lower/upper 3-corners; NO keywords (TypeError if inclusive= or any kw passed — R3)",
   "outputs": "a refined GeometryChain (Gmsh BRep bbox-containment, intersected with current atoms)",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.in_sphere",
   "label": "in_sphere",
   "composite": "model.queries",
   "signature": "GeometryChain.in_sphere(center, radius) -> GeometryChain",
   "summary": "Refine to entities whose bbox centre is within radius of center.",
   "file": "src/apeGmsh/_chain.py:140",
   "flow": [
    {
     "node": "selection",
     "action": "_spatial_sphere: _coords_of = bbox centre per (dim,tag); ||c-ctr||<=r mask",
     "passes": "center,radius",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getBoundingBox per atom for centre",
     "passes": "kept atoms",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap -> new GeometryChain",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "center: 3-seq; radius: float (>=0, else ValueError)",
   "outputs": "a refined GeometryChain (closed ball on entity bbox centre — coarse proxy)",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.intersect",
   "label": "intersect",
   "composite": "model.queries",
   "signature": "GeometryChain.intersect(other) -> GeometryChain  # also a & b",
   "summary": "Intersection of two compatible GeometryChains.",
   "file": "src/apeGmsh/_chain.py:187",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); keep=set(other._items); filter self._items in keep",
     "passes": "other chain",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap(filtered)",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "other: another GeometryChain bound to the same engine",
   "outputs": "a new GeometryChain = self ∩ other; operator a & b",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.nearest_to",
   "label": "nearest_to",
   "composite": "model.queries",
   "signature": "GeometryChain.nearest_to(point, *, count=1) -> GeometryChain",
   "summary": "Refine to the count entities whose bbox centre is nearest a point.",
   "file": "src/apeGmsh/_chain.py:146",
   "flow": [
    {
     "node": "selection",
     "action": "_nearest: _coords_of = bbox centres; sort by squared distance (index tie-break); take count",
     "passes": "point,count",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getBoundingBox per atom for centre",
     "passes": "nearest atoms",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap -> new GeometryChain",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "point: 3-seq; count: int (default 1)",
   "outputs": "a refined GeometryChain of the count nearest entities (by bbox-centre distance)",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.on_plane",
   "label": "on_plane",
   "composite": "model.queries",
   "signature": "GeometryChain.on_plane(point, normal, *, tol) -> GeometryChain",
   "summary": "Refine to entities lying entirely on a plane (all 8 bbox corners within tol).",
   "file": "src/apeGmsh/_chain.py:143",
   "flow": [
    {
     "node": "selection",
     "action": "_spatial_plane: build Plane(normal,anchor); per atom getBoundingBox; keep if all 8 |signed_dist|<=tol",
     "passes": "point,normal,tol",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getBoundingBox per atom",
     "passes": "kept atoms",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap -> new GeometryChain",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "point: anchor 3-seq; normal: 3-seq (nonzero); tol float (>=0)",
   "outputs": "a refined GeometryChain (legacy on= 'on' test through the unified surface)",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.result",
   "label": "result",
   "composite": "model.queries",
   "signature": "GeometryChain.result() -> Selection",
   "summary": "Materialise the chain into the legacy Selection terminal (unchanged .to_label/.to_physical/.tags).",
   "file": "src/apeGmsh/core/_selection.py:945",
   "flow": [
    {
     "node": "selection",
     "action": "_materialize(): Selection(list(self._items), _queries=self._engine)",
     "passes": "current atoms + _Queries engine",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "return legacy Selection wired exactly like queries.select(...) output",
     "passes": "Selection",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "a legacy Selection (so .to_label/.to_physical/.tags/.select(on=) all work through the byte-unchanged terminal)",
   "reads": [
    "selection"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.symmetric_difference",
   "label": "symmetric_difference",
   "composite": "model.queries",
   "signature": "GeometryChain.symmetric_difference(other) -> GeometryChain  # also a ^ b",
   "summary": "Symmetric difference of two compatible GeometryChains.",
   "file": "src/apeGmsh/_chain.py:197",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); only = set(self)^set(other); preserve order across self+other",
     "passes": "other chain",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap(ordered only)",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "other: another GeometryChain bound to the same engine",
   "outputs": "a new GeometryChain = self △ other; operator a ^ b",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.union",
   "label": "union",
   "composite": "model.queries",
   "signature": "GeometryChain.union(other) -> GeometryChain  # also a | b",
   "summary": "Union of two compatible GeometryChains (same type + engine), order-preserving dedup.",
   "file": "src/apeGmsh/_chain.py:183",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other): same subclass & same _engine else TypeError",
     "passes": "other chain",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap(self._items + other._items) with dedup",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "other: another GeometryChain bound to the same engine",
   "outputs": "a new GeometryChain = self ∪ other; operator a | b",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GeometryChain.where",
   "label": "where",
   "composite": "model.queries",
   "signature": "GeometryChain.where(predicate) -> GeometryChain",
   "summary": "Keep entities whose bbox-centre coordinate row satisfies a predicate callable.",
   "file": "src/apeGmsh/_chain.py:149",
   "flow": [
    {
     "node": "selection",
     "action": "_coords_of(items) bbox centres; keep atoms where predicate(xyz) is truthy",
     "passes": "predicate callable",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getBoundingBox per atom",
     "passes": "filtered atoms",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "_wrap -> new GeometryChain",
     "passes": "GeometryChain",
     "to": "selection"
    }
   ],
   "inputs": "predicate: callable(np.ndarray xyz)->bool applied to each entity's bbox centre",
   "outputs": "a refined GeometryChain",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "GroupResult.__bool__",
   "label": "bool(GroupResult)",
   "composite": "fem.elements",
   "signature": "__bool__() -> bool",
   "summary": "True if the result holds at least one group.",
   "file": "src/apeGmsh/mesh/_element_types.py:267",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return len(self._groups) > 0",
     "passes": "bool",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "bool",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.__iter__",
   "label": "iter(GroupResult)",
   "composite": "fem.elements",
   "signature": "__iter__() -> Iterator[ElementGroup]",
   "summary": "Iterate the per-type ElementGroup blocks in the selection result.",
   "file": "src/apeGmsh/mesh/_element_types.py:261",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return iter(self._groups)",
     "passes": "Iterator[ElementGroup]",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of ElementGroup",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.__len__",
   "label": "len(GroupResult)",
   "composite": "fem.elements",
   "signature": "__len__() -> int",
   "summary": "Number of per-type groups in the result.",
   "file": "src/apeGmsh/mesh/_element_types.py:264",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return len(self._groups)",
     "passes": "int",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "group count int",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.connectivity",
   "label": "connectivity",
   "composite": "fem.elements",
   "signature": "property connectivity -> ndarray",
   "summary": "Flat connectivity — only if the result is homogeneous; raises TypeError when multiple element types are present.",
   "file": "src/apeGmsh/mesh/_element_types.py:294",
   "flow": [
    {
     "node": "fem.elements",
     "action": "if homogeneous return _groups[0].connectivity else TypeError",
     "passes": "ndarray(N,npe) int64 | TypeError",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,npe) int64 (homogeneous only)",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.get",
   "label": "get",
   "composite": "fem.elements",
   "signature": "get(*, dim: int | None = None, element_type: str | int | None = None) -> GroupResult",
   "summary": "Chainable re-filter of an existing result by dim and/or element_type (AND intersection).",
   "file": "src/apeGmsh/mesh/_element_types.py:317",
   "flow": [
    {
     "node": "fem.elements",
     "action": "filter _groups by g.dim and resolve_type_filter(element_type)",
     "passes": "type_codes set[int]",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "return new GroupResult(filtered)",
     "passes": "list[ElementGroup]",
     "to": "fem.elements"
    }
   ],
   "inputs": "dim and/or element_type",
   "outputs": "GroupResult (re-filtered)",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.ids",
   "label": "ids",
   "composite": "fem.elements",
   "signature": "property ids -> ndarray",
   "summary": "All element IDs concatenated across the result's groups (ndarray int64).",
   "file": "src/apeGmsh/mesh/_element_types.py:272",
   "flow": [
    {
     "node": "fem.elements",
     "action": "np.concatenate([g.ids for g in _groups])",
     "passes": "ndarray(E,) int64",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(E,) int64 element IDs",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.is_homogeneous",
   "label": "is_homogeneous",
   "composite": "fem.elements",
   "signature": "property is_homogeneous -> bool",
   "summary": "True if all elements in the result are the same type (<=1 group).",
   "file": "src/apeGmsh/mesh/_element_types.py:289",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return len(self._groups) <= 1",
     "passes": "bool",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "bool",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.n_elements",
   "label": "n_elements",
   "composite": "fem.elements",
   "signature": "property n_elements -> int",
   "summary": "Total element count across all groups in the result.",
   "file": "src/apeGmsh/mesh/_element_types.py:279",
   "flow": [
    {
     "node": "fem.elements",
     "action": "sum(len(g) for g in _groups)",
     "passes": "int",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "int element count",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.resolve",
   "label": "resolve",
   "composite": "fem.elements",
   "signature": "resolve(element_type: str | int | None = None) -> tuple[ndarray, ndarray]",
   "summary": "Flatten the result to (ids, connectivity) arrays; optionally filter to one element_type first; raises TypeError if multiple types and no element_type given.",
   "file": "src/apeGmsh/mesh/_element_types.py:345",
   "flow": [
    {
     "node": "fem.elements",
     "action": "optional self.get(element_type=); require homogeneous (else TypeError)",
     "passes": "GroupResult",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "return (group.ids, group.connectivity)",
     "passes": "(ids int64(N,), connectivity int64(N,npe))",
     "to": "fem.elements"
    }
   ],
   "inputs": "optional element_type",
   "outputs": "(ids ndarray, connectivity ndarray)",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "GroupResult.types",
   "label": "types",
   "composite": "fem.elements",
   "signature": "property types -> list[ElementTypeInfo]",
   "summary": "Element types present in the result.",
   "file": "src/apeGmsh/mesh/_element_types.py:284",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return [g.element_type for g in _groups]",
     "passes": "list[ElementTypeInfo]",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "list[ElementTypeInfo]",
   "reads": [
    "GroupResult._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "LayerSlab",
   "label": "LayerSlab",
   "composite": "results.slabs",
   "signature": "@dataclass(frozen=True) LayerSlab(component, values: ndarray (T,sum_L), element_index, gp_index, layer_index, sub_gp_index, thickness: each ndarray (sum_L,), local_axes_quaternion: ndarray (sum_L,4), time: ndarray (T,))",
   "summary": "Immutable layered-shell layer slab returned by results.elements.layers.get. One row per (element, surface GP, layer, through-thickness sub-GP); carries a per-row local_axes_quaternion. Exported from apeGmsh.results.",
   "file": "src/apeGmsh/results/_slabs.py:105",
   "flow": [
    {
     "node": "results.slabs",
     "action": "constructed by ResultsReader.read_layers",
     "passes": "LayerSlab",
     "to": "external"
    }
   ],
   "inputs": "(constructed by the reader)",
   "outputs": "frozen LayerSlab dataclass (.component/.values/.element_index/.gp_index/.layer_index/.sub_gp_index/.thickness/.local_axes_quaternion/.time)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "LineStationSlab",
   "label": "LineStationSlab",
   "composite": "results.slabs",
   "signature": "@dataclass(frozen=True) LineStationSlab(component: str, values: ndarray (T,sum_S), element_index: ndarray (sum_S,), station_natural_coord: ndarray (sum_S,), time: ndarray (T,))",
   "summary": "Immutable beam line-diagram slab returned by results.elements.line_stations.get. One column per integration station; station_natural_coord in [-1,+1], element_index is the parent element per row. Exported from apeGmsh.results.",
   "file": "src/apeGmsh/results/_slabs.py:53",
   "flow": [
    {
     "node": "results.slabs",
     "action": "constructed by ResultsReader.read_line_stations",
     "passes": "LineStationSlab",
     "to": "external"
    }
   ],
   "inputs": "(constructed by the reader)",
   "outputs": "frozen LineStationSlab dataclass (.component/.values/.element_index/.station_natural_coord/.time)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "MeshSelectionChain.__and__",
   "label": "& (intersection)",
   "composite": "mesh_selection",
   "signature": "__and__(other) -> MeshSelectionChain",
   "summary": "Set-intersect two engine-identical live-mesh chains.",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:158",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "inherited SelectionChain set-algebra gated by engine identity",
     "passes": "self._items ∩ other._items -> type(self)(inter, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "other MeshSelectionChain",
   "outputs": "new MeshSelectionChain",
   "reads": [],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.__or__",
   "label": "| (union)",
   "composite": "mesh_selection",
   "signature": "__or__(other) -> MeshSelectionChain",
   "summary": "Set-union two engine-identical live-mesh chains (loud across differing level/dim/session).",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:158",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "inherited SelectionChain set-algebra gated by engine identity (_compatible)",
     "passes": "self._items ∪ other._items (same _engine) -> type(self)(union, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "other MeshSelectionChain",
   "outputs": "new MeshSelectionChain",
   "reads": [],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.__sub__",
   "label": "- (difference)",
   "composite": "mesh_selection",
   "signature": "__sub__(other) -> MeshSelectionChain",
   "summary": "Set-difference of two engine-identical live-mesh chains.",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:158",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "inherited SelectionChain set-algebra gated by engine identity",
     "passes": "self._items \\ other._items -> type(self)(diff, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "other MeshSelectionChain",
   "outputs": "new MeshSelectionChain",
   "reads": [],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.__xor__",
   "label": "^ (symmetric difference)",
   "composite": "mesh_selection",
   "signature": "__xor__(other) -> MeshSelectionChain",
   "summary": "Symmetric-difference of two engine-identical live-mesh chains.",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:158",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "inherited SelectionChain set-algebra gated by engine identity",
     "passes": "self._items △ other._items -> type(self)(symdiff, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "other MeshSelectionChain",
   "outputs": "new MeshSelectionChain",
   "reads": [],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.ids",
   "label": "ids",
   "composite": "mesh_selection",
   "signature": "ids -> list[int]  (property)",
   "summary": "The selected live-mesh ids (node ids or element ids) of the narrowed chain.",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:304",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "return int-cast list of the chain's current atoms",
     "passes": "[int(a) for a in self._items] -> list[int]",
     "to": "mesh_selection"
    }
   ],
   "inputs": "none",
   "outputs": "list[int]",
   "reads": [
    "chain atoms"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.in_box",
   "label": "in_box",
   "composite": "mesh_selection",
   "signature": "in_box(lo, hi, *, inclusive=False) -> MeshSelectionChain",
   "summary": "Narrow the live-mesh chain to atoms whose node coords / element centroids fall in an axis-aligned box (half-open by default).",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:265",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "base SelectionChain.in_box calls _spatial_box; _coords_of fetches live node coords or fail-loud element centroids via the engine's MeshSelectionSet",
     "passes": "_coords_of(atoms) -> mask -> type(self)(kept, _engine=self._engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "lo (x,y,z), hi (x,y,z), inclusive",
   "outputs": "new MeshSelectionChain (covariant)",
   "reads": [
    "live gmsh mesh nodes/elements via _LiveMeshEngine.ms"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.in_sphere",
   "label": "in_sphere",
   "composite": "mesh_selection",
   "signature": "in_sphere(center, radius) -> MeshSelectionChain",
   "summary": "Narrow the live-mesh chain to atoms within a closed ball.",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:277",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "base SelectionChain.in_sphere calls _spatial_sphere; ||c-center||<=r masked over _coords_of(atoms)",
     "passes": "_coords_of(atoms); norm<=radius -> type(self)(kept, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "center (x,y,z), radius float",
   "outputs": "new MeshSelectionChain",
   "reads": [
    "live gmsh mesh nodes/elements via engine"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.nearest_to",
   "label": "nearest_to",
   "composite": "mesh_selection",
   "signature": "nearest_to(point, count=1) -> MeshSelectionChain",
   "summary": "Narrow the live-mesh chain to the N atoms nearest a point (point-family verb from SelectionChain).",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:158",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "inherited point-family verb; ranks atoms by distance using _coords_of and keeps the closest count",
     "passes": "_coords_of(atoms) -> nearest indices -> type(self)(kept, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "point (x,y,z), count int",
   "outputs": "new MeshSelectionChain",
   "reads": [
    "live gmsh mesh nodes/elements via engine"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.on_plane",
   "label": "on_plane",
   "composite": "mesh_selection",
   "signature": "on_plane(point, normal, *, tol=...) -> MeshSelectionChain",
   "summary": "Narrow the live-mesh chain to atoms within tol of a plane (point+normal).",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:288",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "base SelectionChain.on_plane calls _spatial_plane; signed distance to plane masked over _coords_of(atoms)",
     "passes": "_coords_of(atoms); dist=|(c-p)·n̂| -> type(self)(kept, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "point (x,y,z), normal (x,y,z), tol",
   "outputs": "new MeshSelectionChain",
   "reads": [
    "live gmsh mesh nodes/elements via engine"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.result",
   "label": "result",
   "composite": "mesh_selection",
   "signature": "result() -> dict",
   "summary": "Materialise the narrowed chain into the same-shape dict get_nodes/get_elements return (live coords/connectivity).",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:309",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "node level -> {'tags','coords'} from live coords; element level -> mask live (ids,conn) preserving row order",
     "passes": "_materialize() via engine.ms._get_mesh_nodes/_get_mesh_elements -> dict",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "dict ({'tags','coords'} or {'element_ids','connectivity'})",
   "reads": [
    "live gmsh mesh nodes/elements via engine"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MeshSelectionChain.where",
   "label": "where",
   "composite": "mesh_selection",
   "signature": "where(predicate) -> MeshSelectionChain",
   "summary": "Narrow the live-mesh chain by a user predicate over atom coordinates (inherited point-family verb).",
   "file": "src/apeGmsh/mesh/_mesh_selection_chain.py:158",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "inherited SelectionChain.where applies predicate over _coords_of(atoms)",
     "passes": "predicate(coords) -> mask -> type(self)(kept, _engine)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "predicate callable over coords",
   "outputs": "new MeshSelectionChain",
   "reads": [
    "live gmsh mesh nodes/elements via engine"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "MshLoader.load",
   "label": "load",
   "composite": "loader",
   "signature": "load(path, *, dim=2, verbose=False) -> FEMData  (classmethod)",
   "summary": "Standalone: open a temporary Gmsh session, load a .msh, and return a self-contained FEMData (no session).",
   "file": "src/apeGmsh/mesh/MshLoader.py:97",
   "flow": [
    {
     "node": "loader",
     "action": "validate the .msh path",
     "passes": "_validate_path(path) -> Path",
     "to": "loader"
    },
    {
     "node": "loader",
     "action": "delegate to FEMData.from_msh which manages its own gmsh session (merge + extract + teardown)",
     "passes": "FEMData.from_msh(str(p), dim:int)",
     "to": "fem.extract"
    },
    {
     "node": "fem.extract",
     "action": "return a self-contained broker with physical groups + mesh stats + connectivity",
     "passes": "FEMData(info.n_nodes, info.n_elems, info.bandwidth)",
     "to": "femdata"
    }
   ],
   "inputs": "path str|Path, dim, verbose",
   "outputs": "FEMData object",
   "reads": [
    ".msh file on disk"
   ],
   "writes": [
    "temporary internal gmsh session (torn down)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "Node",
   "label": "Node",
   "composite": "os.node",
   "signature": "Node(*, tag, coords, bridge)  [obtain via ops.nodes.get(tag=...)]",
   "summary": "Lightweight typed wrapper over one FEM node carrying tag/coords + bridge-side fix/mass verbs.",
   "file": "src/apeGmsh/opensees/node.py:61",
   "flow": [
    {
     "node": "os.node",
     "action": "hold tag+coords cached + back-ref to bridge",
     "passes": "Node(tag,coords,bridge)",
     "to": "apesees"
    }
   ],
   "inputs": "tag:int, coords:(x,y,z), bridge:apeSees (constructed via ops.nodes.get)",
   "outputs": "Node (with .tag, .coords, .fix, .mass)",
   "reads": [
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "Node.fix",
   "label": "fix",
   "composite": "os.node",
   "signature": "fix(*, dofs: tuple[int,...]) -> None",
   "summary": "Apply a homogeneous SP constraint at this node (delegates to ops.fix(nodes=(tag,))).",
   "file": "src/apeGmsh/opensees/node.py:89",
   "flow": [
    {
     "node": "os.node",
     "action": "Node.fix calls bridge.fix(nodes=(self.tag,),dofs=)",
     "passes": "single-tag FixRecord",
     "to": "apesees"
    },
    {
     "node": "apesees",
     "action": "append FixRecord consumed at emit time",
     "passes": "FixRecord",
     "to": "os.build"
    }
   ],
   "inputs": "dofs:tuple[int,...]",
   "outputs": "None (appends a FixRecord)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "Node.mass",
   "label": "mass",
   "composite": "os.node",
   "signature": "mass(*, values: tuple[float,...]) -> None",
   "summary": "Attach lumped nodal mass at this node (delegates to ops.mass(nodes=(tag,))).",
   "file": "src/apeGmsh/opensees/node.py:97",
   "flow": [
    {
     "node": "os.node",
     "action": "Node.mass calls bridge.mass(nodes=(self.tag,),values=)",
     "passes": "single-tag MassRecord",
     "to": "apesees"
    },
    {
     "node": "apesees",
     "action": "append MassRecord consumed at emit time",
     "passes": "MassRecord",
     "to": "os.build"
    }
   ],
   "inputs": "values:tuple[float,...]",
   "outputs": "None (appends a MassRecord)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "NodeResult.__bool__",
   "label": "bool(NodeResult)",
   "composite": "fem.nodes",
   "signature": "__bool__() -> bool",
   "summary": "True if the result holds at least one node.",
   "file": "src/apeGmsh/mesh/FEMData.py:121",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return len(self._ids) > 0",
     "passes": "bool",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "bool",
   "reads": [
    "NodeResult._ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "NodeResult.__iter__",
   "label": "iter(NodeResult)",
   "composite": "fem.nodes",
   "signature": "__iter__() -> Iterator[tuple[int, ndarray]]",
   "summary": "Yields (node_id, xyz) pairs — the one-liner for solver node emission (ops.node(nid, *xyz)).",
   "file": "src/apeGmsh/mesh/FEMData.py:114",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "zip(_ids, _coords) yielding (int, ndarray(3,))",
     "passes": "(node_id:int, xyz:float64(3,))",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of (node_id, xyz) pairs",
   "reads": [
    "NodeResult._ids",
    "NodeResult._coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "NodeResult.__len__",
   "label": "len(NodeResult)",
   "composite": "fem.nodes",
   "signature": "__len__() -> int",
   "summary": "Number of nodes in the result.",
   "file": "src/apeGmsh/mesh/FEMData.py:118",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return len(self._ids)",
     "passes": "int",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "node count int",
   "reads": [
    "NodeResult._ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "NodeResult.coords",
   "label": "coords",
   "composite": "fem.nodes",
   "signature": "property coords -> ndarray",
   "summary": "Selected node coordinates as ndarray(N, 3) float64.",
   "file": "src/apeGmsh/mesh/FEMData.py:110",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return _coords (float64 at NodeResult.__init__)",
     "passes": "ndarray(N,3) float64",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,3) float64 coordinates",
   "reads": [
    "NodeResult._coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "NodeResult.ids",
   "label": "ids",
   "composite": "fem.nodes",
   "signature": "property ids -> ndarray",
   "summary": "Selected node IDs as ndarray(N,) object dtype (iterates as Python int); the materialized result of fem.nodes.get()/select().result().",
   "file": "src/apeGmsh/mesh/FEMData.py:106",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return _ids (object-dtype coerced at NodeResult.__init__)",
     "passes": "ndarray(N,) object",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,) object dtype node IDs",
   "reads": [
    "NodeResult._ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "NodeResult.to_dataframe",
   "label": "to_dataframe",
   "composite": "fem.nodes",
   "signature": "to_dataframe() -> pd.DataFrame",
   "summary": "DataFrame of the selected nodes indexed by node_id with x/y/z columns.",
   "file": "src/apeGmsh/mesh/FEMData.py:127",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "build pd.DataFrame from _coords indexed by int(_ids)",
     "passes": "pd.DataFrame",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame (node_id → x,y,z)",
   "reads": [
    "NodeResult._ids",
    "NodeResult._coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "NodeSet",
   "label": "NodeSet",
   "composite": "os.node",
   "signature": "NodeSet(*, nodes, bridge)  [obtain via ops.nodes.get(pg=...)]",
   "summary": "Collection of Node instances from one bridge with whole-set fix/mass verbs + container protocol.",
   "file": "src/apeGmsh/opensees/node.py:126",
   "flow": [
    {
     "node": "os.node",
     "action": "hold Node tuple + bridge back-ref",
     "passes": "NodeSet(nodes,bridge)",
     "to": "apesees"
    }
   ],
   "inputs": "nodes:tuple[Node,...], bridge:apeSees (constructed via ops.nodes.get(pg=))",
   "outputs": "NodeSet (iterable, indexable, .tags, .summary, .fix, .mass)",
   "reads": [
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "NodeSet.fix",
   "label": "fix",
   "composite": "os.node",
   "signature": "fix(*, dofs: tuple[int,...]) -> None",
   "summary": "Apply fix to every node in the set as one explicit-tag FixRecord (no PG fan-out at emit).",
   "file": "src/apeGmsh/opensees/node.py:149",
   "flow": [
    {
     "node": "os.node",
     "action": "NodeSet.fix calls bridge.fix(nodes=tuple-of-tags,dofs=)",
     "passes": "multi-tag FixRecord",
     "to": "apesees"
    },
    {
     "node": "apesees",
     "action": "append FixRecord consumed at emit time",
     "passes": "FixRecord",
     "to": "os.build"
    }
   ],
   "inputs": "dofs:tuple[int,...]",
   "outputs": "None (appends a FixRecord; no-op if empty)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "NodeSet.mass",
   "label": "mass",
   "composite": "os.node",
   "signature": "mass(*, values: tuple[float,...]) -> None",
   "summary": "Attach mass to every node in the set as one explicit-tag MassRecord.",
   "file": "src/apeGmsh/opensees/node.py:163",
   "flow": [
    {
     "node": "os.node",
     "action": "NodeSet.mass calls bridge.mass(nodes=tuple-of-tags,values=)",
     "passes": "multi-tag MassRecord",
     "to": "apesees"
    },
    {
     "node": "apesees",
     "action": "append MassRecord consumed at emit time",
     "passes": "MassRecord",
     "to": "os.build"
    }
   ],
   "inputs": "values:tuple[float,...]",
   "outputs": "None (appends a MassRecord; no-op if empty)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "NodeSet.summary",
   "label": "summary",
   "composite": "os.node",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame of the set's nodes with columns tag, x, y, z.",
   "file": "src/apeGmsh/opensees/node.py:195",
   "flow": [
    {
     "node": "os.node",
     "action": "build DataFrame from each Node tag+coords",
     "passes": "DataFrame(tag,x,y,z)",
     "to": "os.node"
    }
   ],
   "inputs": "none",
   "outputs": "pd.DataFrame",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "NodeSet.tags",
   "label": "tags",
   "composite": "os.node",
   "signature": "tags -> tuple[int,...]",
   "summary": "Tuple of node tags in the set's iteration order.",
   "file": "src/apeGmsh/opensees/node.py:205",
   "flow": [
    {
     "node": "os.node",
     "action": "map self._nodes to .tag",
     "passes": "tuple[int,...]",
     "to": "os.node"
    }
   ],
   "inputs": "none",
   "outputs": "tuple[int,...]",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "NodeSlab",
   "label": "NodeSlab",
   "composite": "results.slabs",
   "signature": "@dataclass(frozen=True) NodeSlab(component: str, values: ndarray (T,N), node_ids: ndarray (N,), time: ndarray (T,))",
   "summary": "Immutable node-level result slab returned by results.nodes.get / nearest_to / in_box / in_sphere / on_plane and node-level ResultChain.get. values is (T,N); for a scalar time_slice T==1 (leading axis preserved). Exported from apeGmsh.results.",
   "file": "src/apeGmsh/results/_slabs.py:35",
   "flow": [
    {
     "node": "results.slabs",
     "action": "constructed by ResultsReader.read_nodes and handed back to the caller",
     "passes": "NodeSlab",
     "to": "external"
    }
   ],
   "inputs": "(constructed by the reader)",
   "outputs": "frozen NodeSlab dataclass (.component/.values/.node_ids/.time)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Part.__enter__",
   "label": "with part: (__enter__/__exit__)",
   "composite": "part",
   "signature": "with part: ...  (context manager from _SessionBase)",
   "summary": "Context-manager sugar: __enter__ calls begin(), __exit__ calls end() (triggering auto-persist of STEP + sidecar).",
   "file": "src/apeGmsh/core/Part.py:104",
   "flow": [
    {
     "node": "part",
     "action": "__enter__ -> Part.begin() opens gmsh session",
     "passes": "self",
     "to": "session"
    },
    {
     "node": "part",
     "action": "__exit__ -> Part.end() auto-persists + finalises gmsh",
     "passes": "STEP + sidecar paths",
     "to": "model.io"
    }
   ],
   "inputs": "none (with-statement)",
   "outputs": "Part (self) inside block; on exit geometry persisted to tempfile + sidecar",
   "reads": [
    "inherited _SessionBase __enter__/__exit__"
   ],
   "writes": [
    "see Part.begin / Part.end"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.__init__",
   "label": "Part",
   "composite": "part",
   "signature": "Part(name: str, *, auto_persist: bool = True)",
   "summary": "Construct an isolated geometry-only unit with its own Gmsh session; registers the name in the process-wide clash table for copy/pattern.",
   "file": "src/apeGmsh/core/Part.py:147",
   "flow": [
    {
     "node": "part",
     "action": "_SessionBase.__init__ sets up composites Model/Labels/PhysicalGroups/Inspect/Plot/PartEdit",
     "passes": "name: str, verbose=False",
     "to": "session"
    },
    {
     "node": "part",
     "action": "register name in process-global _LIVE_PART_NAMES weakdict",
     "passes": "name: str, self",
     "to": "part"
    },
    {
     "node": "part",
     "action": "init file_path=None, properties={}, auto-persist bookkeeping",
     "passes": "auto_persist: bool",
     "to": "part"
    }
   ],
   "inputs": "name (Gmsh model name); optional auto_persist=bool",
   "outputs": "Part instance (composites model/labels/physical/inspect/plot/edit not yet active)",
   "reads": [],
   "writes": [
    "_LIVE_PART_NAMES[name] = self",
    "self.file_path / properties / _auto_persist / _owns_file"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.begin",
   "label": "begin",
   "composite": "part",
   "signature": "begin(*, verbose: bool | None = None) -> Part",
   "summary": "Open the Part's isolated Gmsh session; if reused, cleans up a stale auto-persisted tempfile first so the next end() persists fresh geometry.",
   "file": "src/apeGmsh/core/Part.py:173",
   "flow": [
    {
     "node": "part",
     "action": "if _owns_file: cleanup() stale tempfile and reset file_path",
     "passes": "_owns_file: bool",
     "to": "part"
    },
    {
     "node": "part",
     "action": "_SessionBase.begin() initialises gmsh + creates composites",
     "passes": "verbose: bool|None",
     "to": "session"
    }
   ],
   "inputs": "optional verbose=bool",
   "outputs": "Part (self, session now active; model/labels/physical/inspect/edit live)",
   "reads": [
    "self._owns_file"
   ],
   "writes": [
    "gmsh.initialize (via _SessionBase.begin)",
    "self.file_path (reset if reused)",
    "self._active"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.cleanup",
   "label": "cleanup",
   "composite": "part",
   "signature": "cleanup() -> None",
   "summary": "Eagerly delete any auto-persisted tempfile/tempdir now (guarded by _owns_file so an explicitly-saved user file is never touched); idempotent.",
   "file": "src/apeGmsh/core/Part.py:273",
   "flow": [
    {
     "node": "part",
     "action": "snapshot _owns_file, run finalizer if alive (rmtree tempdir)",
     "passes": "temp_dir: Path",
     "to": "part"
    },
    {
     "node": "part",
     "action": "reset _owns_file/_temp_dir; if was owned reset file_path",
     "passes": "was_owned: bool",
     "to": "part"
    }
   ],
   "inputs": "none",
   "outputs": "None (tempdir removed; has_file becomes False if it owned the file)",
   "reads": [
    "self._owns_file / _finalizer / _temp_dir"
   ],
   "writes": [
    "filesystem (shutil.rmtree tempdir)",
    "self._owns_file=False, _temp_dir=None, file_path=None (if owned)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.edit",
   "label": "edit",
   "composite": "part",
   "signature": "part.edit -> PartEdit  (composite attribute)",
   "summary": "Whole-Part transform/structural-operation composite (translate/rotate/mirror/scale/copy/pattern/align).",
   "file": "src/apeGmsh/core/Part.py:145",
   "flow": [
    {
     "node": "part",
     "action": "expose PartEdit composite bound to this Part",
     "passes": "PartEdit(self)",
     "to": "part"
    }
   ],
   "inputs": "none (attribute access)",
   "outputs": "PartEdit composite (part.edit.translate/rotate/.../copy/pattern_*/align_to)",
   "reads": [
    "self (composite registry)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.end",
   "label": "end",
   "composite": "part",
   "signature": "end() -> None",
   "summary": "Close the Part's Gmsh session; when auto_persist and no explicit save(), write the geometry + label sidecar to an OS tempfile before finalising Gmsh.",
   "file": "src/apeGmsh/core/Part.py:186",
   "flow": [
    {
     "node": "part",
     "action": "if active+auto_persist+no file+has entities: _auto_persist_to_temp()",
     "passes": "gmsh.model.getEntities() non-empty",
     "to": "part"
    },
    {
     "node": "part",
     "action": "mkdtemp + save(target, write_anchors=True) writes STEP",
     "passes": "target: Path = tempdir/{name}.step",
     "to": "model.io"
    },
    {
     "node": "part",
     "action": "collect_anchors + write_sidecar ({name}.step.apegmsh.json)",
     "passes": "anchors: list[dict] from _label: PGs",
     "to": "labels"
    },
    {
     "node": "part",
     "action": "register weakref.finalize for tempdir, then _SessionBase.end()",
     "passes": "temp_dir: Path",
     "to": "session"
    }
   ],
   "inputs": "none",
   "outputs": "None (Gmsh finalised; tempfile + sidecar written when auto-persisting; warns on persist failure)",
   "reads": [
    "self._active / _auto_persist / file_path",
    "gmsh.model.getEntities",
    "gmsh label PGs (collect_anchors)"
   ],
   "writes": [
    "{name}.step tempfile via gmsh.write",
    "{name}.step.apegmsh.json sidecar",
    "self._owns_file=True, self._finalizer",
    "gmsh.finalize"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.has_file",
   "label": "has_file",
   "composite": "part",
   "signature": "has_file -> bool  (property)",
   "summary": "True if the Part has been written to disk and that file still exists (gate for g.parts.add).",
   "file": "src/apeGmsh/core/Part.py:395",
   "flow": [
    {
     "node": "part",
     "action": "return file_path is not None and file_path.exists()",
     "passes": "file_path: Path|None",
     "to": "part"
    }
   ],
   "inputs": "none (property)",
   "outputs": "bool",
   "reads": [
    "self.file_path",
    "filesystem (exists)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.inspect",
   "label": "inspect",
   "composite": "part",
   "signature": "part.inspect -> Inspect  (composite attribute)",
   "summary": "The Part's geometry inspection composite for querying entities/topology within the Part session.",
   "file": "src/apeGmsh/core/Part.py:143",
   "flow": [
    {
     "node": "part",
     "action": "expose Inspect composite over the Part's session",
     "passes": "Inspect for this Part",
     "to": "model.queries"
    }
   ],
   "inputs": "none (attribute access)",
   "outputs": "Inspect composite",
   "reads": [
    "self (composite registry)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.labels",
   "label": "labels",
   "composite": "part",
   "signature": "part.labels -> Labels  (composite attribute)",
   "summary": "The Part's own Labels composite; label= on geometry add_* auto-creates label PGs here that travel through the STEP sidecar into the assembly.",
   "file": "src/apeGmsh/core/Part.py:141",
   "flow": [
    {
     "node": "part",
     "action": "expose Labels composite over the Part's gmsh session",
     "passes": "Labels (g.labels-equivalent) for this Part",
     "to": "labels"
    }
   ],
   "inputs": "none (attribute access)",
   "outputs": "Labels composite (part.labels.add/entities/get_all/...)",
   "reads": [
    "self (composite registry)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.model",
   "label": "model",
   "composite": "part",
   "signature": "part.model -> Model  (composite attribute)",
   "summary": "The Part's own Model composite (geometry/boolean/transforms/io/queries) used to build the Part's geometry inside its with-block.",
   "file": "src/apeGmsh/core/Part.py:140",
   "flow": [
    {
     "node": "part",
     "action": "expose Model composite created by _SessionBase._create_composites",
     "passes": "Model bound to this Part's gmsh session",
     "to": "model.geometry"
    }
   ],
   "inputs": "none (attribute access; requires active session to use)",
   "outputs": "Model composite (part.model.geometry.add_*, part.model.boolean.*, part.model.transforms.*, part.model.sync())",
   "reads": [
    "self (composite registry)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.physical",
   "label": "physical",
   "composite": "part",
   "signature": "part.physical -> PhysicalGroups  (composite attribute)",
   "summary": "The Part's own PhysicalGroups composite (solver-facing groups); rarely used on Parts since labels are the geometry-time naming tier.",
   "file": "src/apeGmsh/core/Part.py:142",
   "flow": [
    {
     "node": "part",
     "action": "expose PhysicalGroups composite over the Part's session",
     "passes": "PhysicalGroups for this Part",
     "to": "physical"
    }
   ],
   "inputs": "none (attribute access)",
   "outputs": "PhysicalGroups composite",
   "reads": [
    "self (composite registry)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.plot",
   "label": "plot",
   "composite": "part",
   "signature": "part.plot -> Plot  (lazy composite attribute)",
   "summary": "The Part's lazily-created plotting composite for visualizing the Part geometry.",
   "file": "src/apeGmsh/core/Part.py:144",
   "flow": [
    {
     "node": "part",
     "action": "lazily instantiate + expose Plot composite",
     "passes": "Plot for this Part",
     "to": "external",
     "to_raw": "viz.Plot (visualization composite; not in node vocabulary — Part-local plotting of geometry)"
    }
   ],
   "inputs": "none (attribute access)",
   "outputs": "Plot composite",
   "reads": [
    "self (composite registry)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.properties",
   "label": "properties",
   "composite": "part",
   "signature": "part.properties -> dict[str, Any]  (attribute)",
   "summary": "User metadata dict (thickness, material, section type) carried through g.parts.add into the placed Instance.properties.",
   "file": "src/apeGmsh/core/Part.py:155",
   "flow": [
    {
     "node": "part",
     "action": "user-mutable metadata dict copied into Instance on add()",
     "passes": "properties: dict[str,Any]",
     "to": "parts"
    }
   ],
   "inputs": "none (attribute, user-assigned)",
   "outputs": "dict (passed by value into Instance.properties via parts.add)",
   "reads": [],
   "writes": [
    "self.properties (user sets keys)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "Part.save",
   "label": "save",
   "composite": "part",
   "signature": "save(file_path=None, *, fmt=None, write_anchors=True) -> Path",
   "summary": "Export the Part geometry to a named CAD file (STEP/IGES), transferring file ownership to the caller and writing the label->COM JSON sidecar.",
   "file": "src/apeGmsh/core/Part.py:301",
   "flow": [
    {
     "node": "part",
     "action": "require active session; if _owns_file (and not internal) cleanup tempfile",
     "passes": "_owns_file: bool",
     "to": "part"
    },
    {
     "node": "part",
     "action": "resolve path/extension, validate against _VALID_EXT",
     "passes": "file_path: Path, fmt: str|None",
     "to": "part"
    },
    {
     "node": "part",
     "action": "occ.synchronize + gmsh.write(path)",
     "passes": "file_path str",
     "to": "model.io"
    },
    {
     "node": "part",
     "action": "_write_anchors -> collect_anchors + write_sidecar if named entities",
     "passes": "anchors: list[dict] (label PGs)",
     "to": "labels"
    }
   ],
   "inputs": "optional file_path (default '{name}.step'), fmt ('step'/'iges'), write_anchors=bool",
   "outputs": "Path (resolved written CAD file); sidecar written alongside; sets self.file_path",
   "reads": [
    "self._active / _owns_file",
    "gmsh label PGs (collect_anchors)"
   ],
   "writes": [
    "CAD file via gmsh.occ.synchronize + gmsh.write",
    "{path}.apegmsh.json sidecar",
    "self.file_path",
    "cleanup() of prior tempfile"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "ResultChain.difference",
   "label": "ResultChain.difference / -",
   "composite": "results.chain",
   "signature": "difference(other) -> ResultChain  (alias: self - other)",
   "summary": "Set-algebra verb (inherited): keep atoms in self not in other. Same _compatible gate as union. Returns a new ResultChain.",
   "file": "src/apeGmsh/_chain.py:192",
   "flow": [
    {
     "node": "results.chain",
     "action": "_compatible(other) gate",
     "passes": "(validation)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_wrap(a for a in self if a not in set(other))",
     "passes": "ResultChain (difference atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "other (a ResultChain from the same composite/level)",
   "outputs": "ResultChain (difference)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.get",
   "label": "ResultChain.get",
   "composite": "results.chain",
   "signature": "get(*, component: str, time=None, stage=None, **extra) -> NodeSlab | ElementSlab | GaussSlab | FiberSlab | LayerSlab | LineStationSlab | SpringSlab",
   "summary": "Chain terminal: materialise the slab for the chain's narrowed ids. Delegates verbatim to the spawning composite's existing .get(ids=list(self._items), component=, time=, stage=, **extra) — so the slab type and id/value parity match results.<level>.get(ids=<equiv>). **extra forwards each sub-composite's extra kwargs (gp_indices= for fibers; gp_indices=/layer_indices= for layers); unknown kwargs fail loud in the host's .get.",
   "file": "src/apeGmsh/results/_result_chain.py:286",
   "flow": [
    {
     "node": "results.chain",
     "action": "read self._engine.host (the spawning composite/sub-composite)",
     "passes": "host, ids:list[int]",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "host.get(ids=, component=, time=, stage=, **extra) — re-enters the existing composite read path (node OR element family)",
     "passes": "ids:list[int], component:str",
     "to": "results.slabs"
    },
    {
     "node": "results.slabs",
     "action": "the host's reader.read_* returns the matching frozen Slab dataclass",
     "passes": "NodeSlab|ElementSlab|GaussSlab|FiberSlab|LayerSlab|LineStationSlab|SpringSlab",
     "to": "external"
    }
   ],
   "inputs": "component (required), time, stage, plus host-specific **extra (gp_indices/layer_indices)",
   "outputs": "the spawning composite's Slab type",
   "reads": [
    "the spawning composite's .get (→ ResultsReader.read_*)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.in_box",
   "label": "ResultChain.in_box",
   "composite": "results.chain",
   "signature": "in_box(lo, hi, *, inclusive: bool = False) -> ResultChain",
   "summary": "Refining verb (inherited from SelectionChain): keep atoms whose coordinate row is in the box. Default half-open [lo,hi); inclusive=True → closed [lo,hi]. Coordinates are node coords for node-level chains or fail-loud element centroids for element-level chains (computed/memoised on the engine adapter; an unknown connectivity node id raises). Returns a new ResultChain (covariant, same engine) for further chaining.",
   "file": "src/apeGmsh/results/_result_chain.py:247",
   "flow": [
    {
     "node": "results.chain",
     "action": "_coords_of(atoms): node level → fem.nodes.coords via _node_row_map; element level → _centroid_map (fail-loud)",
     "passes": "coords:ndarray(n,3)",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "Results._fem supplies nodes.coords/ids + elements.types/resolve for centroids",
     "passes": "coords:ndarray(n,3)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_spatial_box mask (inclusive flag) → _wrap new ResultChain (same _engine)",
     "passes": "ResultChain (narrowed atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "lo, hi (3-vectors), inclusive flag",
   "outputs": "ResultChain (narrowed)",
   "reads": [
    "FEMData.nodes.coords/ids or elements.types/resolve (via Results._fem)"
   ],
   "writes": [
    "_apegmsh_rc_node_idrow / _apegmsh_rc_elem_centroid memoised on the engine"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.in_sphere",
   "label": "ResultChain.in_sphere",
   "composite": "results.chain",
   "signature": "in_sphere(center, radius: float) -> ResultChain",
   "summary": "Refining verb (inherited): keep atoms within radius of center (closed ball; ValueError on negative radius). Uses the same per-level coordinate source as in_box. Returns a new ResultChain (covariant, same engine).",
   "file": "src/apeGmsh/results/_result_chain.py:259",
   "flow": [
    {
     "node": "results.chain",
     "action": "_coords_of(atoms) → per-level coords (node coords / fail-loud centroids)",
     "passes": "coords:ndarray(n,3)",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "Results._fem supplies coordinate source",
     "passes": "coords:ndarray(n,3)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_spatial_sphere mask (closed ball) → _wrap new ResultChain",
     "passes": "ResultChain (narrowed atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "center (3-vector), radius",
   "outputs": "ResultChain (narrowed)",
   "reads": [
    "FEMData.nodes.coords/ids or elements.types/resolve (via Results._fem)"
   ],
   "writes": [
    "coordinate/centroid cache on the engine"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.intersect",
   "label": "ResultChain.intersect / &",
   "composite": "results.chain",
   "signature": "intersect(other) -> ResultChain  (alias: self & other)",
   "summary": "Set-algebra verb (inherited): keep atoms present in both chains (self order preserved). Same _compatible gate as union (cross-level/results raises). Returns a new ResultChain.",
   "file": "src/apeGmsh/_chain.py:187",
   "flow": [
    {
     "node": "results.chain",
     "action": "_compatible(other) gate",
     "passes": "(validation)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_wrap(a for a in self if a in set(other)) ",
     "passes": "ResultChain (intersection atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "other (a ResultChain from the same composite/level)",
   "outputs": "ResultChain (intersection)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.iter",
   "label": "ResultChain iteration / len / bool / repr",
   "composite": "results.chain",
   "signature": "__iter__() -> Iterator[int] ; __len__() -> int ; __bool__() -> bool ; __repr__() -> str",
   "summary": "Inherited SelectionChain introspection over the narrowed atoms: iterate the selected node/element ids, len() the count, truthiness = non-empty, repr shows FAMILY='point' and atom count. Lets the user inspect a selection before the terminal .get(component=).",
   "file": "src/apeGmsh/_chain.py:216",
   "flow": [
    {
     "node": "results.chain",
     "action": "expose self._items (the de-duplicated narrowed id tuple)",
     "passes": "ids:tuple[int]",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "iterator / int / bool / str over the selected ids",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.nearest_to",
   "label": "ResultChain.nearest_to",
   "composite": "results.chain",
   "signature": "nearest_to(point, *, count: int = 1) -> ResultChain",
   "summary": "Refining verb (inherited from SelectionChain, point-family default _nearest): keep the count atoms closest to point (squared distance via math.fsum, deterministic lowest-index tie-break). Per-level coordinate source as in_box. Returns a new ResultChain (covariant, same engine).",
   "file": "src/apeGmsh/_chain.py:146",
   "flow": [
    {
     "node": "results.chain",
     "action": "_coords_of(atoms) → per-level coords",
     "passes": "coords:ndarray(n,3)",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "Results._fem supplies coordinate source",
     "passes": "coords:ndarray(n,3)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_nearest sorts by squared distance, take first count → _wrap new ResultChain",
     "passes": "ResultChain (≤count atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "point (3-vector), count",
   "outputs": "ResultChain (≤count atoms)",
   "reads": [
    "FEMData.nodes.coords/ids or elements.types/resolve (via Results._fem)"
   ],
   "writes": [
    "coordinate/centroid cache on the engine"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.on_plane",
   "label": "ResultChain.on_plane",
   "composite": "results.chain",
   "signature": "on_plane(point, normal, *, tol: float) -> ResultChain",
   "summary": "Refining verb (inherited): keep atoms within tol of the plane (point + normal; ValueError on zero-length normal or negative tol). Uses the same per-level coordinate source as in_box. Returns a new ResultChain (covariant, same engine).",
   "file": "src/apeGmsh/results/_result_chain.py:270",
   "flow": [
    {
     "node": "results.chain",
     "action": "_coords_of(atoms) → per-level coords",
     "passes": "coords:ndarray(n,3)",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "Results._fem supplies coordinate source",
     "passes": "coords:ndarray(n,3)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_spatial_plane: |(c-p)·n̂| ≤ tol → _wrap new ResultChain",
     "passes": "ResultChain (narrowed atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "point (3-vector), normal (3-vector), tol",
   "outputs": "ResultChain (narrowed)",
   "reads": [
    "FEMData.nodes.coords/ids or elements.types/resolve (via Results._fem)"
   ],
   "writes": [
    "coordinate/centroid cache on the engine"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.result",
   "label": "ResultChain.result",
   "composite": "results.chain",
   "signature": "result()  # raises RuntimeError",
   "summary": "Inherited SelectionChain terminal, overridden to fail loud: a results selection identifies node/element ids but a slab read needs a component, so _materialize raises RuntimeError directing the user to .get(component=...) instead of a bare .result().",
   "file": "src/apeGmsh/results/_result_chain.py:317",
   "flow": [
    {
     "node": "results.chain",
     "action": "_materialize() raises RuntimeError (no component → meaningless)",
     "passes": "RuntimeError",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "(always raises RuntimeError)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.symmetric_difference",
   "label": "ResultChain.symmetric_difference / ^",
   "composite": "results.chain",
   "signature": "symmetric_difference(other) -> ResultChain  (alias: self ^ other)",
   "summary": "Set-algebra verb (inherited): keep atoms in exactly one chain, insertion order preserved across self then other. Same _compatible gate as union. Returns a new ResultChain.",
   "file": "src/apeGmsh/_chain.py:197",
   "flow": [
    {
     "node": "results.chain",
     "action": "_compatible(other) gate",
     "passes": "(validation)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_wrap symmetric-difference atoms (ordered)",
     "passes": "ResultChain (sym-diff atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "other (a ResultChain from the same composite/level)",
   "outputs": "ResultChain (symmetric difference)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.union",
   "label": "ResultChain.union / | ",
   "composite": "results.chain",
   "signature": "union(other) -> ResultChain  (alias: self | other)",
   "summary": "Set-algebra verb (inherited): ordered-dedup concatenation of two ResultChains. _compatible enforces same chain type and same engine identity — two selections from the same results.<level> compose; cross-level / cross-Results / cross-derive raise TypeError. Returns a new ResultChain.",
   "file": "src/apeGmsh/_chain.py:183",
   "flow": [
    {
     "node": "results.chain",
     "action": "_compatible(other): type + engine-identity gate (engine_for memoised per composite/level)",
     "passes": "(validation)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "_wrap(self._items + other._items) with insertion-order dedup",
     "passes": "ResultChain (union atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "other (a ResultChain from the same composite/level)",
   "outputs": "ResultChain (union)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "ResultChain.where",
   "label": "ResultChain.where",
   "composite": "results.chain",
   "signature": "where(predicate) -> ResultChain",
   "summary": "Refining verb (inherited): keep atoms whose coordinate row satisfies predicate(xyz). Per-level coordinate source as in_box. Returns a new ResultChain (covariant, same engine).",
   "file": "src/apeGmsh/_chain.py:149",
   "flow": [
    {
     "node": "results.chain",
     "action": "_coords_of(atoms) → per-level coords",
     "passes": "coords:ndarray(n,3)",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "Results._fem supplies coordinate source",
     "passes": "coords:ndarray(n,3)",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "filter atoms by predicate(xyz) → _wrap new ResultChain",
     "passes": "ResultChain (filtered atoms)",
     "to": "results.chain"
    }
   ],
   "inputs": "predicate (callable on a (3,) coord)",
   "outputs": "ResultChain (filtered)",
   "reads": [
    "FEMData.nodes.coords/ids or elements.types/resolve (via Results._fem)"
   ],
   "writes": [
    "coordinate/centroid cache on the engine"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.__enter__",
   "label": "__enter__ / __exit__",
   "composite": "results",
   "signature": "__enter__() -> Results ; __exit__(*exc) -> None",
   "summary": "Context-manager support: `with Results.from_native(p) as results:` yields the Results and calls .close() (releasing the reader handle) on block exit.",
   "file": "src/apeGmsh/results/Results.py:365",
   "flow": [
    {
     "node": "results",
     "action": "__enter__ returns self; __exit__ calls self.close()",
     "passes": "Results",
     "to": "results.readers"
    }
   ],
   "inputs": "(none)",
   "outputs": "Results (enter); None (exit, reader closed)",
   "reads": [],
   "writes": [
    "closes ResultsReader file handle on exit"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.__repr__",
   "label": "__repr__",
   "composite": "results",
   "signature": "__repr__() -> str",
   "summary": "Repr of a Results is its inspect.summary() — path, bound-FEM node/element counts + snapshot_id, and the per-stage listing. Lets `results` echo a readable summary in a REPL/notebook.",
   "file": "src/apeGmsh/results/Results.py:507",
   "flow": [
    {
     "node": "results",
     "action": "delegate to self.inspect.summary()",
     "passes": "(none)",
     "to": "results.inspect"
    },
    {
     "node": "results.inspect",
     "action": "build the multi-line summary string",
     "passes": "summary:str",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "str summary",
   "reads": [
    "ResultsInspect.summary()"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.bind",
   "label": "bind",
   "composite": "results.bind",
   "signature": "bind(fem: FEMData) -> Results",
   "summary": "Re-bind to a fresh-session FEMData (carries labels/Parts the embedded snapshot may lack). resolve_bound_fem returns the candidate verbatim with no snapshot_id validation, then ._derive produces a new Results sharing the reader and stages-cache but with the swapped FEM and fresh composites. Pairing a consistent FEMData with the results file is the user's responsibility.",
   "file": "src/apeGmsh/results/Results.py:274",
   "flow": [
    {
     "node": "results.bind",
     "action": "resolve_bound_fem(self._reader, fem) returns candidate fem unchanged (no hash check)",
     "passes": "bound:FEMData",
     "to": "results"
    },
    {
     "node": "results",
     "action": "self._derive(fem=bound) — clone Results, share reader + _stages_cache, swap _fem, rebuild nodes/elements/inspect composites",
     "passes": "Results(reader-shared, fem=bound)",
     "to": "results"
    }
   ],
   "inputs": "fem (replacement FEMData)",
   "outputs": "Results (derived, re-bound; same reader/stages, new composites)",
   "reads": [
    "src/apeGmsh/results/_bind.py:resolve_bound_fem"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.close",
   "label": "close",
   "composite": "results",
   "signature": "close() -> None",
   "summary": "Close the underlying reader and release the HDF5 file handle (no-op if the reader has no close()). Important on Windows where an open handle blocks re-running a capture that recreates the same .h5.",
   "file": "src/apeGmsh/results/Results.py:360",
   "flow": [
    {
     "node": "results",
     "action": "if reader has close(): reader.close() releases the HDF5 handle",
     "passes": "(none)",
     "to": "results.readers"
    }
   ],
   "inputs": "(none)",
   "outputs": "None",
   "reads": [],
   "writes": [
    "closes ResultsReader file handle"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.eigenvalue",
   "label": "eigenvalue",
   "composite": "results",
   "signature": "property eigenvalue -> float",
   "summary": "Modal eigenvalue for a mode-scoped Results. Raises AttributeError unless the scoped stage has kind=='mode' (via _require_mode).",
   "file": "src/apeGmsh/results/Results.py:335",
   "flow": [
    {
     "node": "results",
     "action": "_require_mode() (AttributeError if unscoped or kind!='mode') returns StageInfo, read .eigenvalue",
     "passes": "eigenvalue:float",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a mode-scoped Results)",
   "outputs": "float eigenvalue",
   "reads": [
    "StageInfo.eigenvalue (via _require_mode)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.elements",
   "label": "elements",
   "composite": "results.elements",
   "signature": "attribute elements: ElementResultsComposite",
   "summary": "Per-element-node force composite plus the five sub-composites .gauss / .fibers / .layers / .line_stations / .springs (all built in __init__/._derive). Inherits the selection vocabulary and element-centroid geometric selectors.",
   "file": "src/apeGmsh/results/Results.py:104",
   "flow": [
    {
     "node": "results",
     "action": "ElementResultsComposite(self) — builds gauss/fibers/layers/line_stations/springs sub-composites",
     "passes": "ElementResultsComposite",
     "to": "results.elements"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "ElementResultsComposite bound to this Results",
   "reads": [
    "src/apeGmsh/results/_composites.py:ElementResultsComposite"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.fem",
   "label": "fem",
   "composite": "results",
   "signature": "property fem -> Optional[FEMData]",
   "summary": "The currently bound FEMData snapshot (the one selectors resolve pg=/label=/selection= against), or None if Results was constructed bare with no embedded/explicit FEM.",
   "file": "src/apeGmsh/results/Results.py:269",
   "flow": [
    {
     "node": "results",
     "action": "return self._fem (set at construction by resolve_bound_fem or by .bind)",
     "passes": "_fem:Optional[FEMData]",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "Optional[FEMData] — the bound snapshot",
   "reads": [
    "Results._fem"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.frequency_hz",
   "label": "frequency_hz",
   "composite": "results",
   "signature": "property frequency_hz -> float",
   "summary": "Modal natural frequency in Hz for a mode-scoped Results. Raises AttributeError unless the scoped stage has kind=='mode'.",
   "file": "src/apeGmsh/results/Results.py:340",
   "flow": [
    {
     "node": "results",
     "action": "_require_mode() returns StageInfo, read .frequency_hz",
     "passes": "frequency_hz:float",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a mode-scoped Results)",
   "outputs": "float frequency (Hz)",
   "reads": [
    "StageInfo.frequency_hz (via _require_mode)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.from_mpco",
   "label": "from_mpco",
   "composite": "results",
   "signature": "classmethod from_mpco(path: str | Path | list[str | Path], *, fem: Optional[FEMData] = None, merge_partitions: bool = True) -> Results",
   "summary": "Open a STKO .mpco HDF5 results file. Single path → MPCOReader; a .part-N path (or explicit list) auto-discovers siblings via discover_partition_files and builds an MPCOMultiPartitionReader that stitches boundary nodes (first-occurrence dedup) and concatenates elements across partitions. resolve_bound_fem synthesizes a partial FEMData from the MPCO MODEL/ group when fem= is omitted (no apeGmsh labels / Part provenance). merge_partitions=False reads only the named file.",
   "file": "src/apeGmsh/results/Results.py:209",
   "flow": [
    {
     "node": "results",
     "action": "if list/tuple: MPCOMultiPartitionReader(paths) for >1 else MPCOReader(paths[0]); else discover_partition_files(anchor) (unless merge_partitions=False) → multi vs single reader",
     "passes": "path:str|Path|list",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.fem() synthesizes a partial FEMData from MPCO MODEL/ group",
     "passes": "embedded_fem:Optional[FEMData]",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "resolve_bound_fem(reader, fem) prefers candidate fem= else the synthesized MPCO snapshot",
     "passes": "bound_fem:Optional[FEMData]",
     "to": "results"
    },
    {
     "node": "results",
     "action": "Results(reader, fem=bound_fem, path=anchor) — unscoped, partition-stitched slabs are transparent",
     "passes": "Results(reader, bound_fem, anchor)",
     "to": "results"
    }
   ],
   "inputs": "path (one .mpco / .part-N .mpco / list of partition paths), optional fem=, merge_partitions flag",
   "outputs": "Results (unscoped; MPCOReader or MPCOMultiPartitionReader backing)",
   "reads": [
    "src/apeGmsh/results/readers/_mpco.py:MPCOReader",
    "src/apeGmsh/results/readers/_mpco_multi.py:MPCOMultiPartitionReader, discover_partition_files",
    "MPCO MODEL/ group"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.from_native",
   "label": "from_native",
   "composite": "results",
   "signature": "classmethod from_native(path: str | Path, *, fem: Optional[FEMData] = None) -> Results",
   "summary": "Open an apeGmsh native HDF5 results file. Constructs a NativeReader over the path, then resolve_bound_fem picks the FEMData: explicit fem= wins, else the reader's embedded /model/ snapshot is reconstructed. No snapshot_id equality is enforced (hash is computed/stored for metadata only); pairing is the user's responsibility. Returns a top-level (unscoped) Results carrying all stages.",
   "file": "src/apeGmsh/results/Results.py:112",
   "flow": [
    {
     "node": "results",
     "action": "construct NativeReader(path) over the native HDF5",
     "passes": "path:str|Path",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.fem() reconstructs the embedded FEMData snapshot from /model/",
     "passes": "embedded_fem:Optional[FEMData]",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "resolve_bound_fem(reader, fem) returns candidate fem= if given else the embedded snapshot (no snapshot_id hash check)",
     "passes": "bound_fem:Optional[FEMData]",
     "to": "results"
    },
    {
     "node": "results",
     "action": "Results(reader, fem=bound_fem, path=Path(path)) — builds nodes/elements/inspect composites, _stage_id=None (unscoped)",
     "passes": "Results(reader, bound_fem, path)",
     "to": "results"
    }
   ],
   "inputs": "path (native .h5 file), optional fem= FEMData override",
   "outputs": "Results (unscoped, bound to FEMData, all stages visible)",
   "reads": [
    "src/apeGmsh/results/readers/_native.py:NativeReader",
    "embedded /model/ snapshot in the .h5"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.from_recorders",
   "label": "from_recorders",
   "composite": "results",
   "signature": "classmethod from_recorders(spec, output_dir: str | Path, *, fem: FEMData, cache_root=None, stage_name='analysis', stage_kind='transient', file_format='out', stage_id=None) -> Results",
   "summary": "Open an OpenSees run driven by Tcl/Py recorders. Requires fem= (TypeError otherwise). Builds a cache key from source file mtime+size, PARSER_VERSION and fem.snapshot_id; on cache miss runs RecorderTranscoder to transcode the .out/.xml files at output_dir into a native HDF5 at cache_root, then delegates to from_native(cached_h5, fem=fem). stage_id filters per-stage filename prefixes; Phase 6 v1 supports nodal records only.",
   "file": "src/apeGmsh/results/Results.py:130",
   "flow": [
    {
     "node": "results",
     "action": "require fem (else TypeError); if stage_id set and stage_name default, mirror stage_name=stage_id",
     "passes": "spec, output_dir, fem:FEMData",
     "to": "results.transcode"
    },
    {
     "node": "results.transcode",
     "action": "_cache.list_source_files + compute_cache_key(parser_version, fem.snapshot_id); cache_paths → cached_h5",
     "passes": "cache_key:str, cached_h5:Path",
     "to": "results.writers"
    },
    {
     "node": "results.writers",
     "action": "if cached_h5 missing: RecorderTranscoder(spec, out_dir, cached_h5, fem, ...).run() parses .out/.xml into native HDF5",
     "passes": "native_h5_file:Path",
     "to": "results"
    },
    {
     "node": "results",
     "action": "cls.from_native(cached_h5, fem=fem) — reopen the transcoded HDF5 through NativeReader",
     "passes": "cached_h5:Path, fem:FEMData",
     "to": "results.readers"
    }
   ],
   "inputs": "spec (ResolvedRecorderSpec), output_dir (.out/.xml dir), required fem=, cache_root, stage_name/kind/id, file_format",
   "outputs": "Results (unscoped; backed by cached native HDF5)",
   "reads": [
    "src/apeGmsh/results/schema/_versions.py:PARSER_VERSION",
    "src/apeGmsh/results/transcoders:RecorderTranscoder",
    "src/apeGmsh/results/writers/_cache.py",
    "OpenSees .out/.xml recorder files at output_dir"
   ],
   "writes": [
    "cached native HDF5 at cache_root (compute_cache_key keyed)"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.inspect",
   "label": "inspect",
   "composite": "results.inspect",
   "signature": "attribute inspect: ResultsInspect",
   "summary": "Inspection composite (built in __init__/._derive). Exposes summary(), components(stage=) and diagnose(component, stage=) — answers what's in the file, what stages exist, and why a component slab might be empty.",
   "file": "src/apeGmsh/results/Results.py:105",
   "flow": [
    {
     "node": "results",
     "action": "ResultsInspect(self) — back-ref _r to the owning Results",
     "passes": "ResultsInspect",
     "to": "results.inspect"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "ResultsInspect bound to this Results",
   "reads": [
    "src/apeGmsh/results/_inspect.py:ResultsInspect"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.kind",
   "label": "kind",
   "composite": "results",
   "signature": "property kind -> str",
   "summary": "Stage kind ('transient' | 'static' | 'mode') for a stage-scoped Results. Raises AttributeError on an unscoped instance (use results.stage(...) or results.modes[i] first).",
   "file": "src/apeGmsh/results/Results.py:317",
   "flow": [
    {
     "node": "results",
     "action": "_require_scoped() (AttributeError if _stage_id is None) returns the StageInfo, read .kind",
     "passes": "kind:str",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a scoped Results)",
   "outputs": "str stage kind",
   "reads": [
    "StageInfo.kind (via _require_scoped)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.mode_index",
   "label": "mode_index",
   "composite": "results",
   "signature": "property mode_index -> Optional[int]",
   "summary": "Mode number for a mode-scoped Results (may be None if the writer didn't tag it). Raises AttributeError unless the scoped stage has kind=='mode'.",
   "file": "src/apeGmsh/results/Results.py:352",
   "flow": [
    {
     "node": "results",
     "action": "_require_mode() returns StageInfo, read .mode_index",
     "passes": "mode_index:Optional[int]",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a mode-scoped Results)",
   "outputs": "Optional[int] mode number",
   "reads": [
    "StageInfo.mode_index (via _require_mode)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.modes",
   "label": "modes",
   "composite": "results",
   "signature": "property modes -> list[Results]",
   "summary": "Stages with kind=='mode' as a list of mode-scoped Results (one ._derive(stage_id=) per modal stage), in write order. Each element exposes .mode_index/.eigenvalue/.frequency_hz/.period_s plus the normal .nodes/.elements scoped reads. Sort by .mode_index for stable indexing.",
   "file": "src/apeGmsh/results/Results.py:300",
   "flow": [
    {
     "node": "results",
     "action": "filter _all_stages() to kind=='mode'",
     "passes": "list[StageInfo]",
     "to": "results"
    },
    {
     "node": "results",
     "action": "self._derive(stage_id=s.id) per modal stage",
     "passes": "list[Results] (mode-scoped)",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "list[Results] (each scoped to a kind='mode' stage)",
   "reads": [
    "ResultsReader.stages() (via _all_stages cache)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.n_steps",
   "label": "n_steps",
   "composite": "results",
   "signature": "property n_steps -> int",
   "summary": "Number of time steps in a stage-scoped Results' stage (1 for modes). Raises AttributeError when unscoped.",
   "file": "src/apeGmsh/results/Results.py:326",
   "flow": [
    {
     "node": "results",
     "action": "_require_scoped() returns the StageInfo, read .n_steps",
     "passes": "n_steps:int",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a scoped Results)",
   "outputs": "int step count",
   "reads": [
    "StageInfo.n_steps (via _require_scoped)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.name",
   "label": "name",
   "composite": "results",
   "signature": "property name -> str",
   "summary": "Stage name for a stage-scoped Results. Raises AttributeError when unscoped.",
   "file": "src/apeGmsh/results/Results.py:322",
   "flow": [
    {
     "node": "results",
     "action": "_require_scoped() returns the StageInfo, read .name",
     "passes": "name:str",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a scoped Results)",
   "outputs": "str stage name",
   "reads": [
    "StageInfo.name (via _require_scoped)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.nodes",
   "label": "nodes",
   "composite": "results.nodes",
   "signature": "attribute nodes: NodeResultsComposite",
   "summary": "Node-level result access composite (built in Results.__init__ / ._derive). Mirrors FEMData.nodes selection vocabulary: .get(), .select(), and the geometric selectors nearest_to/in_box/in_sphere/on_plane. All reads resolve the stage via the owning Results and selectors via the bound FEMData.",
   "file": "src/apeGmsh/results/Results.py:103",
   "flow": [
    {
     "node": "results",
     "action": "NodeResultsComposite(self) — holds back-ref _r to the owning Results",
     "passes": "NodeResultsComposite",
     "to": "results.nodes"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "NodeResultsComposite bound to this Results",
   "reads": [
    "src/apeGmsh/results/_composites.py:NodeResultsComposite"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.period_s",
   "label": "period_s",
   "composite": "results",
   "signature": "property period_s -> float",
   "summary": "Modal period in seconds for a mode-scoped Results. Raises AttributeError unless the scoped stage has kind=='mode'.",
   "file": "src/apeGmsh/results/Results.py:347",
   "flow": [
    {
     "node": "results",
     "action": "_require_mode() returns StageInfo, read .period_s",
     "passes": "period_s:float",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a mode-scoped Results)",
   "outputs": "float period (s)",
   "reads": [
    "StageInfo.period_s (via _require_mode)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.plot",
   "label": "plot",
   "composite": "results.plot",
   "signature": "property plot -> ResultsPlot",
   "summary": "Static matplotlib renderer (lazily constructed, memoised on _plot). Mirrors the interactive viewer's diagram catalog as headless figures — results.plot.contour(...), .deformed(...), .history(...). Requires the [plot] extra.",
   "file": "src/apeGmsh/results/Results.py:375",
   "flow": [
    {
     "node": "results",
     "action": "if _plot is None: import + construct ResultsPlot(self), cache on _plot",
     "passes": "ResultsPlot(results)",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "return the cached ResultsPlot bound to this Results",
     "passes": "ResultsPlot",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "ResultsPlot (matplotlib renderer)",
   "reads": [
    "src/apeGmsh/results/plot:ResultsPlot"
   ],
   "writes": [
    "Results._plot (memoised)"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.stage",
   "label": "stage",
   "composite": "results",
   "signature": "stage(name_or_id: str) -> Results",
   "summary": "Return a Results scoped to one stage, matched by StageInfo.id or .name (KeyError listing available names/ids on miss). The scoped instance shares the reader/stages-cache via ._derive but carries _stage_id, so subsequent .get() reads resolve to that stage without needing stage=. Scoped instances expose .kind/.name/.n_steps/.time.",
   "file": "src/apeGmsh/results/Results.py:295",
   "flow": [
    {
     "node": "results",
     "action": "_lookup_stage(name_or_id) scans _all_stages() for id==name_or_id or name==name_or_id (KeyError on miss)",
     "passes": "info:StageInfo",
     "to": "results"
    },
    {
     "node": "results",
     "action": "self._derive(stage_id=info.id) — clone with _stage_id set, fresh composites",
     "passes": "stage_id:str",
     "to": "results"
    }
   ],
   "inputs": "name_or_id (stage id or name)",
   "outputs": "Results (stage-scoped to info.id)",
   "reads": [
    "ResultsReader.stages() (via _all_stages cache)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.stages",
   "label": "stages",
   "composite": "results",
   "signature": "property stages -> list[StageInfo]",
   "summary": "All stages in the file (also available on scoped instances). Lists StageInfo records (id, name, kind in transient|static|mode, n_steps, plus eigenvalue/frequency_hz/period_s/mode_index for modes). Cached: the reader's stages() is called once and memoised on _stages_cache.",
   "file": "src/apeGmsh/results/Results.py:290",
   "flow": [
    {
     "node": "results",
     "action": "_all_stages() lazily calls reader.stages() once, caches on _stages_cache",
     "passes": "list[StageInfo]",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.stages() enumerates stages in write order",
     "passes": "list[StageInfo]",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "list[StageInfo]",
   "reads": [
    "ResultsReader.stages()"
   ],
   "writes": [
    "Results._stages_cache"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.time",
   "label": "time",
   "composite": "results.time",
   "signature": "property time -> ndarray",
   "summary": "The time vector (shape (n_steps,)) for a stage-scoped Results' stage, read fresh from the backing reader. Raises AttributeError when unscoped.",
   "file": "src/apeGmsh/results/Results.py:329",
   "flow": [
    {
     "node": "results",
     "action": "_require_scoped() yields the StageInfo for _stage_id",
     "passes": "info:StageInfo",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.time_vector(info.id) returns the step time array",
     "passes": "time:ndarray(n_steps,)",
     "to": "external"
    }
   ],
   "inputs": "(none; requires a scoped Results)",
   "outputs": "ndarray (n_steps,) time vector",
   "reads": [
    "ResultsReader.time_vector(stage_id)"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "Results.viewer",
   "label": "viewer",
   "composite": "viewer.results",
   "signature": "viewer(*, blocking=True, title=None, restore_session='prompt', save_session=True, cuts=None, model_h5=None) -> ResultsViewer | subprocess.Popen | None",
   "summary": "Open the post-solve results viewer. blocking=True constructs ResultsViewer(self, ...).show() in-process and blocks; blocking=False spawns `python -m apeGmsh.viewers <path>` (requires on-disk Results) and closes the parent reader so a capture rerun can recreate the .h5. Returns None if APEGMSH_SKIP_VIEWER is set. cuts= renders SectionCutDefs as Layers (requires model_h5=).",
   "file": "src/apeGmsh/results/Results.py:397",
   "flow": [
    {
     "node": "results",
     "action": "if APEGMSH_SKIP_VIEWER env set → return None",
     "passes": "(none)",
     "to": "external"
    },
    {
     "node": "results",
     "action": "blocking=False → _spawn_viewer_subprocess(title) launches `python -m apeGmsh.viewers <path>` then self.close()",
     "passes": "path:str, subprocess.Popen",
     "to": "viewer.results"
    },
    {
     "node": "results",
     "action": "blocking=True → import ResultsViewer",
     "passes": "self:Results, cuts, model_h5",
     "to": "viewer.results"
    },
    {
     "node": "viewer.results",
     "action": "ResultsViewer(self, title, restore_session, save_session, cuts, model_h5).show() blocks until window close",
     "passes": "ResultsViewer",
     "to": "external"
    }
   ],
   "inputs": "blocking, title, restore_session, save_session, cuts (SectionCutDefs), model_h5",
   "outputs": "ResultsViewer (blocking) | subprocess.Popen (non-blocking) | None (skip env)",
   "reads": [
    "src/apeGmsh/viewers/results_viewer.py:ResultsViewer",
    "Results._path"
   ],
   "writes": [
    "<results>.viewer-session.json on close (if save_session)",
    "closes parent reader on non-blocking spawn"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "Selection.difference",
   "label": "difference",
   "composite": "model.queries",
   "signature": "Selection.difference(other) -> Selection  # also a - b",
   "summary": "Set difference of two Selections (self minus other).",
   "file": "src/apeGmsh/core/_selection.py:674",
   "flow": [
    {
     "node": "selection",
     "action": "__sub__: other_set=set(other); keep dt in self if not in other_set",
     "passes": "difference [(dim,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(result, _queries=self._queries)",
     "passes": "Selection",
     "to": "selection"
    }
   ],
   "inputs": "other: another Selection",
   "outputs": "a new Selection = self − other; operator form a - b",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.intersect",
   "label": "intersect",
   "composite": "model.queries",
   "signature": "Selection.intersect(other) -> Selection  # also a & b",
   "summary": "Intersection of two Selections.",
   "file": "src/apeGmsh/core/_selection.py:670",
   "flow": [
    {
     "node": "selection",
     "action": "__and__: other_set=set(other); keep dt in self if in other_set",
     "passes": "intersected [(dim,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(result, _queries=self._queries)",
     "passes": "Selection",
     "to": "selection"
    }
   ],
   "inputs": "other: another Selection",
   "outputs": "a new Selection = self ∩ other; operator form a & b",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.normal_along",
   "label": "normal_along",
   "composite": "model.queries",
   "signature": "Selection.normal_along(direction, *, angle_tol=1.0) -> Selection",
   "summary": "Keep surfaces (dim=2) whose face normal is along a direction.",
   "file": "src/apeGmsh/core/_selection.py:579",
   "flow": [
    {
     "node": "selection",
     "action": "_require_dim(self,2); _parse_direction(direction)",
     "passes": "target unit vec",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_face_normal per surface (3 boundary pts cross product); keep |n·target|>=cos(tol)",
     "passes": "filtered [(2,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "new Selection(kept, _queries=self._queries)",
     "passes": "Selection",
     "to": "selection"
    }
   ],
   "inputs": "direction: 'x'|'y'|'z' or 3-vector (anti-parallel counts); angle_tol degrees; Selection must be all dim=2 (else ValueError)",
   "outputs": "a new Selection of matching surfaces (exact for flat faces, approx for curved)",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.parallel_to",
   "label": "parallel_to",
   "composite": "model.queries",
   "signature": "Selection.parallel_to(direction, *, angle_tol=1.0) -> Selection",
   "summary": "Keep curves (dim=1) whose endpoint chord is parallel to a direction.",
   "file": "src/apeGmsh/core/_selection.py:533",
   "flow": [
    {
     "node": "selection",
     "action": "_require_dim(self,1); _parse_direction(direction) -> unit vec",
     "passes": "target unit vec",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_chord_direction per curve (getBoundary endpoints + getValue); keep |chord·target|>=cos(tol)",
     "passes": "filtered [(1,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "new Selection(kept, _queries=self._queries)",
     "passes": "Selection",
     "to": "selection"
    }
   ],
   "inputs": "direction: 'x'|'y'|'z' or 3-vector (anti-parallel counts); angle_tol degrees; Selection must be all dim=1 (else ValueError)",
   "outputs": "a new Selection of matching curves",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.partition_by",
   "label": "partition_by",
   "composite": "model.queries",
   "signature": "Selection.partition_by(axis=None) -> dict[str,Selection] | Selection",
   "summary": "Group entities by their dominant bounding-box axis (x/y/z).",
   "file": "src/apeGmsh/core/_selection.py:680",
   "flow": [
    {
     "node": "selection",
     "action": "validate axis; per entity getBoundingBox spans; curve/vol->argmax, surface->argmin extent",
     "passes": "groups{'x','y','z'}",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getBoundingBox per (dim,tag)",
     "passes": "per-axis grouping",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "axis given -> single Selection else dict of 3 Selections",
     "passes": "dict|Selection",
     "to": "selection"
    }
   ],
   "inputs": "axis: None (returns dict keyed x/y/z) or 'x'|'y'|'z' (single Selection); curves grouped by largest extent, surfaces by smallest (≈normal)",
   "outputs": "dict[str,Selection] or one Selection, bound to the same queries engine",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.select",
   "label": "select",
   "composite": "model.queries",
   "signature": "Selection.select(*, on=None, crossing=None, not_on=None, not_crossing=None, tol=1e-6) -> Selection",
   "summary": "Filter this Selection further by another geometric predicate.",
   "file": "src/apeGmsh/core/_selection.py:443",
   "flow": [
    {
     "node": "selection",
     "action": "_select_impl(self, on/crossing/...) — exactly one predicate required",
     "passes": "self dimtags + primitive",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getBoundingBox per dimtag; signed-distance on/crossing test",
     "passes": "filtered [(dim,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "new Selection(result, _queries=self._queries)",
     "passes": "Selection",
     "to": "selection"
    }
   ],
   "inputs": "exactly one of on/crossing/not_on/not_crossing (primitive: {'z':0}|2pts|3pts|Plane|Line); tol",
   "outputs": "a new narrowed Selection bound to the same queries engine",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.tags",
   "label": "tags",
   "composite": "model.queries",
   "signature": "Selection.tags() -> list[int]",
   "summary": "Return bare integer tags (drops the dim component).",
   "file": "src/apeGmsh/core/_selection.py:450",
   "flow": [
    {
     "node": "selection",
     "action": "[t for _,t in self]",
     "passes": "list[int]",
     "to": "selection"
    }
   ],
   "inputs": "nothing",
   "outputs": "list[int] of tags (dim dropped) — feed into transfinite/sizing calls",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.to_label",
   "label": "to_label",
   "composite": "model.queries",
   "signature": "Selection.to_label(name) -> Selection",
   "summary": "Register every entity in the selection as a Tier-1 label (grouped by dim).",
   "file": "src/apeGmsh/core/_selection.py:454",
   "flow": [
    {
     "node": "selection",
     "action": "require _queries bound; session=_queries._model._parent; group by dim",
     "passes": "name + per-dim tags",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "session.labels.add(d, tags, name=name) per dim (multi-dim warning suppressed)",
     "passes": "label PG(s)",
     "to": "physical"
    }
   ],
   "inputs": "name: label name; Selection must be bound to the queries engine (built via queries.select/select_all_*)",
   "outputs": "self (Selection) for chaining; entities registered as a Tier-1 label PG per dim",
   "reads": [
    "selection"
   ],
   "writes": [
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.to_physical",
   "label": "to_physical",
   "composite": "model.queries",
   "signature": "Selection.to_physical(name) -> Selection",
   "summary": "Register every entity in the selection as a Tier-2 user physical group (grouped by dim).",
   "file": "src/apeGmsh/core/_selection.py:497",
   "flow": [
    {
     "node": "selection",
     "action": "require _queries bound; session=_queries._model._parent; group by dim",
     "passes": "name + per-dim tags",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "session.physical.add(d, tags, name=name) per dim",
     "passes": "physical group(s)",
     "to": "physical"
    }
   ],
   "inputs": "name: physical-group name; Selection must be bound to the queries engine",
   "outputs": "self (Selection) for chaining; entities registered as a user (Tier-2) physical group per dim",
   "reads": [
    "selection"
   ],
   "writes": [
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "Selection.union",
   "label": "union",
   "composite": "model.queries",
   "signature": "Selection.union(other) -> Selection  # also a | b",
   "summary": "Union of two Selections with insertion-order dedup.",
   "file": "src/apeGmsh/core/_selection.py:666",
   "flow": [
    {
     "node": "selection",
     "action": "__or__: seen=set(self); merged=list(self)+[dt in other not seen]",
     "passes": "merged [(dim,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(merged, _queries=self._queries)",
     "passes": "Selection",
     "to": "selection"
    }
   ],
   "inputs": "other: another Selection",
   "outputs": "a new Selection = self ∪ other (dedup, self order first); operator form a | b",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "Spherical",
   "label": "Spherical",
   "composite": "os.transform",
   "signature": "Spherical(origin=(0,0,0))",
   "summary": "Spherical orientation triad about a fixed origin (polar axis +Z) passed as orientation= to a geomTransf.",
   "file": "src/apeGmsh/opensees/_orientation.py:205",
   "flow": [
    {
     "node": "types",
     "action": "store origin; triad_at computes meridian/parallel/radial per point",
     "passes": "Spherical orientation field",
     "to": "os.transform"
    },
    {
     "node": "os.emit",
     "action": "emit_transform_specs calls triad_at(element-point) to derive per-element vecxz",
     "passes": "(e_theta,e_phi,e_r) triad",
     "to": "os.emit"
    }
   ],
   "inputs": "origin:3-vector",
   "outputs": "Spherical orientation (triad_at(p))",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "SpringSlab",
   "label": "SpringSlab",
   "composite": "results.slabs",
   "signature": "@dataclass(frozen=True) SpringSlab(component: str, values: ndarray (T,E), element_index: ndarray (E,), time: ndarray (T,))",
   "summary": "Immutable ZeroLength spring slab returned by results.elements.springs.get. One column per element for a single spring direction encoded in component (e.g. 'spring_force_0'); element_index carries the raw OpenSees element tag. Not in results.__all__ but is the public return type of springs.get.",
   "file": "src/apeGmsh/results/_slabs.py:119",
   "flow": [
    {
     "node": "results.slabs",
     "action": "constructed by ResultsReader.read_springs",
     "passes": "SpringSlab",
     "to": "external"
    }
   ],
   "inputs": "(constructed by the reader)",
   "outputs": "frozen SpringSlab dataclass (.component/.values/.element_index/.time)",
   "reads": [],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "apeGmsh.cuts.DriftDef",
   "label": "DriftDef",
   "composite": "cuts.drift",
   "signature": "DriftDef(top_node, bottom_node, direction=None, story_height=None, label=None); .from_node_pair(...); .from_pgs(top_pg=,bottom_pg=,fem=,...); .preflight(fem, tol=); .save_pickle/.load_pickle",
   "summary": "Frozen, picklable node-pair drift carrier (delta-u = u(top) - u(bottom), optionally axis-projected / story-height-normalized). from_pgs resolves each single-node PG through FEMData (raises on empty/multi-node PG).",
   "file": "src/apeGmsh/cuts/_drift.py:57",
   "flow": [
    {
     "node": "cuts.drift",
     "action": "DriftDef(...) / from_node_pair __post_init__ coerces node ids (must differ), unit-normalizes direction, validates story_height",
     "passes": "validated DriftDef (frozen dataclass)",
     "to": "cuts.drift"
    },
    {
     "node": "cuts.drift",
     "action": "from_pgs resolves top_pg/bottom_pg via _single_node_from_pg -> fem.nodes.get_ids(pg=) (exactly one node each, else ValueError)",
     "passes": "FEMData ref -> single FEM node ids",
     "to": "fem.nodes"
    },
    {
     "node": "cuts.drift",
     "action": "preflight(fem, tol=) looks up top/bottom node coords (fem.nodes.index/coords) -> PreflightReport with D-E1/D-E2/D-W1",
     "passes": "DriftDef + FEMData -> PreflightReport",
     "to": "cuts.drift"
    }
   ],
   "inputs": "top_node/bottom_node ids OR top_pg/bottom_pg + fem FEMData; direction, story_height, label, tol",
   "outputs": "frozen DriftDef; .preflight() -> PreflightReport; pickle file",
   "reads": [
    "FEMData fem.nodes.get_ids/index/coords"
   ],
   "writes": [
    "pickle file (save_pickle, optional)"
   ],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.cuts.DriftSweepDef",
   "label": "DriftSweepDef",
   "composite": "cuts.drift",
   "signature": "DriftSweepDef(drifts); .from_pg_pairs(pg_pairs=, fem=, direction=, story_height=); .preflight(fem, tol=); .elevations(axis='z', fem=); .save_pickle/.load_pickle",
   "summary": "Frozen sequence of DriftDef — the inter-story drift profile carrier. from_pg_pairs builds one DriftDef.from_pgs per (top_pg, bottom_pg) tuple; elevations(fem=) returns each drift's top-node coordinate along an axis for plotting drift-vs-elevation.",
   "file": "src/apeGmsh/cuts/_drift.py:284",
   "flow": [
    {
     "node": "cuts.drift",
     "action": "from_pg_pairs builds one DriftDef.from_pgs per (top_pg,bottom_pg) pair, sharing direction + story_height",
     "passes": "FEMData ref + PG pairs -> tuple[DriftDef,...]",
     "to": "cuts.drift"
    },
    {
     "node": "cuts.drift",
     "action": "preflight runs each drift.preflight(fem); elevations(axis, fem=) looks up each top-node coord via _try_node_coord (KeyError if absent)",
     "passes": "DriftSweepDef + FEMData -> tuple[PreflightReport] / ndarray elevations",
     "to": "fem.nodes"
    }
   ],
   "inputs": "drifts tuple; from_pg_pairs: pg_pairs Sequence[(str,str)], fem FEMData, direction, story_height; elevations axis, fem",
   "outputs": "frozen DriftSweepDef; .preflight() -> tuple[PreflightReport]; .elevations() -> ndarray; pickle file",
   "reads": [
    "FEMData fem.nodes (get_ids/index/coords)"
   ],
   "writes": [
    "pickle file (save_pickle, optional)"
   ],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.cuts.FemToOpsTagMap",
   "label": "FemToOpsTagMap",
   "composite": "cuts.stko",
   "signature": "FemToOpsTagMap.from_h5(path) -> FemToOpsTagMap; .ops_tag(fem_eid); .ops_tags_for_fem_eids(...); .fem_eids_for_ops_tags(...); .type_token_for(fem_eid)",
   "summary": "Read-only FEM-element-id <-> OpenSees-tag bridge built from a Phase-8.6+ model.h5. Consumed by SectionCutDef builders and preflight to translate physical-group element selections into OpenSees tags.",
   "file": "src/apeGmsh/cuts/_tag_map.py:42",
   "flow": [
    {
     "node": "cuts.stko",
     "action": "from_h5(path) opens model.h5 via opensees.emitter.h5_reader, walks element_meta groups, pairs ids/fem_eids, drops sentinels, detects collisions (ValueError)",
     "passes": "model.h5 path -> {fem_eid: ops_tag} + {fem_eid: type_token}",
     "to": "femdata.h5"
    },
    {
     "node": "cuts.stko",
     "action": "ops_tags_for_fem_eids / fem_eids_for_ops_tags / ops_tag / type_token_for perform exact lookups, raising KeyError listing all missing ids",
     "passes": "FEM eid tuple <-> OpenSees tag tuple",
     "to": "cuts"
    }
   ],
   "inputs": "model.h5 path (from_h5); fem_eids / ops_tags iterables for lookups",
   "outputs": "FemToOpsTagMap (in-memory bidirectional id map); lookups return int / tuple[int,...]",
   "reads": [
    "model.h5 /opensees/element_meta/{type}/{ids,fem_eids} via apeGmsh.opensees.emitter.h5_reader"
   ],
   "writes": [],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.cuts.SectionCutDef",
   "label": "SectionCutDef",
   "composite": "cuts",
   "signature": "SectionCutDef(plane_point, plane_normal, element_ids, side='positive', label=None, bounding_polygon=None); .from_plane_and_pg(...); .from_planar_pg(...); .preflight(fem,...); .to_spec(); .save_pickle/.load_pickle",
   "summary": "The apeGmsh-side frozen, picklable section-cut carrier. Builders from_plane_and_pg / from_planar_pg resolve a physical group to FEM eids via FEMData and map them to OpenSees tags via FemToOpsTagMap(model_h5). preflight() validates against a live FEMData; to_spec() lazily converts to STKO_to_python's SectionCutSpec.",
   "file": "src/apeGmsh/cuts/_defs.py:78",
   "flow": [
    {
     "node": "cuts",
     "action": "SectionCutDef(...) __post_init__ coerces/validates plane_point, plane_normal (unit-normalized), element_ids (non-empty int tuple), side, optional bounding_polygon",
     "passes": "validated SectionCutDef (frozen dataclass)",
     "to": "cuts"
    },
    {
     "node": "cuts",
     "action": "from_plane_and_pg resolves elements_pg via fem.elements.get_ids(pg=) then maps FEM eids -> OpenSees tags via FemToOpsTagMap.from_h5(model_h5)",
     "passes": "FEMData ref + model.h5 path -> ops element-tag tuple",
     "to": "cuts.stko"
    },
    {
     "node": "cuts",
     "action": "from_planar_pg fits the plane from plane_pg nodes via cuts.planes.plane_from_physical_surface, optionally derives a convex bounding polygon, then delegates to from_plane_and_pg",
     "passes": "FEMData ref -> (point,normal) plane + optional polygon",
     "to": "cuts.planes"
    },
    {
     "node": "cuts",
     "action": "preflight(fem, model_h5=|tag_map=, tol=) dispatches to _preflight.run_cut_checks -> structured PreflightReport (E1-E4, W1)",
     "passes": "SectionCutDef + FEMData -> PreflightReport",
     "to": "cuts"
    },
    {
     "node": "cuts",
     "action": "to_spec() lazy-imports STKO_to_python.cuts, rebuilds Plane, returns SectionCutSpec; save_pickle/load_pickle round-trip the def",
     "passes": "SectionCutDef -> STKO SectionCutSpec / pickle file",
     "to": "cuts.stko"
    }
   ],
   "inputs": "plane_point/plane_normal Vec3, element_ids ints; builders take plane=(pt,normal), elements_pg/plane_pg str, fem FEMData, model_h5 path, side, label, bounding_polygon/with_bounding, normal_hint, tol",
   "outputs": "frozen SectionCutDef; .to_spec() -> STKO SectionCutSpec; .preflight() -> PreflightReport; .save_pickle -> pickle Path",
   "reads": [
    "FEMData (fem.elements.get_ids, fem.nodes.get_coords)",
    "model.h5 /opensees/element_meta via FemToOpsTagMap",
    "STKO_to_python.cuts (lazy, to_spec only)"
   ],
   "writes": [
    "pickle file (save_pickle, optional)"
   ],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.cuts.SectionSweepDef",
   "label": "SectionSweepDef",
   "composite": "cuts.sweeps",
   "signature": "SectionSweepDef(cuts); .from_planes(...); .from_horizontal_grid(...); .from_pg_pattern(...); .from_pg_glob(...); .preflight(fem,...); .to_specs(); .plane_locators(axis=); .save_pickle/.load_pickle",
   "summary": "A frozen sequence of SectionCutDef sharing one element filter — the carrier for story-shear-vs-elevation profiles and depth scans. preflight runs each cut's checks (reusing one tag map); to_specs converts the whole sweep to STKO specs.",
   "file": "src/apeGmsh/cuts/_sweeps.py:41",
   "flow": [
    {
     "node": "cuts.sweeps",
     "action": "from_planes / from_horizontal_grid build one SectionCutDef.from_plane_and_pg per plane/elevation sharing elements_pg + side",
     "passes": "planes + FEMData + model.h5 -> tuple[SectionCutDef,...]",
     "to": "cuts"
    },
    {
     "node": "cuts.sweeps",
     "action": "from_pg_pattern / from_pg_glob resolve plane PGs (glob matched against fem.nodes.physical.names(dim=), natural-sorted) and build one SectionCutDef.from_planar_pg each",
     "passes": "FEMData ref + PG names -> tuple[SectionCutDef,...]",
     "to": "cuts"
    },
    {
     "node": "cuts.sweeps",
     "action": "preflight loads one FemToOpsTagMap and runs each cut.preflight; to_specs maps each cut.to_spec; plane_locators returns per-plane scalar coords",
     "passes": "SectionSweepDef -> tuple[PreflightReport] / list[SectionCutSpec] / ndarray locators",
     "to": "cuts.stko"
    }
   ],
   "inputs": "cuts tuple; builders: planes/elevations/plane_pgs/pattern, elements_pg, fem FEMData, model_h5, side, label_prefix, normal_hint, tol, with_bounding, dim",
   "outputs": "frozen SectionSweepDef; .to_specs() -> list[SectionCutSpec]; .preflight() -> tuple[PreflightReport]; .plane_locators() -> ndarray; pickle",
   "reads": [
    "FEMData (fem.elements/nodes, fem.nodes.physical.names)",
    "model.h5 via FemToOpsTagMap",
    "STKO_to_python.cuts (lazy, to_specs)"
   ],
   "writes": [
    "pickle file (save_pickle, optional)"
   ],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.cuts._planes",
   "label": "plane builders",
   "composite": "cuts.planes",
   "signature": "plane_horizontal(z); plane_vertical(axis=, at=); plane_from_three_points(p1,p2,p3,normal_hint=); plane_from_coords(coords,tol=,normal_hint=); plane_from_physical_surface(fem,pg_name,tol=,normal_hint=) -> (Vec3,Vec3)",
   "summary": "Plane-builder helpers returning (point, normal) tuples that splat straight into SectionCutDef. plane_from_physical_surface pulls a PG's node coords from FEMData and SVD-fits the plane.",
   "file": "src/apeGmsh/cuts/_planes.py:38",
   "flow": [
    {
     "node": "cuts.planes",
     "action": "plane_horizontal/plane_vertical return axis-aligned (point,normal); plane_from_three_points cross-products three points with optional normal_hint flip",
     "passes": "(Vec3 point, Vec3 unit-normal)",
     "to": "cuts.planes"
    },
    {
     "node": "cuts.planes",
     "action": "plane_from_coords SVD-fits an (N,3) array, enforces collinearity + coplanarity within tol, returns (centroid, smallest-singular-vector normal)",
     "passes": "ndarray (N,3) -> (point,normal)",
     "to": "cuts.planes"
    },
    {
     "node": "cuts.planes",
     "action": "plane_from_physical_surface reads fem.nodes.get_coords(pg=pg_name) then defers to plane_from_coords",
     "passes": "FEMData ref -> PG node coords -> (point,normal)",
     "to": "fem.nodes"
    }
   ],
   "inputs": "z / axis+at / three points / (N,3) coords / (fem FEMData, pg_name); tol, normal_hint",
   "outputs": "(point, normal) Vec3 tuple consumed by SectionCutDef.plane_point/plane_normal",
   "reads": [
    "FEMData fem.nodes.get_coords (plane_from_physical_surface only)"
   ],
   "writes": [],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.cuts.bounding_polygon_from_physical_surface",
   "label": "bounding_polygon",
   "composite": "cuts.polygons",
   "signature": "bounding_polygon_from_physical_surface(fem, pg_name, *, tol=1e-6, normal_hint=None) -> tuple[Vec3, ...]",
   "summary": "Derives a convex bounding polygon (CCW around the plane normal) from a planar PG's nodes: SVD-fit the plane, project nodes to 2D, take the scipy ConvexHull, re-embed hull vertices in 3D. Feeds SectionCutDef.bounding_polygon.",
   "file": "src/apeGmsh/cuts/_polygons.py:142",
   "flow": [
    {
     "node": "cuts.polygons",
     "action": "read fem.nodes.get_coords(pg=pg_name) (raises on empty PG)",
     "passes": "FEMData ref -> PG node coords ndarray",
     "to": "fem.nodes"
    },
    {
     "node": "cuts.polygons",
     "action": "plane_from_coords fits the plane; _plane_basis + _project_to_basis project nodes to 2-D plane coords",
     "passes": "(point,normal) + 2-D projected points",
     "to": "cuts.planes"
    },
    {
     "node": "cuts.polygons",
     "action": "_convex_hull_2d_ccw (scipy.spatial.ConvexHull, lazy) then _embed_from_basis re-embeds hull verts in 3-D",
     "passes": "2-D hull -> tuple[Vec3,...] on-plane CCW polygon",
     "to": "cuts.polygons"
    }
   ],
   "inputs": "fem FEMData, pg_name str, tol, normal_hint",
   "outputs": "tuple[Vec3,...] convex on-plane polygon for SectionCutDef.bounding_polygon",
   "reads": [
    "FEMData fem.nodes.get_coords",
    "cuts._planes.plane_from_coords",
    "scipy.spatial.ConvexHull (lazy)"
   ],
   "writes": [],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.cuts.persist_to_h5",
   "label": "persist_to_h5",
   "composite": "cuts.h5",
   "signature": "persist_to_h5(path, *, cuts=(), sweeps=()) -> None  # also write_cuts_into(f, cuts=, sweeps=)",
   "summary": "Append-writer that persists SectionCutDef/SectionSweepDef into an existing model.h5 under /opensees/cuts/ and /opensees/sweeps/, then bumps /meta/schema_version to 2.5.0 if lower. write_cuts_into is the lower-level primitive shared with apeSees.h5(cuts=).",
   "file": "src/apeGmsh/cuts/_h5_io.py:367",
   "flow": [
    {
     "node": "cuts.h5",
     "action": "persist_to_h5 no-ops on empty input; validates /meta schema-major via opensees.emitter.h5_reader.open (read-only)",
     "passes": "model.h5 path -> schema check",
     "to": "femdata.h5"
    },
    {
     "node": "cuts.h5",
     "action": "reopen h5py r+, delete existing /opensees/cuts and/or /opensees/sweeps for non-empty kwargs, call write_cuts_into",
     "passes": "SectionCutDef/SectionSweepDef -> h5 groups (/opensees/cuts/cut_N, /opensees/sweeps/sweep_N)",
     "to": "femdata.h5"
    },
    {
     "node": "cuts.h5",
     "action": "write_cuts_into serializes each cut (plane attrs + element_ids/bounding_polygon datasets) and sweep; _maybe_bump_schema_version raises 2.x to 2.5.0",
     "passes": "cut/sweep dataclasses -> h5 attrs+datasets; schema_version 2.5.0",
     "to": "femdata.h5"
    },
    {
     "node": "cuts.h5",
     "action": "read_cuts_and_sweeps reverses it — walks /opensees/cuts and /opensees/sweeps, reconstructing via the public constructors so __post_init__ validation re-runs",
     "passes": "model.h5 -> (tuple[SectionCutDef], tuple[SectionSweepDef])",
     "to": "cuts"
    }
   ],
   "inputs": "path to existing model.h5; cuts Sequence[SectionCutDef], sweeps Sequence[SectionSweepDef]; read_cuts_and_sweeps takes only path",
   "outputs": "None (in-place h5 mutation); read_cuts_and_sweeps -> (cuts, sweeps) tuples; schema_version bumped to 2.5.0",
   "reads": [
    "model.h5 /meta/schema_version via h5_reader.open",
    "h5py file in r+"
   ],
   "writes": [
    "model.h5 /opensees/cuts/, /opensees/sweeps/, /meta/schema_version (2.5.0)"
   ],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.sections.W_profile",
   "label": "W_profile (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.W_profile(bf, tf, h, tw, *, anchor='start', align='z', name='W_profile') -> Part",
   "summary": "Standalone factory: build a 2D W-shape cross-section surface only (no extrusion) in its own auto-persisted Part with label 'profile'; for fiber analysis or sweep.",
   "file": "src/apeGmsh/sections/profile.py:18",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, add_rectangle x3 -> boolean.cut (dim=2)",
     "passes": "outer,void_l,void_r -> I-surface",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "label surviving surface 'profile', apply_placement(length=None)",
     "passes": "surface tag -> 'profile' label PG",
     "to": "labels"
    },
    {
     "node": "sections",
     "action": "Part __exit__ auto-persists STEP + sidecar",
     "passes": "STEP + {name}.step.apegmsh.json",
     "to": "model.io"
    }
   ],
   "inputs": "bf, tf, h, tw; optional anchor (start/tuple only — length=None), align, name",
   "outputs": "Part (dim=2 single surface; label 'profile'; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities(2)"
   ],
   "writes": [
    "Part geometry add_rectangle/cut",
    "part.labels.add('profile')",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.W_shell",
   "label": "W_shell (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.W_shell(bf, tf, h, tw, length, *, anchor='start', align='z', name='W_shell') -> Part",
   "summary": "Standalone factory: build a W-shape as 3 mid-surface shell rectangles in its own auto-persisted Part with labels top_flange/bottom_flange/web.",
   "file": "src/apeGmsh/sections/shell.py:38",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, _build_rect_surface x3 (points/lines/loop/plane_surface, label=)",
     "passes": "3 quad surfaces + label PGs",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "part.model.sync + apply_placement(anchor,align,length), auto-persist on exit",
     "passes": "STEP + sidecar",
     "to": "part"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, name",
   "outputs": "Part (dim=2; labels top_flange/bottom_flange/web; auto-persisted)",
   "reads": [
    "gmsh model topology (add_plane_surface needs sync)"
   ],
   "writes": [
    "Part geometry add_point/line/curve_loop/plane_surface",
    "part.labels.add (via label=)",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.W_solid",
   "label": "W_solid (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.W_solid(bf, tf, h, tw, length, *, anchor='start', align='z', name='W_solid') -> Part",
   "summary": "Standalone factory: build a 7-volume hex-ready W-section in its own Part session (auto-persisted STEP+sidecar), labeled by structural role, ready for g.parts.add.",
   "file": "src/apeGmsh/sections/solid.py:28",
   "flow": [
    {
     "node": "sections",
     "action": "construct Part(name), open its isolated session",
     "passes": "name -> Part",
     "to": "part"
    },
    {
     "node": "sections",
     "action": "geometry.add_rectangle x3 -> boolean.cut -> transforms.extrude -> geo.slice x4",
     "passes": "I-profile -> 7 volumes",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "classify_w_volumes/end_faces/w_outer_faces/w_web_side_faces -> part.labels.add",
     "passes": "volume + face tags -> label PGs",
     "to": "labels"
    },
    {
     "node": "sections",
     "action": "apply_placement(anchor, align, length) single affine + PG snap/restore",
     "passes": "affine matrix",
     "to": "part"
    },
    {
     "node": "sections",
     "action": "Part __exit__ auto-persists STEP + collect_anchors sidecar",
     "passes": "STEP + {name}.step.apegmsh.json",
     "to": "model.io"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, name",
   "outputs": "Part (has_file=True after with-block; labels top_flange/bottom_flange/web/*_face/start_face/end_face travel via sidecar)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass / getAdjacencies (classify)"
   ],
   "writes": [
    "Part's gmsh geometry (add_rectangle/cut/extrude/slice)",
    "part.labels.add (label PGs)",
    "apply_placement (occ.affineTransform)",
    "STEP tempfile + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.angle_solid",
   "label": "angle_solid (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.angle_solid(b, h, t, length, *, anchor='start', align='z', name='angle_solid') -> Part",
   "summary": "Standalone factory: build an L-shape (two fused rectangles, extruded, sliced at corner) in its own auto-persisted Part labeled by leg/face.",
   "file": "src/apeGmsh/sections/solid.py:334",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, add_rectangle x2 -> boolean.fuse -> extrude -> slice x2",
     "passes": "h_leg,v_leg -> L-volumes",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "classify legs by COM + classify_end/angle_outer_faces + apply_placement, auto-persist",
     "passes": "leg+face tags, STEP+sidecar",
     "to": "part"
    }
   ],
   "inputs": "b, h, t, length; optional anchor, align, name",
   "outputs": "Part (labels horizontal_leg/vertical_leg/*_face/start_face/end_face; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass / getAdjacencies"
   ],
   "writes": [
    "Part geometry fuse/extrude/slice",
    "part.labels.add",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.channel_solid",
   "label": "channel_solid (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.channel_solid(bf, tf, h, tw, length, *, anchor='start', align='z', name='channel_solid') -> Part",
   "summary": "Standalone factory: build a C-shape (outer rect minus open-side void, extruded, sliced) in its own auto-persisted Part labeled by volume/face.",
   "file": "src/apeGmsh/sections/solid.py:419",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, add_rectangle x2 -> boolean.cut -> extrude -> slice x3",
     "passes": "outer,void -> C-volumes",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "classify_w_volumes/end_faces/w_outer_faces + apply_placement, auto-persist",
     "passes": "volume+face tags, STEP+sidecar",
     "to": "part"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, name",
   "outputs": "Part (labels top_flange/bottom_flange/web/*_face/start_face/end_face; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass"
   ],
   "writes": [
    "Part geometry cut/extrude/slice",
    "part.labels.add",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.pipe_hollow",
   "label": "pipe_hollow (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.pipe_hollow(r_outer, t, length, *, anchor='start', align='z', name='pipe_hollow') -> Part",
   "summary": "Standalone factory: build a hollow circular pipe (outer cylinder minus inner) in its own auto-persisted Part with label 'body' + end faces.",
   "file": "src/apeGmsh/sections/solid.py:282",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, add_cylinder outer/inner -> boolean.cut",
     "passes": "outer,inner -> cut",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "label 'body' + classify_end_faces + apply_placement, auto-persist",
     "passes": "body+face tags, STEP+sidecar",
     "to": "part"
    }
   ],
   "inputs": "r_outer, t, length; optional anchor, align, name",
   "outputs": "Part (labels body/start_face/end_face; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass"
   ],
   "writes": [
    "Part geometry add_cylinder/cut",
    "part.labels.add",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.pipe_solid",
   "label": "pipe_solid (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.pipe_solid(r, length, *, anchor='start', align='z', name='pipe_solid') -> Part",
   "summary": "Standalone factory: build a solid circular bar (add_cylinder) in its own auto-persisted Part with label 'body' + end faces.",
   "file": "src/apeGmsh/sections/solid.py:243",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, geometry.add_cylinder(label='body')",
     "passes": "cylinder -> 'body' label PG",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "classify_end_faces + apply_placement, auto-persist on exit",
     "passes": "face tags, STEP+sidecar",
     "to": "part"
    }
   ],
   "inputs": "r, length; optional anchor, align, name",
   "outputs": "Part (labels body/start_face/end_face; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass"
   ],
   "writes": [
    "Part geometry add_cylinder",
    "part.labels.add",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.rect_hollow",
   "label": "rect_hollow (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.rect_hollow(b, h, t, length, *, anchor='start', align='z', name='rect_hollow') -> Part",
   "summary": "Standalone factory: build a hollow rectangular tube (outer box minus inner) in its own auto-persisted Part with label 'body' + end faces.",
   "file": "src/apeGmsh/sections/solid.py:186",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, add_box outer/inner -> boolean.cut",
     "passes": "outer,inner -> cut",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "label 'body' + classify_end_faces + apply_placement, auto-persist",
     "passes": "body+face tags, STEP+sidecar",
     "to": "part"
    }
   ],
   "inputs": "b, h, t, length; optional anchor, align, name",
   "outputs": "Part (labels body/start_face/end_face; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass"
   ],
   "writes": [
    "Part geometry add_box/cut",
    "part.labels.add",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.rect_solid",
   "label": "rect_solid (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.rect_solid(b, h, length, *, anchor='start', align='z', name='rect_solid') -> Part",
   "summary": "Standalone factory: build a solid rectangular bar (add_box) in its own auto-persisted Part with label 'body' + end faces.",
   "file": "src/apeGmsh/sections/solid.py:144",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, geometry.add_box(label='body')",
     "passes": "box -> 'body' label PG",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "classify_end_faces + apply_placement, auto-persist on exit",
     "passes": "face tags, STEP+sidecar",
     "to": "part"
    }
   ],
   "inputs": "b, h, length; optional anchor, align, name",
   "outputs": "Part (labels body/start_face/end_face; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass"
   ],
   "writes": [
    "Part geometry add_box",
    "part.labels.add",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.sections.tee_solid",
   "label": "tee_solid (module factory)",
   "composite": "sections",
   "signature": "apeGmsh.sections.tee_solid(bf, tf, h, tw, length, *, anchor='start', align='z', name='tee_solid') -> Part",
   "summary": "Standalone factory: build a T-shape (flange+stem fused, extruded, sliced) in its own auto-persisted Part labeled flange/stem/faces by COM.",
   "file": "src/apeGmsh/sections/solid.py:500",
   "flow": [
    {
     "node": "sections",
     "action": "Part(name) session, add_rectangle x2 -> boolean.fuse -> extrude -> slice x3",
     "passes": "flange,stem -> T-volumes",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "classify by COM + classify_end/tee_outer_faces + apply_placement, auto-persist",
     "passes": "flange/stem+face tags, STEP+sidecar",
     "to": "part"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, name",
   "outputs": "Part (labels flange/stem/*_face/start_face/end_face; auto-persisted)",
   "reads": [
    "gmsh.model.getEntities / occ.getCenterOfMass"
   ],
   "writes": [
    "Part geometry fuse/extrude/slice",
    "part.labels.add",
    "apply_placement",
    "STEP + sidecar on __exit__"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "apeGmsh.viewers.GeomTransfViewer",
   "label": "GeomTransfViewer",
   "composite": "viewer.geomtransf",
   "signature": "GeomTransfViewer(title='OpenSees geomTransf viewer'); .show(node_i=, node_j=, vecxz=, beams=) -> None",
   "summary": "Browser-based 3D viewer for OpenSees geomTransf (local-axes) visualization. .show() normalizes single-beam or multi-beam args, generates a self-contained Three.js HTML page computing the OpenSees local frame, spins up a localhost HTTP server, opens the browser, and blocks until the tab fires a /shutdown beacon.",
   "file": "src/apeGmsh/viewers/geom_transf_viewer.py:56",
   "flow": [
    {
     "node": "viewer.geomtransf",
     "action": "GeomTransfViewer(title); .show() normalizes args via _build_beam_list into [{node_i,node_j,vecxz}] (raises ValueError if neither single-beam nor beams given)",
     "passes": "list[dict] beam specs",
     "to": "viewer.geomtransf"
    },
    {
     "node": "viewer.geomtransf",
     "action": "_build_html embeds the beams JSON into a Three.js page that computes the local frame (ex/ey/ez) from vecxz with the OpenSees convention",
     "passes": "HTML/JS string",
     "to": "viewer"
    },
    {
     "node": "viewer.geomtransf",
     "action": "start ephemeral HTTPServer on 127.0.0.1:0, webbrowser.open the page, block on shutdown_event until the tab POSTs /shutdown, then shut the server down",
     "passes": "rendered geomTransf page over localhost HTTP",
     "to": "external"
    }
   ],
   "inputs": "title (constructor); show(node_i=[x,y,z], node_j=[x,y,z], vecxz=[vx,vy,vz], beams=[{node_i,node_j,vecxz},...])",
   "outputs": "None; opens a browser tab, blocks until closed (no file/session side effects)",
   "reads": [
    "caller-supplied node/vecxz geometry only"
   ],
   "writes": [],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeGmsh.viewers.export_animation",
   "label": "export_animation",
   "composite": "viewer.animation",
   "signature": "export_animation(plotter, director: ResultsDirector, path: str|Path, *, fps=30, step_stride=1) -> Path",
   "summary": "Programmatic animation export: drives a ResultsDirector's time scrubber across the step schedule, captures each plotter frame, and encodes an MP4 (imageio-ffmpeg) or GIF (Pillow) chosen by the path suffix. Used by ResultsViewer.export_animation and headless scripts.",
   "file": "src/apeGmsh/viewers/animation.py:33",
   "flow": [
    {
     "node": "viewer.animation",
     "action": "validate plotter/director non-None and director.n_steps > 0; validate suffix in {.mp4,.gif}, fps>0; build step index schedule (stride + forced last step)",
     "passes": "list[int] step indices, writer kwargs dict",
     "to": "viewer.animation"
    },
    {
     "node": "viewer.animation",
     "action": "for each step index call director.set_step(i) (runs the full diagram-update + render pipeline), force plotter.render(), capture plotter.screenshot(return_img=True)",
     "passes": "step index -> ndarray RGB frame",
     "to": "viewer"
    },
    {
     "node": "viewer.animation",
     "action": "imageio.get_writer(path, **writer_kwargs).append_data(frame) per frame; finally director.set_step(saved_step) to restore the user's view",
     "passes": "frames -> encoded .mp4/.gif file at out_path",
     "to": "external"
    }
   ],
   "inputs": "plotter (built pv.Plotter), director (ResultsDirector with n_steps/step_index/set_step), path (.mp4 or .gif), fps, step_stride",
   "outputs": "resolved output Path; encoded MP4/GIF written to disk; director step restored",
   "reads": [
    "director.n_steps / director.step_index",
    "plotter scene (screenshot)"
   ],
   "writes": [
    "MP4/GIF file at path (parent dirs created)"
   ],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "apeSees",
   "label": "apeSees",
   "composite": "apesees",
   "signature": "apeSees(fem, *, default_orientation=Cartesian() | None)",
   "summary": "Construct the OpenSees bridge against a FEMData snapshot; holds declared typed-primitive state.",
   "file": "src/apeGmsh/opensees/apesees.py:428",
   "flow": [
    {
     "node": "apesees",
     "action": "store FEMData snapshot read-only on self._fem",
     "passes": "FEMData snapshot",
     "to": "femdata"
    },
    {
     "node": "apesees",
     "action": "create per-family namespaces + TagAllocator + resolve default_orientation sentinel",
     "passes": "TagAllocator + namespace objects",
     "to": "os.registry"
    }
   ],
   "inputs": "fem: FEMData; default_orientation: Orientation|None|UNSET",
   "outputs": "apeSees instance with .uniaxialMaterial/.nDMaterial/.section/.geomTransf/.beamIntegration/.timeSeries/.pattern/.element/.recorder/.nodes/.constraints/.numberer/.system/.test/.algorithm/.integrator/.analysis namespaces",
   "reads": [
    "femdata"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.analysis.AMD",
   "label": "AMD",
   "composite": "os.analysis",
   "signature": "AMD() -> AMD",
   "summary": "Typed `numberer AMD` primitive — approximate-minimum-degree fill-reducing DOF permutation; singleton.",
   "file": "src/apeGmsh/opensees/analysis/numberer.py:78",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs AMD(); ops._register adds it",
     "passes": "<AMD:Numberer primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.numberer('AMD')",
     "passes": "<command:('AMD',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.numberer command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.ArcLength",
   "label": "ArcLength",
   "composite": "os.analysis",
   "signature": "ArcLength(*, s: float, alpha: float) -> ArcLength",
   "summary": "Typed `integrator ArcLength s alpha` — static arc-length continuation; s>0 validated.",
   "file": "src/apeGmsh/opensees/analysis/integrator.py:178",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs ArcLength(s=,alpha=); __post_init__ validates s>0; ops._register adds it",
     "passes": "<ArcLength:Integrator primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.integrator('ArcLength', s, alpha)",
     "passes": "<command:('ArcLength',float,float)>",
     "to": "os.emit"
    }
   ],
   "inputs": "s:float>0, alpha:float",
   "outputs": "none (emitter.integrator command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.BFGS",
   "label": "BFGS",
   "composite": "os.analysis",
   "signature": "BFGS(*, count: int|None=None) -> BFGS",
   "summary": "Typed `algorithm BFGS [count]` — quasi-Newton with BFGS rank-2 update; count caps stored updates.",
   "file": "src/apeGmsh/opensees/analysis/algorithm.py:227",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs BFGS(count=); __post_init__ validates count>=1; ops._register adds it",
     "passes": "<BFGS:SolutionAlgorithm primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.algorithm('BFGS'[, count])",
     "passes": "<command:('BFGS',[int])>",
     "to": "os.emit"
    }
   ],
   "inputs": "count: optional int>=1",
   "outputs": "none (emitter.algorithm command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.BandGeneral",
   "label": "BandGeneral",
   "composite": "os.analysis",
   "signature": "BandGeneral() -> BandGeneral",
   "summary": "Typed `system BandGeneral` primitive — banded general (non-symmetric) linear-system solver; singleton.",
   "file": "src/apeGmsh/opensees/analysis/system.py:50",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs BandGeneral(); ops._register adds it",
     "passes": "<BandGeneral:LinearSystem primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.system('BandGeneral')",
     "passes": "<command:('BandGeneral',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.system command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.BandSPD",
   "label": "BandSPD",
   "composite": "os.analysis",
   "signature": "BandSPD() -> BandSPD",
   "summary": "Typed `system BandSPD` primitive — banded symmetric-positive-definite solver; singleton.",
   "file": "src/apeGmsh/opensees/analysis/system.py:66",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs BandSPD(); ops._register adds it",
     "passes": "<BandSPD:LinearSystem primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.system('BandSPD')",
     "passes": "<command:('BandSPD',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.system command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Broyden",
   "label": "Broyden",
   "composite": "os.analysis",
   "signature": "Broyden(*, count: int|None=None) -> Broyden",
   "summary": "Typed `algorithm Broyden [count]` — quasi-Newton with Broyden rank-1 update; count caps stored updates.",
   "file": "src/apeGmsh/opensees/analysis/algorithm.py:257",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Broyden(count=); __post_init__ validates count>=1; ops._register adds it",
     "passes": "<Broyden:SolutionAlgorithm primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.algorithm('Broyden'[, count])",
     "passes": "<command:('Broyden',[int])>",
     "to": "os.emit"
    }
   ],
   "inputs": "count: optional int>=1",
   "outputs": "none (emitter.algorithm command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.CentralDifference",
   "label": "CentralDifference",
   "composite": "os.analysis",
   "signature": "CentralDifference() -> CentralDifference",
   "summary": "Typed `integrator CentralDifference` — explicit central-difference transient integrator; no parameters.",
   "file": "src/apeGmsh/opensees/analysis/integrator.py:268",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs CentralDifference(); ops._register adds it",
     "passes": "<CentralDifference:Integrator primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.integrator('CentralDifference')",
     "passes": "<command:('CentralDifference',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.integrator command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.DisplacementControl",
   "label": "DisplacementControl",
   "composite": "os.analysis",
   "signature": "DisplacementControl(*, node: int, dof: int, dU: float, num_iter: int|None=None, min_dU: float|None=None, max_dU: float|None=None) -> DisplacementControl",
   "summary": "Typed `integrator DisplacementControl node dof dU [numIter [mindU maxdU]]` — static displacement-control at one DOF.",
   "file": "src/apeGmsh/opensees/analysis/integrator.py:112",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs DisplacementControl(node=,dof=,dU=); __post_init__ validates dof/bracket; ops._register adds it",
     "passes": "<DisplacementControl:Integrator primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; assembles args (node,dof,dU,...); emits emitter.integrator('DisplacementControl', ...)",
     "passes": "<command:('DisplacementControl',int,int,float,...)>",
     "to": "os.emit"
    }
   ],
   "inputs": "node:int (OpenSees node tag), dof:int>=1, dU:float, optional num_iter + min_dU/max_dU",
   "outputs": "none (emitter.integrator command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.EnergyIncr",
   "label": "EnergyIncr",
   "composite": "os.analysis",
   "signature": "EnergyIncr(*, tol: float, max_iter: int, print_flag: int=0, norm_type: int=2) -> EnergyIncr",
   "summary": "Typed `test EnergyIncr tol maxIter [pFlag normType]` convergence test — converges on incremental energy (du·dF) norm.",
   "file": "src/apeGmsh/opensees/analysis/test.py:117",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs EnergyIncr(tol=,max_iter=); __post_init__ validates; ops._register adds it",
     "passes": "<EnergyIncr:ConvergenceTest primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.test('EnergyIncr', tol, max_iter, print_flag, norm_type)",
     "passes": "<command:('EnergyIncr',float,int,int,int)>",
     "to": "os.emit"
    }
   ],
   "inputs": "tol:float>0, max_iter:int>=1, print_flag:int, norm_type:int",
   "outputs": "none (emitter.test command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.ExplicitDifference",
   "label": "ExplicitDifference",
   "composite": "os.analysis",
   "signature": "ExplicitDifference() -> ExplicitDifference",
   "summary": "Typed `integrator ExplicitDifference` — explicit difference-scheme transient integrator; no parameters.",
   "file": "src/apeGmsh/opensees/analysis/integrator.py:284",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs ExplicitDifference(); ops._register adds it",
     "passes": "<ExplicitDifference:Integrator primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.integrator('ExplicitDifference')",
     "passes": "<command:('ExplicitDifference',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.integrator command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.FixedNumIter",
   "label": "FixedNumIter",
   "composite": "os.analysis",
   "signature": "FixedNumIter(*, max_iter: int, print_flag: int=0, norm_type: int=2) -> FixedNumIter",
   "summary": "Typed `test FixedNumIter maxIter [pFlag normType]` convergence test — runs a fixed iteration count every step regardless of residual.",
   "file": "src/apeGmsh/opensees/analysis/test.py:151",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs FixedNumIter(max_iter=); __post_init__ validates max_iter>=1; ops._register adds it",
     "passes": "<FixedNumIter:ConvergenceTest primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.test('FixedNumIter', max_iter, print_flag, norm_type)",
     "passes": "<command:('FixedNumIter',int,int,int)>",
     "to": "os.emit"
    }
   ],
   "inputs": "max_iter:int>=1, print_flag:int, norm_type:int",
   "outputs": "none (emitter.test command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.FullGeneral",
   "label": "FullGeneral",
   "composite": "os.analysis",
   "signature": "FullGeneral() -> FullGeneral",
   "summary": "Typed `system FullGeneral` primitive — dense general solver for small systems; singleton.",
   "file": "src/apeGmsh/opensees/analysis/system.py:146",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs FullGeneral(); ops._register adds it",
     "passes": "<FullGeneral:LinearSystem primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.system('FullGeneral')",
     "passes": "<command:('FullGeneral',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.system command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.HHT",
   "label": "HHT",
   "composite": "os.analysis",
   "signature": "HHT(*, alpha: float, gamma: float|None=None, beta: float|None=None) -> HHT",
   "summary": "Typed `integrator HHT alpha [gamma beta]` — Hilber-Hughes-Taylor alpha-method; gamma/beta both-or-neither override defaults.",
   "file": "src/apeGmsh/opensees/analysis/integrator.py:232",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs HHT(alpha=); __post_init__ enforces both-or-neither gamma/beta; ops._register adds it",
     "passes": "<HHT:Integrator primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.integrator('HHT', alpha[, gamma, beta])",
     "passes": "<command:('HHT',float,[float,float])>",
     "to": "os.emit"
    }
   ],
   "inputs": "alpha:float, optional gamma/beta pair",
   "outputs": "none (emitter.integrator command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.KrylovNewton",
   "label": "KrylovNewton",
   "composite": "os.analysis",
   "signature": "KrylovNewton(*, iterate: 'current'|'initial'|'noTangent'|None=None, increment: ...|None=None, max_dim: int|None=None) -> KrylovNewton",
   "summary": "Typed `algorithm KrylovNewton [-iterate][-increment][-maxDim]` — Newton with Krylov-subspace acceleration.",
   "file": "src/apeGmsh/opensees/analysis/algorithm.py:189",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs KrylovNewton(...); __post_init__ validates max_dim>=1; ops._register adds it",
     "passes": "<KrylovNewton:SolutionAlgorithm primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; assembles optional-flag args; emits emitter.algorithm('KrylovNewton', ...)",
     "passes": "<command:('KrylovNewton',...)>",
     "to": "os.emit"
    }
   ],
   "inputs": "iterate/increment: current|initial|noTangent; max_dim:int>=1",
   "outputs": "none (emitter.algorithm command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Lagrange",
   "label": "Lagrange",
   "composite": "os.analysis",
   "signature": "Lagrange(*, alpha_sp: float|None=None, alpha_mp: float|None=None) -> Lagrange",
   "summary": "Typed `constraints Lagrange [alphaSP alphaMP]` primitive — Lagrange-multiplier handler; both alphas optional but must be supplied together.",
   "file": "src/apeGmsh/opensees/analysis/constraint_handler.py:112",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Lagrange(...); __post_init__ enforces both-or-neither; ops._register adds it",
     "passes": "<Lagrange:ConstraintHandler primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.constraints('Lagrange'[, alpha_sp, alpha_mp])",
     "passes": "<command:('Lagrange',[float,float])>",
     "to": "os.emit"
    }
   ],
   "inputs": "alpha_sp/alpha_mp: optional float pair",
   "outputs": "none (emitter.constraints command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Linear",
   "label": "Linear (algorithm)",
   "composite": "os.analysis",
   "signature": "Linear() -> Linear",
   "summary": "Typed `algorithm Linear` solution algorithm — one solve per step, no iteration; singleton.",
   "file": "src/apeGmsh/opensees/analysis/algorithm.py:69",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Linear(); ops._register adds it",
     "passes": "<Linear:SolutionAlgorithm primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; tag discarded; emits emitter.algorithm('Linear')",
     "passes": "<command:('Linear',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.algorithm command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.LoadControl",
   "label": "LoadControl",
   "composite": "os.analysis",
   "signature": "LoadControl(*, dlam: float, num_iter: int|None=None, min_lam: float|None=None, max_lam: float|None=None) -> LoadControl",
   "summary": "Typed `integrator LoadControl dlam [numIter [minLam maxLam]]` — static load-factor-increment integrator; min/max require num_iter.",
   "file": "src/apeGmsh/opensees/analysis/integrator.py:54",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs LoadControl(dlam=); __post_init__ validates bracket coherence; ops._register adds it",
     "passes": "<LoadControl:Integrator primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; assembles args; emits emitter.integrator('LoadControl', dlam, [num_iter,[min_lam,max_lam]])",
     "passes": "<command:('LoadControl',float,...)>",
     "to": "os.emit"
    }
   ],
   "inputs": "dlam:float, optional num_iter:int>=1 + min_lam/max_lam pair",
   "outputs": "none (emitter.integrator command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.ModifiedNewton",
   "label": "ModifiedNewton",
   "composite": "os.analysis",
   "signature": "ModifiedNewton(*, tangent: 'tangent'|'secant'|'initial'='tangent') -> ModifiedNewton",
   "summary": "Typed `algorithm ModifiedNewton [-secant|-initial]` — forms one tangent per step and reuses it across iterations.",
   "file": "src/apeGmsh/opensees/analysis/algorithm.py:114",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs ModifiedNewton(tangent=); ops._register adds it",
     "passes": "<ModifiedNewton:SolutionAlgorithm primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.algorithm('ModifiedNewton'[, '-secant'|'-initial'])",
     "passes": "<command:('ModifiedNewton',[flag])>",
     "to": "os.emit"
    }
   ],
   "inputs": "tangent: 'tangent'|'secant'|'initial'",
   "outputs": "none (emitter.algorithm command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Mumps",
   "label": "Mumps",
   "composite": "os.analysis",
   "signature": "Mumps() -> Mumps",
   "summary": "Typed `system Mumps` primitive — multifrontal massively-parallel sparse direct solver; singleton.",
   "file": "src/apeGmsh/opensees/analysis/system.py:114",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Mumps(); ops._register adds it",
     "passes": "<Mumps:LinearSystem primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.system('Mumps')",
     "passes": "<command:('Mumps',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.system command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Newmark",
   "label": "Newmark",
   "composite": "os.analysis",
   "signature": "Newmark(*, gamma: float, beta: float) -> Newmark",
   "summary": "Typed `integrator Newmark gamma beta` — classical Newmark implicit transient integrator.",
   "file": "src/apeGmsh/opensees/analysis/integrator.py:207",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Newmark(gamma=,beta=); ops._register adds it",
     "passes": "<Newmark:Integrator primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.integrator('Newmark', gamma, beta)",
     "passes": "<command:('Newmark',float,float)>",
     "to": "os.emit"
    }
   ],
   "inputs": "gamma:float, beta:float",
   "outputs": "none (emitter.integrator command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Newton",
   "label": "Newton",
   "composite": "os.analysis",
   "signature": "Newton(*, tangent: 'tangent'|'secant'|'initial'='tangent') -> Newton",
   "summary": "Typed `algorithm Newton [-secant|-initial]` solution algorithm — full Newton-Raphson; tangent enum picks stiffness flavor.",
   "file": "src/apeGmsh/opensees/analysis/algorithm.py:85",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Newton(tangent=); ops._register adds it",
     "passes": "<Newton:SolutionAlgorithm primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.algorithm('Newton'[, '-secant'|'-initial'])",
     "passes": "<command:('Newton',[flag])>",
     "to": "os.emit"
    }
   ],
   "inputs": "tangent: 'tangent'|'secant'|'initial'",
   "outputs": "none (emitter.algorithm command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.NewtonLineSearch",
   "label": "NewtonLineSearch",
   "composite": "os.analysis",
   "signature": "NewtonLineSearch(*, line_search: LineSearchType, tol: float|None=None, max_iter: int|None=None, min_eta: float|None=None, max_eta: float|None=None) -> NewtonLineSearch",
   "summary": "Typed `algorithm NewtonLineSearch -type T [-tol][-maxIter][-minEta][-maxEta]` — Newton with 1-D line search; line_search required.",
   "file": "src/apeGmsh/opensees/analysis/algorithm.py:142",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs NewtonLineSearch(line_search=); __post_init__ validates tol/max_iter; ops._register adds it",
     "passes": "<NewtonLineSearch:SolutionAlgorithm primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; assembles args list; emits emitter.algorithm('NewtonLineSearch', '-type', line_search, ...)",
     "passes": "<command:('NewtonLineSearch',str,...)>",
     "to": "os.emit"
    }
   ],
   "inputs": "line_search: Bisection|Secant|RegulaFalsi|InitialInterpolated; optional tol/max_iter/min_eta/max_eta",
   "outputs": "none (emitter.algorithm command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.NormDispIncr",
   "label": "NormDispIncr",
   "composite": "os.analysis",
   "signature": "NormDispIncr(*, tol: float, max_iter: int, print_flag: int=0, norm_type: int=2) -> NormDispIncr",
   "summary": "Typed `test NormDispIncr tol maxIter [pFlag normType]` convergence test — converges on displacement-increment norm; tol>0 and max_iter>=1 validated.",
   "file": "src/apeGmsh/opensees/analysis/test.py:49",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs NormDispIncr(tol=,max_iter=); __post_init__ validates; ops._register adds it",
     "passes": "<NormDispIncr:ConvergenceTest primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.test('NormDispIncr', tol, max_iter, print_flag, norm_type)",
     "passes": "<command:('NormDispIncr',float,int,int,int)>",
     "to": "os.emit"
    }
   ],
   "inputs": "tol:float>0, max_iter:int>=1, print_flag:int, norm_type:int",
   "outputs": "none (emitter.test command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.NormUnbalance",
   "label": "NormUnbalance",
   "composite": "os.analysis",
   "signature": "NormUnbalance(*, tol: float, max_iter: int, print_flag: int=0, norm_type: int=2) -> NormUnbalance",
   "summary": "Typed `test NormUnbalance tol maxIter [pFlag normType]` convergence test — converges on residual (out-of-balance) force norm.",
   "file": "src/apeGmsh/opensees/analysis/test.py:83",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs NormUnbalance(tol=,max_iter=); __post_init__ validates; ops._register adds it",
     "passes": "<NormUnbalance:ConvergenceTest primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.test('NormUnbalance', tol, max_iter, print_flag, norm_type)",
     "passes": "<command:('NormUnbalance',float,int,int,int)>",
     "to": "os.emit"
    }
   ],
   "inputs": "tol:float>0, max_iter:int>=1, print_flag:int, norm_type:int",
   "outputs": "none (emitter.test command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Penalty",
   "label": "Penalty",
   "composite": "os.analysis",
   "signature": "Penalty(*, alpha_sp: float, alpha_mp: float) -> Penalty",
   "summary": "Typed `constraints Penalty alphaSP alphaMP` primitive — penalty constraint handler with user-chosen weights (both required, validated > 0).",
   "file": "src/apeGmsh/opensees/analysis/constraint_handler.py:63",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Penalty(alpha_sp=,alpha_mp=); __post_init__ validates; ops._register adds to bridge",
     "passes": "<Penalty:ConstraintHandler primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "BuiltModel.emit pre-element pass calls _emit; emits emitter.constraints('Penalty', alpha_sp, alpha_mp)",
     "passes": "<command:('Penalty',float,float)>",
     "to": "os.emit"
    }
   ],
   "inputs": "alpha_sp:float>0, alpha_mp:float>0",
   "outputs": "none (emitter.constraints command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.PlainConstraints",
   "label": "Plain (constraints)",
   "composite": "os.analysis",
   "signature": "Plain() -> Plain",
   "summary": "Typed `constraints Plain` primitive — direct application of homogeneous SPs (default handler). Registered via ops.constraints.Plain() or standalone (P11); singleton, no tag rendered.",
   "file": "src/apeGmsh/opensees/analysis/constraint_handler.py:47",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Plain() and ops._register adds it; build() topo-sorts it into BuiltModel.primitives",
     "passes": "<Plain:ConstraintHandler primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit(emitter,tag) called in BuiltModel.emit pre-element pass; tag discarded (singleton)",
     "passes": "<command:('Plain',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none (flag-only handler)",
   "outputs": "none (mutates emitter via emitter.constraints('Plain'))",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.PlainNumberer",
   "label": "Plain (numberer)",
   "composite": "os.analysis",
   "signature": "Plain() -> Plain",
   "summary": "Typed `numberer Plain` primitive — number DOFs in node-add order; singleton, no tag.",
   "file": "src/apeGmsh/opensees/analysis/numberer.py:47",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Plain() (re-exported as PlainNumberer); ops._register adds it",
     "passes": "<Plain:Numberer primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.numberer('Plain')",
     "passes": "<command:('Plain',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.numberer command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.ProfileSPD",
   "label": "ProfileSPD",
   "composite": "os.analysis",
   "signature": "ProfileSPD() -> ProfileSPD",
   "summary": "Typed `system ProfileSPD` primitive — skyline symmetric-positive-definite solver; singleton.",
   "file": "src/apeGmsh/opensees/analysis/system.py:82",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs ProfileSPD(); ops._register adds it",
     "passes": "<ProfileSPD:LinearSystem primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.system('ProfileSPD')",
     "passes": "<command:('ProfileSPD',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.system command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.RCM",
   "label": "RCM",
   "composite": "os.analysis",
   "signature": "RCM() -> RCM",
   "summary": "Typed `numberer RCM` primitive — reverse Cuthill-McKee bandwidth-reducing DOF permutation; singleton.",
   "file": "src/apeGmsh/opensees/analysis/numberer.py:62",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs RCM(); ops._register adds it",
     "passes": "<RCM:Numberer primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.numberer('RCM')",
     "passes": "<command:('RCM',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.numberer command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.RelativeNormDispIncr",
   "label": "RelativeNormDispIncr",
   "composite": "os.analysis",
   "signature": "RelativeNormDispIncr(*, tol: float, max_iter: int, print_flag: int=0, norm_type: int=2) -> RelativeNormDispIncr",
   "summary": "Typed `test RelativeNormDispIncr tol maxIter [pFlag normType]` convergence test — converges when ||du_k||/||du_0|| < tol.",
   "file": "src/apeGmsh/opensees/analysis/test.py:185",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs RelativeNormDispIncr(tol=,max_iter=); __post_init__ validates; ops._register adds it",
     "passes": "<RelativeNormDispIncr:ConvergenceTest primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.test('RelativeNormDispIncr', tol, max_iter, print_flag, norm_type)",
     "passes": "<command:('RelativeNormDispIncr',float,int,int,int)>",
     "to": "os.emit"
    }
   ],
   "inputs": "tol:float>0, max_iter:int>=1, print_flag:int, norm_type:int",
   "outputs": "none (emitter.test command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.SparseGeneral",
   "label": "SparseGeneral",
   "composite": "os.analysis",
   "signature": "SparseGeneral() -> SparseGeneral",
   "summary": "Typed `system SparseGeneral` primitive — generic sparse general (SuperLU-backed) solver; singleton.",
   "file": "src/apeGmsh/opensees/analysis/system.py:130",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs SparseGeneral(); ops._register adds it",
     "passes": "<SparseGeneral:LinearSystem primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.system('SparseGeneral')",
     "passes": "<command:('SparseGeneral',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.system command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Static",
   "label": "Static",
   "composite": "os.analysis",
   "signature": "Static() -> Static",
   "summary": "Typed `analysis Static` directive — selects StaticAnalysis (pseudo-time); singleton, configures only which Analysis subclass OpenSees instantiates.",
   "file": "src/apeGmsh/opensees/analysis/analysis.py:47",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Static(); ops._register adds it",
     "passes": "<Static:Analysis primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.analysis('Static')",
     "passes": "<command:('Static',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.analysis command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Transformation",
   "label": "Transformation",
   "composite": "os.analysis",
   "signature": "Transformation() -> Transformation",
   "summary": "Typed `constraints Transformation` primitive — exact transformation constraint handler (no spurious modes); singleton.",
   "file": "src/apeGmsh/opensees/analysis/constraint_handler.py:96",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Transformation(); ops._register adds it",
     "passes": "<Transformation:ConstraintHandler primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in BuiltModel.emit pre-element pass; emits emitter.constraints('Transformation')",
     "passes": "<command:('Transformation',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.constraints command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.Transient",
   "label": "Transient",
   "composite": "os.analysis",
   "signature": "Transient() -> Transient",
   "summary": "Typed `analysis Transient` directive — selects fixed-step DirectIntegrationAnalysis; singleton.",
   "file": "src/apeGmsh/opensees/analysis/analysis.py:63",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Transient(); ops._register adds it",
     "passes": "<Transient:Analysis primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.analysis('Transient')",
     "passes": "<command:('Transient',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.analysis command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.UmfPack",
   "label": "UmfPack",
   "composite": "os.analysis",
   "signature": "UmfPack() -> UmfPack",
   "summary": "Typed `system UmfPack` primitive — direct sparse LU solver via UMFPACK; singleton.",
   "file": "src/apeGmsh/opensees/analysis/system.py:98",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs UmfPack(); ops._register adds it",
     "passes": "<UmfPack:LinearSystem primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.system('UmfPack')",
     "passes": "<command:('UmfPack',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.system command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analysis.VariableTransient",
   "label": "VariableTransient",
   "composite": "os.analysis",
   "signature": "VariableTransient() -> VariableTransient",
   "summary": "Typed `analysis VariableTransient` directive — selects adaptive-step transient analysis; singleton.",
   "file": "src/apeGmsh/opensees/analysis/analysis.py:79",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs VariableTransient(); ops._register adds it",
     "passes": "<VariableTransient:Analysis primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.analysis",
     "action": "_emit in pre-element pass; emits emitter.analysis('VariableTransient')",
     "passes": "<command:('VariableTransient',)>",
     "to": "os.emit"
    }
   ],
   "inputs": "none",
   "outputs": "none (emitter.analysis command)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.analyze",
   "label": "analyze",
   "composite": "apesees",
   "signature": "analyze(*, steps: int, dt: float|None=None) -> int",
   "summary": "Build, drive a LiveOpsEmitter end-to-end, then run the analysis chain; returns openseespy analyze code.",
   "file": "src/apeGmsh/opensees/apesees.py:608",
   "flow": [
    {
     "node": "apesees",
     "action": "check analysis chain complete, then build()",
     "passes": "BuiltModel",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit drives LiveOpsEmitter over all primitives",
     "passes": "Primitive tuple + tag_for + FEMData",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "LiveOpsEmitter.analyze(steps,dt) into openseespy",
     "passes": "steps:int, dt:float",
     "to": "external"
    }
   ],
   "inputs": "steps:int, dt:float|None",
   "outputs": "int (openseespy analyze return)",
   "reads": [
    "femdata",
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.HingeEndpoint",
   "label": "HingeEndpoint",
   "composite": "os.integration",
   "signature": "HingeEndpoint(*, section_i, lp_i, section_j, lp_j, section_interior) -> HingeEndpoint",
   "summary": "Concentrated-plasticity 1-point element-end hinge integration (beamIntegration HingeEndpoint); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:404",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (lp_i>0, lp_j>0)",
     "passes": "HingeEndpoint dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; sections declared via dependencies()",
     "passes": "beamIntegration tag + Section refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag each section → emitter.beamIntegration('HingeEndpoint',tag,secI,lpI,secJ,lpJ,secE)",
     "passes": "tag int + 3 section tags + lp lengths",
     "to": "external"
    }
   ],
   "inputs": "section_i/j/interior:Section, lp_i>0, lp_j>0",
   "outputs": "HingeEndpoint (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.HingeMidpoint",
   "label": "HingeMidpoint",
   "composite": "os.integration",
   "signature": "HingeMidpoint(*, section_i, lp_i, section_j, lp_j, section_interior) -> HingeMidpoint",
   "summary": "Concentrated-plasticity 1-point midpoint hinge integration (beamIntegration HingeMidpoint); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:374",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (lp_i>0, lp_j>0)",
     "passes": "HingeMidpoint dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; sections declared via dependencies()",
     "passes": "beamIntegration tag + Section refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag each section → emitter.beamIntegration('HingeMidpoint',tag,secI,lpI,secJ,lpJ,secE)",
     "passes": "tag int + 3 section tags + lp lengths",
     "to": "external"
    }
   ],
   "inputs": "section_i/j/interior:Section, lp_i>0, lp_j>0",
   "outputs": "HingeMidpoint (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.HingeRadau",
   "label": "HingeRadau",
   "composite": "os.integration",
   "signature": "HingeRadau(*, section_i, lp_i, section_j, lp_j, section_interior) -> HingeRadau",
   "summary": "Concentrated-plasticity 2-point Radau hinge integration with i/j/interior sections (beamIntegration HingeRadau); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:305",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (lp_i>0, lp_j>0)",
     "passes": "HingeRadau dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; unique i/j/interior sections declared via dependencies()",
     "passes": "beamIntegration tag + Section refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag each section → emitter.beamIntegration('HingeRadau',tag,secI,lpI,secJ,lpJ,secE)",
     "passes": "tag int + 3 section tags + lp lengths",
     "to": "external"
    }
   ],
   "inputs": "section_i/j/interior:Section, lp_i>0, lp_j>0",
   "outputs": "HingeRadau (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.HingeRadauTwo",
   "label": "HingeRadauTwo",
   "composite": "os.integration",
   "signature": "HingeRadauTwo(*, section_i, lp_i, section_j, lp_j, section_interior) -> HingeRadauTwo",
   "summary": "Concentrated-plasticity Radau-at-both-endpoints hinge integration (beamIntegration HingeRadauTwo); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:344",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (lp_i>0, lp_j>0)",
     "passes": "HingeRadauTwo dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; sections declared via dependencies()",
     "passes": "beamIntegration tag + Section refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag each section → emitter.beamIntegration('HingeRadauTwo',tag,secI,lpI,secJ,lpJ,secE)",
     "passes": "tag int + 3 section tags + lp lengths",
     "to": "external"
    }
   ],
   "inputs": "section_i/j/interior:Section, lp_i>0, lp_j>0",
   "outputs": "HingeRadauTwo (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.Legendre",
   "label": "Legendre",
   "composite": "os.integration",
   "signature": "Legendre(*, section, n_ip) -> Legendre",
   "summary": "Gauss-Legendre interior-only beam integration over one section, n_ip>=1 (beamIntegration Legendre); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:126",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (n_ip>=1)",
     "passes": "Legendre dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; section declared via dependencies()",
     "passes": "beamIntegration tag + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag(section) → emitter.beamIntegration('Legendre',tag,secTag,n_ip)",
     "passes": "tag int + section tag + n_ip",
     "to": "external"
    }
   ],
   "inputs": "section:Section, n_ip>=1",
   "outputs": "Legendre (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.Lobatto",
   "label": "Lobatto",
   "composite": "os.integration",
   "signature": "Lobatto(*, section, n_ip) -> Lobatto",
   "summary": "Gauss-Lobatto beam integration over one section with n_ip>=2 IPs (beamIntegration Lobatto); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:93",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (n_ip>=2)",
     "passes": "Lobatto dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "TagAllocator.allocate_for kind 'beamIntegration'; section declared via dependencies()",
     "passes": "beamIntegration tag + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag(section) → emitter.beamIntegration('Lobatto',tag,secTag,n_ip)",
     "passes": "tag int + section tag + n_ip",
     "to": "external"
    }
   ],
   "inputs": "section:Section, n_ip>=2",
   "outputs": "Lobatto (BeamIntegration); referenced by force/dispBeamColumn",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.NewtonCotes",
   "label": "NewtonCotes",
   "composite": "os.integration",
   "signature": "NewtonCotes(*, section, n_ip) -> NewtonCotes",
   "summary": "Closed Newton-Cotes beam integration over one section, n_ip>=2 (beamIntegration NewtonCotes); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:157",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (n_ip>=2)",
     "passes": "NewtonCotes dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; section declared via dependencies()",
     "passes": "beamIntegration tag + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag(section) → emitter.beamIntegration('NewtonCotes',tag,secTag,n_ip)",
     "passes": "tag int + section tag + n_ip",
     "to": "external"
    }
   ],
   "inputs": "section:Section, n_ip>=2",
   "outputs": "NewtonCotes (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.Radau",
   "label": "Radau",
   "composite": "os.integration",
   "signature": "Radau(*, section, n_ip) -> Radau",
   "summary": "Gauss-Radau beam integration over one section, n_ip>=1 (beamIntegration Radau); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:187",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (n_ip>=1)",
     "passes": "Radau dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; section declared via dependencies()",
     "passes": "beamIntegration tag + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag(section) → emitter.beamIntegration('Radau',tag,secTag,n_ip)",
     "passes": "tag int + section tag + n_ip",
     "to": "external"
    }
   ],
   "inputs": "section:Section, n_ip>=1",
   "outputs": "Radau (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.beamIntegration.Trapezoidal",
   "label": "Trapezoidal",
   "composite": "os.integration",
   "signature": "Trapezoidal(*, section, n_ip) -> Trapezoidal",
   "summary": "Composite trapezoidal beam integration over one section, n_ip>=2 (beamIntegration Trapezoidal); auto-registered.",
   "file": "src/apeGmsh/opensees/integration.py:217",
   "flow": [
    {
     "node": "apesees",
     "action": "_BeamIntegrationNS builds+validates (n_ip>=2)",
     "passes": "Trapezoidal dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate beamIntegration tag; section declared via dependencies()",
     "passes": "beamIntegration tag + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolve_tag(section) → emitter.beamIntegration('Trapezoidal',tag,secTag,n_ip)",
     "passes": "tag int + section tag + n_ip",
     "to": "external"
    }
   ],
   "inputs": "section:Section, n_ip>=2",
   "outputs": "Trapezoidal (BeamIntegration)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.build",
   "label": "build",
   "composite": "os.build",
   "signature": "build() -> BuiltModel",
   "summary": "Freeze declared primitives + tag assignments + fix/mass records into an immutable BuiltModel.",
   "file": "src/apeGmsh/opensees/apesees.py:814",
   "flow": [
    {
     "node": "apesees",
     "action": "require ndm/ndf, snapshot id(p)→tag from TagAllocator",
     "passes": "tag_for dict",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "assemble BuiltModel(primitives,tag_for,ndm,ndf,fem,fix_records,mass_records)",
     "passes": "BuiltModel dataclass",
     "to": "os.build"
    }
   ],
   "inputs": "none (uses declared state)",
   "outputs": "BuiltModel (raises RuntimeError if model() not called)",
   "reads": [
    "femdata",
    "os.registry"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.domain_capture",
   "label": "domain_capture",
   "composite": "results.capture",
   "signature": "domain_capture(spec: DomainCaptureSpec, *, path: str|Path, ops: Any=None) -> DomainCapture",
   "summary": "Resolve a DomainCaptureSpec against the bridge fem/ndm/ndf and return a DomainCapture context manager.",
   "file": "src/apeGmsh/opensees/apesees.py:512",
   "flow": [
    {
     "node": "apesees",
     "action": "require ndm/ndf set, call spec._resolve_with_explicit_ndm_ndf(fem)",
     "passes": "resolved capture spec + FEMData",
     "to": "results.capture"
    },
    {
     "node": "results.capture",
     "action": "construct DomainCapture(resolved,path,fem,ops)",
     "passes": "DomainCapture",
     "to": "results.capture"
    }
   ],
   "inputs": "spec:DomainCaptureSpec, path:str|Path, ops:Any",
   "outputs": "DomainCapture (raises RuntimeError if model() not called)",
   "reads": [
    "femdata"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ASDShellQ4",
   "label": "ASDShellQ4",
   "composite": "os.elements",
   "signature": "ASDShellQ4(*, pg, section, corotational=False, drilling_nt_alpha=None, local_cs=None) -> ASDShellQ4",
   "summary": "4-node ASD shell over a PG with optional corotational/drillingNT/localCS flags (element ASDShellQ4); auto-registered.",
   "file": "src/apeGmsh/opensees/element/shell.py:186",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (local_cs 6-tuple if set)",
     "passes": "ASDShellQ4 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; section declared via dependencies()",
     "passes": "element spec + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 4 nodes + resolve_tag(section) + flags → emitter.element('ASDShellQ4',tag,...)",
     "passes": "per-element 4 node tags + section tag + flag args",
     "to": "external"
    }
   ],
   "inputs": "pg:str, section:Section, corotational, drilling_nt_alpha, local_cs:6-tuple|None",
   "outputs": "ASDShellQ4 (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ASDShellT3",
   "label": "ASDShellT3",
   "composite": "os.elements",
   "signature": "ASDShellT3(*, pg, section, corotational=False, drilling_dof=None, local_cs=None) -> ASDShellT3",
   "summary": "3-node ASD shell over a PG with optional corotational/drillingDOF/localCS flags (element ASDShellT3); auto-registered.",
   "file": "src/apeGmsh/opensees/element/shell.py:248",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (local_cs 6-tuple if set)",
     "passes": "ASDShellT3 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; section declared via dependencies()",
     "passes": "element spec + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 3 nodes + resolve_tag(section) + flags → emitter.element('ASDShellT3',tag,...)",
     "passes": "per-element 3 node tags + section tag + flag args",
     "to": "external"
    }
   ],
   "inputs": "pg:str, section:Section, corotational, drilling_dof, local_cs:6-tuple|None",
   "outputs": "ASDShellT3 (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.CorotTruss",
   "label": "CorotTruss",
   "composite": "os.elements",
   "signature": "CorotTruss(*, pg, A, material, rho=None, c_mass=False, do_rayleigh=False) -> CorotTruss",
   "summary": "Corotational large-rotation truss over a PG (element CorotTruss); same shape as Truss; auto-registered.",
   "file": "src/apeGmsh/opensees/element/truss.py:114",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (A>0, rho>=0)",
     "passes": "CorotTruss dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; material declared via dependencies()",
     "passes": "element spec + UniaxialMaterial ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element resolve_tag(material) → emitter.element('CorotTruss',tag,i,j,A,matTag[,flags])",
     "passes": "per-element node pair + material tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, A>0, material:UniaxialMaterial, rho>=0, c_mass, do_rayleigh",
   "outputs": "CorotTruss (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ElasticTimoshenkoBeam",
   "label": "ElasticTimoshenkoBeam",
   "composite": "os.elements",
   "signature": "ElasticTimoshenkoBeam(*, pg, transf, E, G, A, Iz, Avy, Iy=None, J=None, Avz=None, mass=None, c_mass=False) -> ElasticTimoshenkoBeam",
   "summary": "Closed-form shear-flexible Timoshenko beam over a PG; 3-D auto-selected by Iy/J/Avz; auto-registered.",
   "file": "src/apeGmsh/opensees/element/beam_column.py:435",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (E/G/A/Iz/Avy>0, 3-D needs Iy/J/Avz)",
     "passes": "ElasticTimoshenkoBeam dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; transf declared via dependencies()",
     "passes": "element spec + GeomTransf ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element resolve_tag(transf) → emitter.element('ElasticTimoshenkoBeam',tag,i,j,...)",
     "passes": "per-element node pair + transf tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, transf:GeomTransf, E/G/A/Iz/Avy>0, optional Iy/J/Avz>0, mass>=0, c_mass",
   "outputs": "ElasticTimoshenkoBeam (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.FourNodeQuad",
   "label": "FourNodeQuad",
   "composite": "os.elements",
   "signature": "FourNodeQuad(*, pg, thickness, material, plane_type='PlaneStrain', pressure=None, rho=None, body_force=None) -> FourNodeQuad",
   "summary": "4-node plane quad over a PG (emits OpenSees token 'quad'); composes an NDMaterial; auto-registered.",
   "file": "src/apeGmsh/opensees/element/solid.py:199",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (thickness>0, plane_type∈{PlaneStrain,PlaneStress}, rho>=0)",
     "passes": "FourNodeQuad dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; material declared via dependencies()",
     "passes": "element spec + NDMaterial ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 4 nodes + resolve_tag(material) + optional tail → emitter.element('quad',tag,...)",
     "passes": "per-element 4 node tags + material tag + thickness/type",
     "to": "external"
    }
   ],
   "inputs": "pg:str, thickness>0, material:NDMaterial, plane_type, optional pressure/rho/body_force(2)",
   "outputs": "FourNodeQuad (Element); Python name differs from token 'quad'",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.FourNodeTetrahedron",
   "label": "FourNodeTetrahedron",
   "composite": "os.elements",
   "signature": "FourNodeTetrahedron(*, pg, material, body_force=None) -> FourNodeTetrahedron",
   "summary": "4-node linear tetrahedron over a PG's volume cells (element FourNodeTetrahedron); composes an NDMaterial; auto-registered.",
   "file": "src/apeGmsh/opensees/element/solid.py:66",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds FourNodeTetrahedron dataclass",
     "passes": "FourNodeTetrahedron dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; material declared via dependencies()",
     "passes": "element spec + NDMaterial ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 4 nodes + resolve_tag(material) → emitter.element('FourNodeTetrahedron',tag,i,j,k,l,matTag[,b1,b2,b3])",
     "passes": "per-element 4 node tags + material tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, material:NDMaterial, body_force:(b1,b2,b3)|None",
   "outputs": "FourNodeTetrahedron (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.InertiaTruss",
   "label": "InertiaTruss",
   "composite": "os.elements",
   "signature": "InertiaTruss(*, pg, mass) -> InertiaTruss",
   "summary": "Mass-only truss between two nodes over a PG (element InertiaTruss); no stiffness/material; auto-registered.",
   "file": "src/apeGmsh/opensees/element/truss.py:164",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (mass>0)",
     "passes": "InertiaTruss dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag (no dependencies)",
     "passes": "element spec",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element current_element_nodes → emitter.element('InertiaTruss',tag,i,j,mass)",
     "passes": "per-element node pair + mass",
     "to": "external"
    }
   ],
   "inputs": "pg:str, mass>0",
   "outputs": "InertiaTruss (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ShellDKGQ",
   "label": "ShellDKGQ",
   "composite": "os.elements",
   "signature": "ShellDKGQ(*, pg, section) -> ShellDKGQ",
   "summary": "4-node Discrete Kirchhoff Quadrilateral shell over a PG (element ShellDKGQ); composes a plate Section; auto-registered.",
   "file": "src/apeGmsh/opensees/element/shell.py:153",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds ShellDKGQ dataclass",
     "passes": "ShellDKGQ dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; section declared via dependencies()",
     "passes": "element spec + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 4 nodes + resolve_tag(section) → emitter.element('ShellDKGQ',tag,i,j,k,l,secTag)",
     "passes": "per-element 4 node tags + section tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, section:Section",
   "outputs": "ShellDKGQ (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ShellMITC3",
   "label": "ShellMITC3",
   "composite": "os.elements",
   "signature": "ShellMITC3(*, pg, section) -> ShellMITC3",
   "summary": "3-node MITC shell over a PG's tri cells (element ShellMITC3); composes a plate Section; auto-registered.",
   "file": "src/apeGmsh/opensees/element/shell.py:120",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds ShellMITC3 dataclass",
     "passes": "ShellMITC3 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; section declared via dependencies()",
     "passes": "element spec + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 3 nodes + resolve_tag(section) → emitter.element('ShellMITC3',tag,i,j,k,secTag)",
     "passes": "per-element 3 node tags + section tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, section:Section",
   "outputs": "ShellMITC3 (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ShellMITC4",
   "label": "ShellMITC4",
   "composite": "os.elements",
   "signature": "ShellMITC4(*, pg, section) -> ShellMITC4",
   "summary": "4-node MITC shell over a PG's quad cells (element ShellMITC4); composes a plate Section; auto-registered.",
   "file": "src/apeGmsh/opensees/element/shell.py:82",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds ShellMITC4 dataclass",
     "passes": "ShellMITC4 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; section declared via dependencies()",
     "passes": "element spec + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 4 nodes + resolve_tag(section) → emitter.element('ShellMITC4',tag,i,j,k,l,secTag)",
     "passes": "per-element 4 node tags + section tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, section:Section (plate/shell)",
   "outputs": "ShellMITC4 (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.TenNodeTetrahedron",
   "label": "TenNodeTetrahedron",
   "composite": "os.elements",
   "signature": "TenNodeTetrahedron(*, pg, material, body_force=None) -> TenNodeTetrahedron",
   "summary": "10-node quadratic tetrahedron over a PG (element TenNodeTetrahedron); composes an NDMaterial; auto-registered.",
   "file": "src/apeGmsh/opensees/element/solid.py:106",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds TenNodeTetrahedron dataclass",
     "passes": "TenNodeTetrahedron dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; material declared via dependencies()",
     "passes": "element spec + NDMaterial ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 10 nodes + resolve_tag(material) → emitter.element('TenNodeTetrahedron',tag,...)",
     "passes": "per-element 10 node tags + material tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, material:NDMaterial, body_force:(b1,b2,b3)|None",
   "outputs": "TenNodeTetrahedron (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.Tri31",
   "label": "Tri31",
   "composite": "os.elements",
   "signature": "Tri31(*, pg, thickness, material, plane_type='PlaneStrain', pressure=None, rho=None, body_force=None) -> Tri31",
   "summary": "3-node plane triangle over a PG (emits OpenSees token 'tri31'); composes an NDMaterial; auto-registered.",
   "file": "src/apeGmsh/opensees/element/solid.py:286",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (thickness>0, plane_type valid, rho>=0)",
     "passes": "Tri31 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; material declared via dependencies()",
     "passes": "element spec + NDMaterial ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 3 nodes + resolve_tag(material) + optional tail → emitter.element('tri31',tag,...)",
     "passes": "per-element 3 node tags + material tag + thickness/type",
     "to": "external"
    }
   ],
   "inputs": "pg:str, thickness>0, material:NDMaterial, plane_type, optional pressure/rho/body_force(2)",
   "outputs": "Tri31 (Element); Python name differs from token 'tri31'",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.Truss",
   "label": "Truss",
   "composite": "os.elements",
   "signature": "Truss(*, pg, A, material, rho=None, c_mass=False, do_rayleigh=False) -> Truss",
   "summary": "Uniaxial-material truss over a PG's 2-node line cells (element Truss); auto-registered.",
   "file": "src/apeGmsh/opensees/element/truss.py:53",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (A>0, rho>=0)",
     "passes": "Truss dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; material declared via dependencies()",
     "passes": "element spec + UniaxialMaterial ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element current_element_nodes + resolve_tag(material) → emitter.element('Truss',tag,i,j,A,matTag[,flags])",
     "passes": "per-element node pair + material tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, A>0, material:UniaxialMaterial, rho>=0, c_mass, do_rayleigh",
   "outputs": "Truss (Element); composes a UniaxialMaterial",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ZeroLength",
   "label": "ZeroLength",
   "composite": "os.elements",
   "signature": "ZeroLength(*, pg, mat_dirs: tuple[ZeroLengthMatDir,...], orient=None, do_rayleigh=False) -> ZeroLength",
   "summary": "Coupled (material,dof) springs between two coincident nodes over a PG (element zeroLength); auto-registered.",
   "file": "src/apeGmsh/opensees/element/zero_length.py:92",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (>=1 mat_dir)",
     "passes": "ZeroLength dataclass + ZeroLengthMatDir tuple",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; unique mat_dir materials declared via dependencies()",
     "passes": "element spec + UniaxialMaterial refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element resolve_tag each material → emitter.element('zeroLength',tag,i,j,-mat..,-dir..[,-orient])",
     "passes": "per-element node pair + material tags + dofs",
     "to": "external"
    }
   ],
   "inputs": "pg:str, mat_dirs:tuple[ZeroLengthMatDir] (>=1), orient:6-tuple|None, do_rayleigh",
   "outputs": "ZeroLength (Element); composes UniaxialMaterials",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ZeroLengthMatDir",
   "label": "ZeroLengthMatDir",
   "composite": "os.elements",
   "signature": "ZeroLengthMatDir(*, material, dof) -> ZeroLengthMatDir",
   "summary": "Value object: one (UniaxialMaterial, dof) pair on a ZeroLength element (not a Primitive, no tag).",
   "file": "src/apeGmsh/opensees/element/zero_length.py:61",
   "flow": [
    {
     "node": "types",
     "action": "frozen value object holding a UniaxialMaterial ref + 1-based dof index",
     "passes": "ZeroLengthMatDir into ZeroLength.mat_dirs",
     "to": "os.elements"
    }
   ],
   "inputs": "material:UniaxialMaterial, dof>=1",
   "outputs": "ZeroLengthMatDir value object (held by ZeroLength)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.ZeroLengthSection",
   "label": "ZeroLengthSection",
   "composite": "os.elements",
   "signature": "ZeroLengthSection(*, pg, section, orient=None, do_rayleigh=False) -> ZeroLengthSection",
   "summary": "Section-coupled zero-length element between two nodes over a PG (element zeroLengthSection); auto-registered.",
   "file": "src/apeGmsh/opensees/element/zero_length.py:165",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds ZeroLengthSection dataclass",
     "passes": "ZeroLengthSection dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; section declared via dependencies()",
     "passes": "element spec + Section ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element resolve_tag(section) → emitter.element('zeroLengthSection',tag,i,j,secTag[,-orient])",
     "passes": "per-element node pair + section tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, section:Section, orient:6-tuple|None, do_rayleigh",
   "outputs": "ZeroLengthSection (Element); composes a Section",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.dispBeamColumn",
   "label": "dispBeamColumn",
   "composite": "os.elements",
   "signature": "dispBeamColumn(*, pg, transf, integration, mass=None, c_mass=False) -> dispBeamColumn",
   "summary": "Displacement-based distributed-plasticity beam over a PG (element dispBeamColumn); composes transf + BeamIntegration; auto-registered.",
   "file": "src/apeGmsh/opensees/element/beam_column.py:372",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (pg non-empty, mass>=0)",
     "passes": "dispBeamColumn dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; integration+transf declared via dependencies()",
     "passes": "element spec + BeamIntegration + GeomTransf refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element resolve transf+integration tags → emitter.element('dispBeamColumn',tag,i,j,transfTag,integTag)",
     "passes": "per-element node pair + transf/integ tags",
     "to": "external"
    }
   ],
   "inputs": "pg:str, transf:GeomTransf, integration:BeamIntegration, mass>=0, c_mass",
   "outputs": "dispBeamColumn (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.elasticBeamColumn",
   "label": "elasticBeamColumn",
   "composite": "os.elements",
   "signature": "elasticBeamColumn(*, pg, transf, A, E, Iz, Iy=None, G=None, J=None, mass=None, c_mass=False) -> elasticBeamColumn",
   "summary": "Linear-elastic beam-column over a PG (element elasticBeamColumn); 3-D auto-selected by Iy/G/J; auto-registered.",
   "file": "src/apeGmsh/opensees/element/beam_column.py:137",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (pg non-empty, A/E/Iz>0, 3-D needs Iy/G/J)",
     "passes": "elasticBeamColumn dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "TagAllocator.allocate_for kind 'element'; transf declared via dependencies()",
     "passes": "element spec + GeomTransf ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "BuiltModel fans spec across PG line cells; per element current_element_nodes + resolve_tag(transf) → emitter.element('elasticBeamColumn',tag,i,j,...)",
     "passes": "per-element node pair + transf tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, transf:Linear|PDelta|Corotational, A/E/Iz>0, optional Iy/G/J>0, mass>=0, c_mass",
   "outputs": "elasticBeamColumn (Element); composes a GeomTransf",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.forceBeamColumn",
   "label": "forceBeamColumn",
   "composite": "os.elements",
   "signature": "forceBeamColumn(*, pg, transf, integration, mass=None, max_iter=None, tol=None) -> forceBeamColumn",
   "summary": "Force-based distributed-plasticity beam over a PG (element forceBeamColumn); composes transf + BeamIntegration; auto-registered.",
   "file": "src/apeGmsh/opensees/element/beam_column.py:262",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds+validates (pg non-empty, iter pair all-or-none)",
     "passes": "forceBeamColumn dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; integration+transf declared via dependencies()",
     "passes": "element spec + BeamIntegration + GeomTransf refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element resolve_tag(transf)+resolve_tag(integration) → emitter.element('forceBeamColumn',tag,i,j,transfTag,integTag[,-iter])",
     "passes": "per-element node pair + transf/integ tags",
     "to": "external"
    }
   ],
   "inputs": "pg:str, transf:GeomTransf, integration:BeamIntegration, mass>=0, max_iter+tol paired",
   "outputs": "forceBeamColumn (Element); composes GeomTransf + BeamIntegration",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.element.stdBrick",
   "label": "stdBrick",
   "composite": "os.elements",
   "signature": "stdBrick(*, pg, material, body_force=None) -> stdBrick",
   "summary": "8-node trilinear continuum brick over a PG (element stdBrick); composes an NDMaterial; auto-registered.",
   "file": "src/apeGmsh/opensees/element/solid.py:146",
   "flow": [
    {
     "node": "apesees",
     "action": "_ElementNS builds stdBrick dataclass",
     "passes": "stdBrick dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate element tag; material declared via dependencies()",
     "passes": "element spec + NDMaterial ref",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "fan over PG; per element 8 nodes + resolve_tag(material) → emitter.element('stdBrick',tag,...)",
     "passes": "per-element 8 node tags + material tag",
     "to": "external"
    }
   ],
   "inputs": "pg:str, material:NDMaterial, body_force:(b1,b2,b3)|None",
   "outputs": "stdBrick (Element)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.export.h5",
   "label": "h5",
   "composite": "os.emit",
   "signature": "h5(path: str, *, model_name: str|None=None, cuts: Sequence[SectionCutDef]=(), sweeps: Sequence[SectionSweepDef]=()) -> None",
   "summary": "Public facade: build() then drive an H5Emitter (structured buffer) end-to-end; composes model.h5 in layers — broker writes /meta + neutral zone, bridge appends /opensees/... via write_opensees_into, cuts/sweeps persisted under /opensees/cuts|sweeps. Schema 2.5.0.",
   "file": "src/apeGmsh/opensees/apesees.py:704",
   "flow": [
    {
     "node": "apesees",
     "action": "user calls ops.h5(path, cuts=, sweeps=); reads fem.snapshot_id; self.build() freezes a BuiltModel; constructs H5Emitter(model_name,snapshot_id)",
     "passes": "<BuiltModel + H5Emitter>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit(H5Emitter) drives every Protocol method; H5Emitter buffers each into typed records (materials/sections/transforms/element_meta/time_series/patterns/recorders/analysis), not a stream",
     "passes": "<OpenSees command tuples → buffered H5 records>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "open h5py.File(w): _try_write_broker_zone(fem) writes /meta+neutral zone (or bridge _write_meta fallback for stub FEM), emitter.write_opensees_into(f) appends /opensees/... groups, write_cuts_into(f,cuts,sweeps)",
     "passes": "<model.h5 Path (dual-zone schema 2.5.0)>",
     "to": "femdata.h5"
    }
   ],
   "inputs": "path:str; model_name; cuts/sweeps sequences",
   "outputs": "a dual-zone model.h5 archive at path (/meta + neutral zone + /opensees/...); returns None",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "femdata.h5",
    "external"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.export.py",
   "label": "py",
   "composite": "os.emit",
   "signature": "py(path: str, *, run: bool=False) -> None",
   "summary": "Public facade: build() then drive a PyEmitter end-to-end, write a self-contained openseespy Python script (imports openseespy, ops.wipe(), one ops.X(...) per command) to path; optionally subprocess-run it.",
   "file": "src/apeGmsh/opensees/apesees.py:664",
   "flow": [
    {
     "node": "apesees",
     "action": "user calls ops.py(path); self.build() freezes a BuiltModel; constructs PyEmitter",
     "passes": "<BuiltModel + PyEmitter>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit(PyEmitter) drives every Protocol method; PyEmitter renders each as an ops.<method>(...) source line (Tcl block dialect → openseespy implicit current-section/pattern state)",
     "passes": "<OpenSees command tuples → ops.X() source lines>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "emitter.lines() joined and written to path; if run=True subprocess the resolved python binary on the script",
     "passes": "<openseespy script Path>",
     "to": "external"
    }
   ],
   "inputs": "path:str; run:bool",
   "outputs": "an openseespy Python script at path (+ optional subprocess run); returns None",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "external"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.export.tcl",
   "label": "tcl",
   "composite": "os.emit",
   "signature": "tcl(path: str, *, run: bool=False, bin: str|None=None) -> None",
   "summary": "Public facade: build() then drive a TclEmitter end-to-end (BuiltModel.emit walks the registry — model/nodes/materials/sections/transforms/elements/fix/mass/patterns/recorders/analysis), write the accumulated Tcl deck to path; optionally subprocess OpenSees.",
   "file": "src/apeGmsh/opensees/apesees.py:632",
   "flow": [
    {
     "node": "apesees",
     "action": "user calls ops.tcl(path); self.build() freezes a BuiltModel; constructs TclEmitter",
     "passes": "<BuiltModel + TclEmitter>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit(TclEmitter) topo-sorts os.registry primitives and drives every Protocol method; TclEmitter appends one Tcl line per call (block dialect for Fiber sections / Plain patterns)",
     "passes": "<OpenSees command tuples → Tcl line strings>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "emitter.lines() joined and written to path (utf-8); if run=True subprocess OpenSees binary on the deck",
     "passes": "<Tcl deck Path>",
     "to": "external"
    }
   ],
   "inputs": "path:str; run:bool; bin: optional OpenSees binary path",
   "outputs": "a Tcl deck file at path (+ optional subprocess run); returns None",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "external"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.fem",
   "label": "fem",
   "composite": "apesees",
   "signature": "fem -> FEMData",
   "summary": "Read-only property returning the FEMData snapshot the bridge was built against.",
   "file": "src/apeGmsh/opensees/apesees.py:501",
   "flow": [
    {
     "node": "apesees",
     "action": "return self._fem",
     "passes": "FEMData snapshot",
     "to": "femdata"
    }
   ],
   "inputs": "none",
   "outputs": "FEMData",
   "reads": [
    "femdata"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.fix",
   "label": "fix",
   "composite": "apesees",
   "signature": "fix(*, pg: str|None=None, nodes: Iterable[int|Node]|None=None, dofs: tuple[int,...]) -> None",
   "summary": "Append a homogeneous SP-constraint record (exactly one of pg= or nodes=).",
   "file": "src/apeGmsh/opensees/apesees.py:561",
   "flow": [
    {
     "node": "apesees",
     "action": "validate exactly-one-of, normalize nodes via _iter_tags, append FixRecord",
     "passes": "FixRecord(pg,nodes,dofs)",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel._emit_fixes expands pg→nodes at emit, calls emitter.fix",
     "passes": "node tags + dofs",
     "to": "os.emit"
    }
   ],
   "inputs": "pg:str | nodes:Iterable[int|Node]; dofs:tuple[int,...]",
   "outputs": "None (appends to self._fix_records)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.geomTransf.Corotational",
   "label": "Corotational",
   "composite": "os.transform",
   "signature": "Corotational(*, orientation=None, vecxz=None, roll_deg=0.0) -> Corotational",
   "summary": "Finite-displacement corotational geometric transform (geomTransf Corotational); same shape as Linear; auto-registered.",
   "file": "src/apeGmsh/opensees/transform.py:228",
   "flow": [
    {
     "node": "apesees",
     "action": "_GeomTransfNS builds+validates (orientation XOR vecxz)",
     "passes": "Corotational dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate geomTransf tag, register",
     "passes": "geomTransf tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "emit_transform_specs per-element vecxz → emitter.geomTransf('Corotational',tag,vecxz)",
     "passes": "tag int + per-element vecxz",
     "to": "external"
    }
   ],
   "inputs": "orientation:Orientation|None, vecxz:(x,y,z)|None, roll_deg",
   "outputs": "Corotational (GeomTransf)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.geomTransf.Linear",
   "label": "Linear",
   "composite": "os.transform",
   "signature": "Linear(*, orientation=None, vecxz=None, roll_deg=0.0) -> Linear",
   "summary": "Small-displacement linearized geometric transform (geomTransf Linear); orientation XOR vecxz; auto-registered.",
   "file": "src/apeGmsh/opensees/transform.py:130",
   "flow": [
    {
     "node": "apesees",
     "action": "_GeomTransfNS builds+validates (orientation XOR vecxz, roll_deg only with orientation), substitutes default_orientation if neither",
     "passes": "Linear dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "TagAllocator.allocate_for kind 'geomTransf', register",
     "passes": "geomTransf tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "emit_transform_specs derives per-element vecxz from orientation triad (ADR 0010) → emitter.geomTransf('Linear',tag,vecxz)",
     "passes": "tag int + per-element vecxz",
     "to": "external"
    }
   ],
   "inputs": "orientation:Orientation|None, vecxz:(x,y,z)|None, roll_deg (orientation-only)",
   "outputs": "Linear (GeomTransf); referenced by beam-column elements",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.geomTransf.PDelta",
   "label": "PDelta",
   "composite": "os.transform",
   "signature": "PDelta(*, orientation=None, vecxz=None, roll_deg=0.0) -> PDelta",
   "summary": "Linear transform plus P-Delta effects (geomTransf PDelta); same construction shape as Linear; auto-registered.",
   "file": "src/apeGmsh/opensees/transform.py:194",
   "flow": [
    {
     "node": "apesees",
     "action": "_GeomTransfNS builds+validates (orientation XOR vecxz)",
     "passes": "PDelta dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate geomTransf tag, register",
     "passes": "geomTransf tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "emit_transform_specs per-element vecxz → emitter.geomTransf('PDelta',tag,vecxz)",
     "passes": "tag int + per-element vecxz",
     "to": "external"
    }
   ],
   "inputs": "orientation:Orientation|None, vecxz:(x,y,z)|None, roll_deg",
   "outputs": "PDelta (GeomTransf)",
   "reads": [
    "fem.elements",
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.h5",
   "label": "h5",
   "composite": "apesees",
   "signature": "h5(path: str, *, model_name: str|None=None, cuts: Sequence[SectionCutDef]=(), sweeps: Sequence[SectionSweepDef]=()) -> None",
   "summary": "Emit a model-definition HDF5 archive: broker neutral zone + bridge /opensees/ enrichment + optional cuts/sweeps.",
   "file": "src/apeGmsh/opensees/apesees.py:704",
   "flow": [
    {
     "node": "apesees",
     "action": "read fem.snapshot_id, build()",
     "passes": "BuiltModel + snapshot_id",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit drives H5Emitter",
     "passes": "H5Emitter populated /opensees groups",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "write broker /meta+neutral zone then /opensees + cuts/sweeps into h5py.File",
     "passes": "HDF5 file groups",
     "to": "femdata.h5"
    }
   ],
   "inputs": "path:str, model_name:str|None, cuts:Seq[SectionCutDef], sweeps:Seq[SectionSweepDef]",
   "outputs": "None (writes model.h5 archive)",
   "reads": [
    "femdata",
    "fem.nodes"
   ],
   "writes": [
    "femdata.h5"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.live.analyze",
   "label": "analyze",
   "composite": "os.emit",
   "signature": "analyze(*, steps: int, dt: float|None=None) -> int",
   "summary": "Public facade: validate the analysis chain is complete, build() + drive a LiveOpsEmitter end-to-end, then issue the openseespy analyze(steps[,dt]) call; returns the openseespy analyze return value (0 on success).",
   "file": "src/apeGmsh/opensees/apesees.py:608",
   "flow": [
    {
     "node": "apesees",
     "action": "user calls ops.analyze(steps=,dt=); _check_analysis_chain_for_analyze raises BridgeError if constraints/numberer/system/test/algorithm/integrator/analysis missing; self.build() freezes a BuiltModel; constructs LiveOpsEmitter(wipe=True)",
     "passes": "<BuiltModel + LiveOpsEmitter>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit(LiveOpsEmitter) drives the full deck into openseespy in-process",
     "passes": "<OpenSees command tuples → live ops.X() calls>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "live_emitter.analyze(steps=,dt=) → ops.analyze(...); return value forwarded to caller",
     "passes": "<analyze exit code int>",
     "to": "external"
    }
   ],
   "inputs": "steps:int; dt: optional float",
   "outputs": "int — openseespy analyze return code (0=success); BridgeError if chain incomplete",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "external"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.live.run",
   "label": "run",
   "composite": "os.emit",
   "signature": "run(*, wipe: bool=True) -> None",
   "summary": "Public facade: build() then drive a LiveOpsEmitter in-process through the full deck (every primitive declared into openseespy state) but does NOT call analyze — the user runs their own analysis driver afterward.",
   "file": "src/apeGmsh/opensees/apesees.py:690",
   "flow": [
    {
     "node": "apesees",
     "action": "user calls ops.run(wipe=); self.build() freezes a BuiltModel; constructs LiveOpsEmitter(wipe=) (lazy-imports openseespy)",
     "passes": "<BuiltModel + LiveOpsEmitter>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit(LiveOpsEmitter) drives every Protocol method; LiveOpsEmitter forwards each to ops.X(...) in-process (no analyze)",
     "passes": "<OpenSees command tuples → live ops.X() calls>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "openseespy domain populated in-process; control returns to user for their own analysis driver",
     "passes": "<populated openseespy in-process state>",
     "to": "external"
    }
   ],
   "inputs": "wipe:bool (ops.wipe() at construction)",
   "outputs": "populated in-process openseespy state (no analyze); returns None",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "external"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.mass",
   "label": "mass",
   "composite": "apesees",
   "signature": "mass(*, pg: str|None=None, nodes: Iterable[int|Node]|None=None, values: tuple[float,...]) -> None",
   "summary": "Append a lumped-nodal-mass record (exactly one of pg= or nodes=).",
   "file": "src/apeGmsh/opensees/apesees.py:586",
   "flow": [
    {
     "node": "apesees",
     "action": "validate exactly-one-of, normalize nodes, append MassRecord",
     "passes": "MassRecord(pg,nodes,values)",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel._emit_masses expands pg→nodes at emit, calls emitter.mass",
     "passes": "node tags + values",
     "to": "os.emit"
    }
   ],
   "inputs": "pg:str | nodes:Iterable[int|Node]; values:tuple[float,...]",
   "outputs": "None (appends to self._mass_records)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.model",
   "label": "model",
   "composite": "apesees",
   "signature": "model(*, ndm: int, ndf: int) -> None",
   "summary": "Set model dimensionality ndm and DOFs-per-node ndf (required before build).",
   "file": "src/apeGmsh/opensees/apesees.py:507",
   "flow": [
    {
     "node": "apesees",
     "action": "store ndm/ndf on the bridge",
     "passes": "ndm:int, ndf:int",
     "to": "os.build"
    }
   ],
   "inputs": "ndm:int, ndf:int",
   "outputs": "None (mutates self._ndm/_ndf)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.nDMaterial.DruckerPrager",
   "label": "DruckerPrager",
   "composite": "os.materials",
   "signature": "DruckerPrager(*, K, G, sigmaY, rho, rhoBar, Kinf, Ko, delta1, delta2, H, theta) -> DruckerPrager",
   "summary": "Pressure-dependent Drucker-Prager elasto-plastic continuum material (nDMaterial DruckerPrager); auto-registered.",
   "file": "src/apeGmsh/opensees/material/nd.py:168",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate DruckerPrager (K/G/sigmaY>0, rho..H>=0, theta∈[0,1])",
     "passes": "DruckerPrager dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate nDMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.nDMaterial('DruckerPrager',tag,K,G,sigmaY,rho,rhoBar,Kinf,Ko,delta1,delta2,H,theta)",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "K>0,G>0,sigmaY>0, rho/rhoBar/Kinf/Ko/delta1/delta2/H>=0, theta∈[0,1]",
   "outputs": "DruckerPrager (NDMaterial)",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.nDMaterial.ElasticIsotropic",
   "label": "ElasticIsotropic",
   "composite": "os.materials",
   "signature": "ElasticIsotropic(*, E, nu, rho=0.0) -> ElasticIsotropic",
   "summary": "Linear-elastic isotropic continuum material (nDMaterial ElasticIsotropic); auto-registered.",
   "file": "src/apeGmsh/opensees/material/nd.py:41",
   "flow": [
    {
     "node": "apesees",
     "action": "_NDMaterialNS builds+validates ElasticIsotropic (E>0, nu∈[0,0.5), rho>=0)",
     "passes": "ElasticIsotropic dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "TagAllocator.allocate_for kind 'nDMaterial', register",
     "passes": "nDMaterial tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.nDMaterial('ElasticIsotropic',tag,E,nu,rho)",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "E>0, nu∈[0,0.5), rho>=0",
   "outputs": "ElasticIsotropic (NDMaterial); referenced by solid elements + shell layers",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.nDMaterial.J2Plasticity",
   "label": "J2Plasticity",
   "composite": "os.materials",
   "signature": "J2Plasticity(*, K, G, sig0, sigInf, delta, H, eta=0.0) -> J2Plasticity",
   "summary": "von-Mises plasticity with combined nonlinear hardening (nDMaterial J2Plasticity); auto-registered.",
   "file": "src/apeGmsh/opensees/material/nd.py:88",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate J2Plasticity (K>0,G>0,sig0>0,delta/H/eta>=0)",
     "passes": "J2Plasticity dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate nDMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.nDMaterial('J2Plasticity',tag,K,G,sig0,sigInf,delta,H,eta)",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "K>0,G>0,sig0>0,sigInf,delta>=0,H>=0,eta>=0",
   "outputs": "J2Plasticity (NDMaterial)",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.nodes.get",
   "label": "get",
   "composite": "os.node",
   "signature": "get(*, tag: int|None=None, pg: str|None=None) -> Node | NodeSet",
   "summary": "Resolve a single Node (tag=), a NodeSet (pg=), or all FEM nodes (no args) from the FEM snapshot.",
   "file": "src/apeGmsh/opensees/node.py:230",
   "flow": [
    {
     "node": "os.node",
     "action": "_NodeAccessor validates at-most-one-of, dispatch to _node_by_tag/_set_by_pg/_all",
     "passes": "tag int | pg str",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "fem.nodes.index/coords/get(pg) lookups build Node/NodeSet wrappers",
     "passes": "Node | NodeSet (tag+coords+bridge)",
     "to": "os.node"
    }
   ],
   "inputs": "tag:int|None or pg:str|None",
   "outputs": "Node | NodeSet (KeyError if tag/pg absent)",
   "reads": [
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.nodes.summary",
   "label": "summary",
   "composite": "os.node",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame of every FEM node with columns tag, x, y, z.",
   "file": "src/apeGmsh/opensees/node.py:281",
   "flow": [
    {
     "node": "os.node",
     "action": "_NodeAccessor._all() then NodeSet.summary()",
     "passes": "NodeSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "read ids+coords into pandas DataFrame",
     "passes": "DataFrame(tag,x,y,z)",
     "to": "os.node"
    }
   ],
   "inputs": "none",
   "outputs": "pd.DataFrame",
   "reads": [
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.pattern.Plain",
   "label": "Plain (pattern)",
   "composite": "os.pattern",
   "signature": "Plain(*, series: TimeSeries) -> Plain  (context manager; .load(pg=|node=, forces=), .sp(pg=|node=, dof=, value=))",
   "summary": "Typed `pattern Plain tag tsTag { loads + sps }` primitive — the workhorse load pattern; an explicit context manager (ADR 0005) recording _LoadRecord/_SPRecord entries played back at emit; depends on its TimeSeries.",
   "file": "src/apeGmsh/opensees/pattern/pattern.py:125",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Plain(series=ts) and inside `with` calls .load(...)/.sp(...) recording _LoadRecord/_SPRecord; ops._register allocates a pattern tag",
     "passes": "<Plain:Pattern primitive + tag int; series dependency; load/sp records>",
     "to": "os.registry"
    },
    {
     "node": "os.pattern",
     "action": "BuiltModel.emit post-element pass routes to build.emit_pattern_spec → resolve_tag(series) then emitter.pattern_open('Plain',tag,ts_tag); each pg= record fans out via expand_pg_to_nodes, node= passes through, emitting emitter.load/emitter.sp; emitter.pattern_close()",
     "passes": "<pattern_open tuple + per-node load/sp commands>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "emit_pattern_spec drives the emitter (pg→node fan-out resolved against FEMData here, not in _emit)",
     "passes": "<emitter.load/sp(node_tag,...) commands>",
     "to": "os.emit"
    }
   ],
   "inputs": "series: a registered TimeSeries primitive; recorded load(pg=|node=,forces=) and sp(pg=|node=,dof=,value=) entries",
   "outputs": "pattern_open/load/sp/pattern_close stream into the emitter (pg fan-out via FEMData at build time)",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.pattern.UniformExcitation",
   "label": "UniformExcitation",
   "composite": "os.pattern",
   "signature": "UniformExcitation(*, direction: int, series: TimeSeries) -> UniformExcitation",
   "summary": "Typed `pattern UniformExcitation tag dir -accel tsTag` primitive — uniform base-acceleration ground-motion pattern; no body, payload is the pattern; direction 1-6 validated; depends on its TimeSeries.",
   "file": "src/apeGmsh/opensees/pattern/pattern.py:290",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs UniformExcitation(direction=,series=ts); __post_init__ validates direction∈1..6; ops._register allocates a pattern tag",
     "passes": "<UniformExcitation:Pattern primitive + tag int; series dependency>",
     "to": "os.registry"
    },
    {
     "node": "os.pattern",
     "action": "BuiltModel.emit post-element pass → emit_pattern_spec; non-Plain so delegates to _emit: resolve_tag(series) then emitter.pattern_open('UniformExcitation',tag,direction,'-accel',ts_tag); emitter.pattern_close()",
     "passes": "<command:('UniformExcitation',tag,dir,'-accel',ts_tag:int)>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "emit_pattern_spec delegates to spec._emit (no pg fan-out)",
     "passes": "<pattern_open/close commands>",
     "to": "os.emit"
    }
   ],
   "inputs": "direction:int∈1..6; series: a registered TimeSeries primitive",
   "outputs": "pattern_open/pattern_close stream into the emitter",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.py",
   "label": "py",
   "composite": "apesees",
   "signature": "py(path: str, *, run: bool=False) -> None",
   "summary": "Build and emit an openseespy Python deck to path; optionally run it via subprocess.",
   "file": "src/apeGmsh/opensees/apesees.py:664",
   "flow": [
    {
     "node": "apesees",
     "action": "build() then drive PyEmitter",
     "passes": "BuiltModel",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit fans primitives into PyEmitter lines",
     "passes": "emitter line list",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "write lines to path; optional subprocess([python,path])",
     "passes": "openseespy .py file",
     "to": "external"
    }
   ],
   "inputs": "path:str, run:bool",
   "outputs": "None (writes .py file; may raise RuntimeError on subprocess failure)",
   "reads": [
    "femdata",
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.recorder.Element",
   "label": "Element (recorder)",
   "composite": "os.recorder",
   "signature": "Element(*, file: str, response: tuple[str,...], elements: tuple[int,...]|None=None, pg: str|None=None, dT: float|None=None, time_format: str='step') -> Element",
   "summary": "Typed `recorder Element -file f [-time][-dT] (-ele|pg) response...` primitive — element-response recorder taking raw OpenSees response-token tuple; exactly-one-of elements=/pg=.",
   "file": "src/apeGmsh/opensees/recorder.py:170",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Element(file=,response=, elements=|pg=); __post_init__ validates response non-empty; ops._register adds it",
     "passes": "<Element:Recorder primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.recorder",
     "action": "BuiltModel.emit post-element pass → emit_recorder_spec; pg= replaced via expand_pg_to_elements (ids only) into elements= replica then replica._emit",
     "passes": "<command:('Element','-file',f,...,'-ele',elements,*response)>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "emit_recorder_spec resolves pg→element ids against FEMData, drives _emit calling emitter.recorder('Element',...)",
     "passes": "<emitter.recorder('Element',*args) command>",
     "to": "os.emit"
    }
   ],
   "inputs": "file:str, response:tuple[str,...] (raw tokens), elements XOR pg, optional dT, time_format step|dt",
   "outputs": "emitter.recorder('Element',...) command (pg→elements via FEMData at build)",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.recorder.MPCO",
   "label": "MPCO",
   "composite": "os.recorder",
   "signature": "MPCO(*, file: str, nodal_responses: tuple[str,...]=(), elem_responses: tuple[str,...]=(), dT: float|None=None, nsteps: int|None=None) -> MPCO",
   "summary": "Typed `recorder mpco f.mpco [-N ...][-E ...][-T dt|nsteps]` primitive — single HDF5 .mpco recorder; takes raw MPCO tokens; >=1 of nodal/elem responses; dT XOR nsteps.",
   "file": "src/apeGmsh/opensees/recorder.py:255",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs MPCO(file=, nodal_responses=|elem_responses=); __post_init__ validates non-empty + dT/nsteps exclusivity; ops._register adds it",
     "passes": "<MPCO:Recorder primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.recorder",
     "action": "BuiltModel.emit post-element pass → emit_recorder_spec; MPCO has no pg resolution so passes straight to _emit which assembles -N/-E/-T args and calls emitter.recorder('mpco',...)",
     "passes": "<command:('mpco',file,['-N',...],['-E',...],['-T',...])>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "emit_recorder_spec pass-through → spec._emit → emitter.recorder('mpco',*args)",
     "passes": "<emitter.recorder('mpco',*args) command>",
     "to": "os.emit"
    }
   ],
   "inputs": "file:str (.mpco), nodal_responses/elem_responses (raw MPCO tokens, >=1), dT XOR nsteps",
   "outputs": "emitter.recorder('mpco',...) command",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.recorder.Node",
   "label": "Node (recorder)",
   "composite": "os.recorder",
   "signature": "Node(*, file: str, response: str, nodes: tuple[int,...]|None=None, pg: str|None=None, dofs: tuple[int,...], dT: float|None=None, time_format: str='step') -> Node",
   "summary": "Typed `recorder Node -file f [-time][-dT] (-node|pg) -dof d... response` primitive — nodal-response recorder; takes raw OpenSees tokens; exactly-one-of nodes=/pg=; pg= fan-out resolved by build pipeline.",
   "file": "src/apeGmsh/opensees/recorder.py:82",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Node(file=,response=,dofs=, nodes=|pg=); __post_init__ validates; ops._register adds it (recorders are dependency-graph leaves)",
     "passes": "<Node:Recorder primitive>",
     "to": "os.registry"
    },
    {
     "node": "os.recorder",
     "action": "BuiltModel.emit post-element pass → build.emit_recorder_spec; pg= replaced via expand_pg_to_nodes into nodes= replica then replica._emit; node= passes to _emit directly",
     "passes": "<command:('Node','-file',f,...,'-dof',dofs,response)>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "emit_recorder_spec resolves pg→node ids against FEMData, drives _emit which calls emitter.recorder('Node',...)",
     "passes": "<emitter.recorder('Node',*args) command>",
     "to": "os.emit"
    }
   ],
   "inputs": "file:str, response:str (raw OpenSees token), dofs (1-based), nodes XOR pg, optional dT, time_format step|dt",
   "outputs": "emitter.recorder('Node',...) command (pg→nodes via FEMData at build)",
   "reads": [
    "os.registry",
    "femdata"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.recorder.RecorderDeclaration",
   "label": "RecorderDeclaration",
   "composite": "os.recorder",
   "signature": "RecorderDeclaration(*, records: tuple[RecorderRecord,...], name: str='default', ndm: int=3, ndf: int=6, file_root: str='.') -> RecorderDeclaration",
   "summary": "Unified bridge-side recorder bundle (Phase 9) registered as one Primitive; carries snapshot ndm/ndf; _emit raises — fan-out is driven by build.emit_recorder_spec which translates canonical components to OpenSees recorder tokens via _recorder_translate/_response_catalog.",
   "file": "src/apeGmsh/opensees/recorder.py:482",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs RecorderDeclaration(records=...) (typically via ops.recorder.declare which snapshots bridge ndm/ndf); ops._register adds it as a Recorder leaf",
     "passes": "<RecorderDeclaration:Recorder primitive (records + ndm/ndf/file_root)>",
     "to": "os.registry"
    },
    {
     "node": "os.recorder",
     "action": "BuiltModel.emit post-element pass → emit_recorder_spec dispatches to _emit_recorder_declaration; per record: recorder_declaration_begin metadata, then nodes→group_node_components_by_ops_token / element-level→element_record_response_tokens translation, selectors resolved to ids via FEMData (expand_pg/label/selection), one emitter.recorder(...) per (ops_token,target) group + raw tokens (+ line_stations gpx pairing), recorder_declaration_end; fibers/layers/modal raise NotImplementedError",
     "passes": "<per-record canonical→ops token translation; resolved id lists; emitter.recorder commands>",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "_emit_recorder_declaration calls into _recorder_translate (node_component_to_ops/group_node_components_by_ops_token, element_record_response_tokens→_response_catalog.gauss_keyword_for_canonical) and FEMData selector expansion, then drives emitter.recorder + recorder_declaration_begin/end (H5 emitter archives the declaration metadata; Tcl/Py/Live treat begin/end as no-ops)",
     "passes": "<emitter.recorder(...) + declaration-metadata bracket commands>",
     "to": "os.emit"
    }
   ],
   "inputs": "records:tuple[RecorderRecord,...]; name; ndm/ndf snapshot; file_root",
   "outputs": "one OpenSees recorder command per (ops_token,target_set) group + raw + line_stations gpx; H5 emitter persists declaration intent under /opensees/recorders/",
   "reads": [
    "os.registry",
    "femdata",
    "vocabulary"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.recorder.RecorderRecord",
   "label": "RecorderRecord",
   "composite": "os.recorder",
   "signature": "RecorderRecord(*, category: str, components: tuple[str,...]=(), raw: tuple[str,...]=(), pg/label/selection: tuple[str,...]=(), ids: tuple[int,...]|None=None, dt/n_steps: ...|None=None, name: str|None=None, n_modes: int|None=None, element_class_name: str|None=None) -> RecorderRecord",
   "summary": "One category-level declaration entry (nodes/elements/line_stations/gauss/fibers/layers/modal) carrying expanded canonical components, validated against the per-category canonical vocabulary; bundled into a RecorderDeclaration.",
   "file": "src/apeGmsh/opensees/recorder.py:368",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs RecorderRecord(category=,components=,...) (Phase 9 unified surface); __post_init__ validates category/components against _CATEGORY_CANONICALS+is_canonical, cadence, selector exclusivity, modal n_modes",
     "passes": "<RecorderRecord value object (expanded canonical components)>",
     "to": "types"
    },
    {
     "node": "types",
     "action": "held in RecorderDeclaration.records tuple; resolution against FEMData deferred to emit_recorder_spec",
     "passes": "<RecorderRecord in declaration bundle>",
     "to": "os.recorder"
    }
   ],
   "inputs": "category + canonical components (or raw= escape), pg/label/selection/ids selectors, dt|n_steps cadence, modal n_modes",
   "outputs": "a validated record entry (no emit by itself; consumed by RecorderDeclaration)",
   "reads": [
    "vocabulary"
   ],
   "writes": [],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.register",
   "label": "register",
   "composite": "os.registry",
   "signature": "register(prim: _P) -> _P",
   "summary": "Register a standalone typed primitive with the bridge, allocating its tag (P11).",
   "file": "src/apeGmsh/opensees/apesees.py:804",
   "flow": [
    {
     "node": "apesees",
     "action": "dispatch _kind_of(prim), TagAllocator.allocate_for, append to _primitives",
     "passes": "Primitive + kind string",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate next tag for the family, return prim",
     "passes": "tag int bound to id(prim)",
     "to": "os.build"
    }
   ],
   "inputs": "prim:Primitive subclass",
   "outputs": "the same primitive (tag now allocated)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.run",
   "label": "run",
   "composite": "apesees",
   "signature": "run(*, wipe: bool=True) -> None",
   "summary": "Drive an in-process LiveOpsEmitter through the full deck without calling analyze.",
   "file": "src/apeGmsh/opensees/apesees.py:690",
   "flow": [
    {
     "node": "apesees",
     "action": "build() then construct LiveOpsEmitter(wipe)",
     "passes": "BuiltModel",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit populates openseespy domain (no analyze)",
     "passes": "Primitive tuple + FEMData",
     "to": "os.emit"
    }
   ],
   "inputs": "wipe:bool",
   "outputs": "None (populates live openseespy state)",
   "reads": [
    "femdata",
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.ElasticMembranePlateSection",
   "label": "ElasticMembranePlateSection",
   "composite": "os.sections",
   "signature": "ElasticMembranePlateSection(*, E, nu, h, rho=0.0) -> ElasticMembranePlateSection",
   "summary": "Single-layer linear-elastic plate section for shells (section ElasticMembranePlateSection); auto-registered.",
   "file": "src/apeGmsh/opensees/section/plate.py:49",
   "flow": [
    {
     "node": "apesees",
     "action": "_SectionNS builds+validates (E>0, nu∈[0,0.5), h>0, rho>=0)",
     "passes": "ElasticMembranePlateSection dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate section tag, register",
     "passes": "section tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.section('ElasticMembranePlateSection',tag,E,nu,h,rho)",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "E>0, nu∈[0,0.5), h>0, rho>=0",
   "outputs": "ElasticMembranePlateSection (Section); referenced by Shell elements",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.ElasticSection",
   "label": "ElasticSection",
   "composite": "os.sections",
   "signature": "ElasticSection(*, E, A, Iz, Iy=None, G=None, J=None, alphaY=None, alphaZ=None) -> ElasticSection",
   "summary": "2-D or 3-D linear-elastic beam section (section Elastic); 3-D variant auto-selected by Iy/J/alphaZ; auto-registered.",
   "file": "src/apeGmsh/opensees/section/beam.py:33",
   "flow": [
    {
     "node": "apesees",
     "action": "_SectionNS builds+validates ElasticSection (3-D needs Iy/G/J)",
     "passes": "ElasticSection dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "TagAllocator.allocate_for kind 'section', register",
     "passes": "section tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit picks 2-D/3-D arg layout → emitter.section('Elastic',tag,...)",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "E>0,A>0,Iz>0; 3-D requires Iy>0,G>0,J>0; optional alphaY/alphaZ",
   "outputs": "ElasticSection (Section); referenced by beam-column elements/integration",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.Fiber",
   "label": "Fiber",
   "composite": "os.sections",
   "signature": "Fiber(*, patches=(), fibers=(), layers=(), GJ=None) -> Fiber",
   "summary": "Block-emit fiber section composed of RectPatch/StraightLayer/FiberPoint value objects; auto-registered.",
   "file": "src/apeGmsh/opensees/section/fiber.py:180",
   "flow": [
    {
     "node": "apesees",
     "action": "_SectionNS builds+validates Fiber (>=1 patch/fiber/layer, GJ>0 if set)",
     "passes": "Fiber dataclass + value objects",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate section tag; unique patch/layer/fiber materials declared via dependencies()",
     "passes": "section tag + nested UniaxialMaterial refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit section_open then per patch/layer/fiber resolve_tag(material)→emitter.patch/layer/fiber, section_close",
     "passes": "tag int + resolved material tags",
     "to": "external"
    }
   ],
   "inputs": "patches:tuple[RectPatch], fibers:tuple[FiberPoint], layers:tuple[StraightLayer], GJ>0|None",
   "outputs": "Fiber (Section); composes UniaxialMaterials as dependencies",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.FiberPoint",
   "label": "FiberPoint",
   "composite": "os.sections",
   "signature": "FiberPoint(*, material, y, z, area) -> FiberPoint",
   "summary": "Value object describing one single fiber point of a Fiber section (not a Primitive, no tag).",
   "file": "src/apeGmsh/opensees/section/fiber.py:147",
   "flow": [
    {
     "node": "types",
     "action": "frozen value object holding a UniaxialMaterial ref + (y,z,area)",
     "passes": "FiberPoint into Fiber.fibers",
     "to": "os.sections"
    }
   ],
   "inputs": "material:UniaxialMaterial, y, z, area>0",
   "outputs": "FiberPoint value object (held by Fiber)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.LayeredShell",
   "label": "LayeredShell",
   "composite": "os.sections",
   "signature": "LayeredShell(*, layers: tuple[ShellLayer,...]) -> LayeredShell",
   "summary": "Stacked nDMaterial through-thickness shell section (section LayeredShell); auto-registered.",
   "file": "src/apeGmsh/opensees/section/plate.py:154",
   "flow": [
    {
     "node": "apesees",
     "action": "_SectionNS builds+validates (>=1 ShellLayer)",
     "passes": "LayeredShell dataclass + ShellLayers",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate section tag; unique layer nDMaterials declared via dependencies()",
     "passes": "section tag + nested NDMaterial refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolves each layer material via resolve_mat_tag → emitter.section('LayeredShell',tag,n,matTag,h,...)",
     "passes": "tag int + resolved nD tags + thicknesses",
     "to": "external"
    }
   ],
   "inputs": "layers:tuple[ShellLayer] (>=1)",
   "outputs": "LayeredShell (Section); composes NDMaterials as dependencies",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.LayeredShellFiberSection",
   "label": "LayeredShellFiberSection",
   "composite": "os.sections",
   "signature": "LayeredShellFiberSection(*, layers: tuple[ShellLayer,...]) -> LayeredShellFiberSection",
   "summary": "Fiber-based stacked nDMaterial shell section (section LayeredShellFiberSection); auto-registered.",
   "file": "src/apeGmsh/opensees/section/plate.py:189",
   "flow": [
    {
     "node": "apesees",
     "action": "_SectionNS builds+validates (>=1 ShellLayer)",
     "passes": "LayeredShellFiberSection dataclass + ShellLayers",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate section tag; layer nDMaterials declared via dependencies()",
     "passes": "section tag + nested NDMaterial refs",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolves layer materials → emitter.section('LayeredShellFiberSection',tag,n,matTag,h,...)",
     "passes": "tag int + resolved nD tags + thicknesses",
     "to": "external"
    }
   ],
   "inputs": "layers:tuple[ShellLayer] (>=1)",
   "outputs": "LayeredShellFiberSection (Section)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.RectPatch",
   "label": "RectPatch",
   "composite": "os.sections",
   "signature": "RectPatch(*, material, ny, nz, yI, zI, yJ, zJ) -> RectPatch",
   "summary": "Value object describing one rect patch cell of a Fiber section (not a Primitive, no tag).",
   "file": "src/apeGmsh/opensees/section/fiber.py:70",
   "flow": [
    {
     "node": "types",
     "action": "frozen value object holding a UniaxialMaterial ref + subdivision/corner coords",
     "passes": "RectPatch into Fiber.patches",
     "to": "os.sections"
    }
   ],
   "inputs": "material:UniaxialMaterial, ny>0, nz>0, yI,zI,yJ,zJ floats",
   "outputs": "RectPatch value object (held by Fiber)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.ShellLayer",
   "label": "ShellLayer",
   "composite": "os.sections",
   "signature": "ShellLayer(*, material, thickness) -> ShellLayer",
   "summary": "Value object: one nDMaterial layer of a LayeredShell/LayeredShellFiberSection (not a Primitive, no tag).",
   "file": "src/apeGmsh/opensees/section/plate.py:110",
   "flow": [
    {
     "node": "types",
     "action": "frozen value object holding an NDMaterial ref + thickness>0",
     "passes": "ShellLayer into LayeredShell(.layers)",
     "to": "os.sections"
    }
   ],
   "inputs": "material:NDMaterial, thickness>0",
   "outputs": "ShellLayer value object (held by layered shell sections)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.section.StraightLayer",
   "label": "StraightLayer",
   "composite": "os.sections",
   "signature": "StraightLayer(*, material, n_bars, area, yI, zI, yJ, zJ) -> StraightLayer",
   "summary": "Value object describing one straight bar layer of a Fiber section (not a Primitive, no tag).",
   "file": "src/apeGmsh/opensees/section/fiber.py:107",
   "flow": [
    {
     "node": "types",
     "action": "frozen value object holding a UniaxialMaterial ref + bar count/area/coords",
     "passes": "StraightLayer into Fiber.layers",
     "to": "os.sections"
    }
   ],
   "inputs": "material:UniaxialMaterial, n_bars>=1, area>0, yI,zI,yJ,zJ floats",
   "outputs": "StraightLayer value object (held by Fiber)",
   "reads": [],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.tag_for",
   "label": "tag_for",
   "composite": "os.registry",
   "signature": "tag_for(prim: Primitive) -> int|None",
   "summary": "Return a registered primitive's allocated tag, or None if not registered.",
   "file": "src/apeGmsh/opensees/apesees.py:808",
   "flow": [
    {
     "node": "apesees",
     "action": "delegate to TagAllocator.tag_for",
     "passes": "Primitive",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "look up id(prim)→tag map",
     "passes": "tag int | None",
     "to": "apesees"
    }
   ],
   "inputs": "prim:Primitive",
   "outputs": "int|None",
   "reads": [
    "os.registry"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.tcl",
   "label": "tcl",
   "composite": "apesees",
   "signature": "tcl(path: str, *, run: bool=False, bin: str|None=None) -> None",
   "summary": "Build and emit a Tcl deck to path; optionally subprocess the OpenSees binary.",
   "file": "src/apeGmsh/opensees/apesees.py:632",
   "flow": [
    {
     "node": "apesees",
     "action": "build() then drive TclEmitter",
     "passes": "BuiltModel",
     "to": "os.build"
    },
    {
     "node": "os.build",
     "action": "BuiltModel.emit fans primitives into TclEmitter lines",
     "passes": "emitter line list",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "write lines to path; optional subprocess([binary,path])",
     "passes": "Tcl text file",
     "to": "external"
    }
   ],
   "inputs": "path:str, run:bool, bin:str|None",
   "outputs": "None (writes Tcl file; may raise RuntimeError on subprocess failure)",
   "reads": [
    "femdata",
    "fem.nodes"
   ],
   "writes": [],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.time_series.Constant",
   "label": "Constant",
   "composite": "os.time_series",
   "signature": "Constant(*, factor: float=1.0) -> Constant",
   "summary": "Typed `timeSeries Constant tag [-factor f]` primitive — constant value factor for all time (sustained gravity loads).",
   "file": "src/apeGmsh/opensees/time_series/time_series.py:74",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Constant(factor=); ops._register allocates a timeSeries tag",
     "passes": "<Constant:TimeSeries primitive + tag int>",
     "to": "os.registry"
    },
    {
     "node": "os.time_series",
     "action": "_emit before patterns; emits emitter.timeSeries('Constant', tag[, '-factor', factor])",
     "passes": "<command:('Constant',tag:int,[float])>",
     "to": "os.emit"
    }
   ],
   "inputs": "factor:float (default 1.0)",
   "outputs": "a registered TimeSeries with a tag",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.time_series.Linear",
   "label": "Linear (timeSeries)",
   "composite": "os.time_series",
   "signature": "Linear(*, factor: float=1.0) -> Linear",
   "summary": "Typed `timeSeries Linear tag [-factor f]` primitive — linear ramp factor·t; gets a bridge-allocated tag patterns reference.",
   "file": "src/apeGmsh/opensees/time_series/time_series.py:49",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Linear(factor=) (ops.timeSeries.Linear or standalone P11); ops._register allocates a timeSeries tag",
     "passes": "<Linear:TimeSeries primitive + tag int>",
     "to": "os.registry"
    },
    {
     "node": "os.time_series",
     "action": "_emit in pre-element pass (topo-sorted before patterns); emits emitter.timeSeries('Linear', tag[, '-factor', factor])",
     "passes": "<command:('Linear',tag:int,[float])>",
     "to": "os.emit"
    }
   ],
   "inputs": "factor:float (default 1.0)",
   "outputs": "a registered TimeSeries with a tag (referenced by Pattern.dependencies)",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.time_series.Path",
   "label": "Path",
   "composite": "os.time_series",
   "signature": "Path(*, file: str|None=None, values: tuple[float,...]|None=None, time: tuple[float,...]|None=None, dt: float|None=None, factor: float=1.0, start_time: float=0.0, prepend_zero: bool=False) -> Path",
   "summary": "Typed `timeSeries Path tag (-filePath|-values) [-time|-dt][-factor][-startTime][-prependZero]` — explicit time-history (the ground-motion workhorse); validates file XOR values and time-info presence.",
   "file": "src/apeGmsh/opensees/time_series/time_series.py:98",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Path(file=|values=, dt=|time=); __post_init__ validates exclusivity + factor>0; ops._register allocates a timeSeries tag",
     "passes": "<Path:TimeSeries primitive + tag int>",
     "to": "os.registry"
    },
    {
     "node": "os.time_series",
     "action": "_emit before patterns; assembles flag args; emits emitter.timeSeries('Path', tag, *args)",
     "passes": "<command:('Path',tag:int,...)>",
     "to": "os.emit"
    }
   ],
   "inputs": "file XOR values; with values requires dt XOR time; factor>0; start_time; prepend_zero",
   "outputs": "a registered TimeSeries with a tag",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.time_series.Pulse",
   "label": "Pulse",
   "composite": "os.time_series",
   "signature": "Pulse(*, t_start: float, t_end: float, period: float, width: float, factor: float=1.0, shift: float=0.0, zero_shift: float=0.0) -> Pulse",
   "summary": "Typed `timeSeries Pulse tag tStart tEnd period width [-factor][-shift][-zeroShift]` — square-wave pulse train; width in (0,1) duty fraction validated.",
   "file": "src/apeGmsh/opensees/time_series/time_series.py:218",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Pulse(t_start=,t_end=,period=,width=); __post_init__ validates period>0/width∈(0,1)/t_end>t_start; ops._register allocates a timeSeries tag",
     "passes": "<Pulse:TimeSeries primitive + tag int>",
     "to": "os.registry"
    },
    {
     "node": "os.time_series",
     "action": "_emit before patterns; assembles args; emits emitter.timeSeries('Pulse', tag, t_start, t_end, period, width, ...)",
     "passes": "<command:('Pulse',tag:int,float,float,float,float,...)>",
     "to": "os.emit"
    }
   ],
   "inputs": "t_start, t_end>t_start, period>0, width∈(0,1), factor, shift, zero_shift",
   "outputs": "a registered TimeSeries with a tag",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.time_series.Trig",
   "label": "Trig",
   "composite": "os.time_series",
   "signature": "Trig(*, t_start: float, t_end: float, period: float, factor: float=1.0, shift: float=0.0, zero_shift: float=0.0) -> Trig",
   "summary": "Typed `timeSeries Trig tag tStart tEnd period [-factor][-shift][-zeroShift]` — sinusoid active in [t_start,t_end]; period>0 and t_end>t_start validated.",
   "file": "src/apeGmsh/opensees/time_series/time_series.py:172",
   "flow": [
    {
     "node": "apesees",
     "action": "user constructs Trig(t_start=,t_end=,period=); __post_init__ validates; ops._register allocates a timeSeries tag",
     "passes": "<Trig:TimeSeries primitive + tag int>",
     "to": "os.registry"
    },
    {
     "node": "os.time_series",
     "action": "_emit before patterns; assembles args; emits emitter.timeSeries('Trig', tag, t_start, t_end, period, ...)",
     "passes": "<command:('Trig',tag:int,float,float,float,...)>",
     "to": "os.emit"
    }
   ],
   "inputs": "t_start, t_end>t_start, period>0, factor, shift, zero_shift",
   "outputs": "a registered TimeSeries with a tag",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.emit"
   ],
   "_cluster": "G_os_emit"
  },
  {
   "id": "apeSees.uniaxialMaterial.ASDSteel1D",
   "label": "ASDSteel1D",
   "composite": "os.materials",
   "signature": "ASDSteel1D(*, E, sy, su, eu, implex=False, implex_control=None, auto_regularization=False, buckling_lch=None, fracture=False, slip_material=None, radius=None, K_alpha=None, max_iter=None, tolU=None, tolR=None) -> ASDSteel1D",
   "summary": "ASDEA plastic-damage steel bar with optional buckling/fracture/slip RVE features; auto-registered.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:158",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate ASDSteel1D dataclass (su>sy etc.)",
     "passes": "ASDSteel1D dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate uniaxialMaterial tag; slip_material declared via dependencies()",
     "passes": "tag int + nested slip mat",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit resolves slip_material tag via resolve_tag, emits flags",
     "passes": "tag int + flag args + slip tag",
     "to": "external"
    }
   ],
   "inputs": "E>0, sy>0, su>sy>0, eu>0, optional RVE feature flags + slip_material UniaxialMaterial",
   "outputs": "ASDSteel1D (UniaxialMaterial); slip_material is its only dependency",
   "reads": [
    "os.registry"
   ],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.uniaxialMaterial.Concrete01",
   "label": "Concrete01",
   "composite": "os.materials",
   "signature": "Concrete01(*, fpc, epsc0, fpcu, epsU) -> Concrete01",
   "summary": "Kent-Scott-Park concrete with no tensile strength (all params compression-negative); auto-registered.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:304",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate Concrete01 (all<0, epsU<epsc0)",
     "passes": "Concrete01 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate uniaxialMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.uniaxialMaterial('Concrete01',tag,fpc,epsc0,fpcu,epsU)",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "fpc<0, epsc0<0, fpcu<0, epsU<epsc0<0",
   "outputs": "Concrete01 (UniaxialMaterial)",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.uniaxialMaterial.Concrete02",
   "label": "Concrete02",
   "composite": "os.materials",
   "signature": "Concrete02(*, fpc, epsc0, fpcu, epsU, lambda_val, ft, Ets) -> Concrete02",
   "summary": "Kent-Scott-Park concrete with tensile strength (lambda field is lambda_val); auto-registered.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:355",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate Concrete02 (compression-neg, lambda∈[0,1], ft>0, Ets>0)",
     "passes": "Concrete02 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate uniaxialMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.uniaxialMaterial('Concrete02',tag,fpc,epsc0,fpcu,epsU,lambda,ft,Ets)",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "fpc<0, epsc0<0, fpcu<0, epsU<epsc0<0, lambda_val∈[0,1], ft>0, Ets>0",
   "outputs": "Concrete02 (UniaxialMaterial)",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.uniaxialMaterial.ENT",
   "label": "ENT",
   "composite": "os.materials",
   "signature": "ENT(*, E) -> ENT",
   "summary": "Elastic-no-tension uniaxial material (uniaxialMaterial ENT); auto-registered.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:622",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate ENT (E>0)",
     "passes": "ENT dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate uniaxialMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.uniaxialMaterial('ENT',tag,E)",
     "passes": "tag int + E",
     "to": "external"
    }
   ],
   "inputs": "E>0",
   "outputs": "ENT (UniaxialMaterial)",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.uniaxialMaterial.ElasticMaterial",
   "label": "ElasticMaterial",
   "composite": "os.materials",
   "signature": "ElasticMaterial(*, E, eta=0.0) -> ElasticMaterial",
   "summary": "Linear-elastic uniaxial material with optional damping (emits OpenSees token 'Elastic'); auto-registered.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:585",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate ElasticMaterial (E>0, eta>=0)",
     "passes": "ElasticMaterial dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate uniaxialMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.uniaxialMaterial('Elastic',tag,E[,eta])",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "E>0, eta>=0",
   "outputs": "ElasticMaterial (UniaxialMaterial); Python name differs from OpenSees token 'Elastic'",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.uniaxialMaterial.Hysteretic",
   "label": "Hysteretic",
   "composite": "os.materials",
   "signature": "Hysteretic(*, s1p,e1p,s2p,e2p,s1n,e1n,s2n,e2n,pinch_x,pinch_y,damage1,damage2, s3p=None,e3p=None,s3n=None,e3n=None, beta=0.0) -> Hysteretic",
   "summary": "Multi-linear hysteretic backbone with pinching/damage (uniaxialMaterial Hysteretic); auto-registered.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:428",
   "flow": [
    {
     "node": "apesees",
     "action": "build+validate Hysteretic (monotone backbone, pinch∈[0,1], all-or-none 3rd point)",
     "passes": "Hysteretic dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate uniaxialMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit assembles ordered backbone params + optional beta",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "positive p-backbone, negative n-backbone, pinch_x/y∈[0,1], damage>=0, optional 3rd-point quad + beta",
   "outputs": "Hysteretic (UniaxialMaterial)",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.uniaxialMaterial.Steel01",
   "label": "Steel01",
   "composite": "os.materials",
   "signature": "Steel01(*, fy, E, b, a1=None, a2=None, a3=None, a4=None) -> Steel01",
   "summary": "Bilinear steel with kinematic hardening (uniaxialMaterial Steel01); auto-registered with the bridge.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:41",
   "flow": [
    {
     "node": "apesees",
     "action": "_UniaxialMaterialNS builds Steel01 dataclass, validates fy/E/b in __post_init__",
     "passes": "Steel01 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "TagAllocator.allocate_for kind 'uniaxialMaterial', append to _primitives",
     "passes": "uniaxialMaterial tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.uniaxialMaterial('Steel01',tag,fy,E,b[,a1..a4])",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "fy>0, E>0, b∈[0,1), optional a1..a4 quad",
   "outputs": "Steel01 (UniaxialMaterial); referenced by Fiber/Truss/ZeroLength via dependencies()",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apeSees.uniaxialMaterial.Steel02",
   "label": "Steel02",
   "composite": "os.materials",
   "signature": "Steel02(*, fy, E, b, R0=20.0, cR1=0.925, cR2=0.15, a1=None..a4=None, sig_init=None) -> Steel02",
   "summary": "Menegotto-Pinto steel with isotropic hardening (uniaxialMaterial Steel02); auto-registered.",
   "file": "src/apeGmsh/opensees/material/uniaxial.py:93",
   "flow": [
    {
     "node": "apesees",
     "action": "_UniaxialMaterialNS builds+validates Steel02 dataclass",
     "passes": "Steel02 dataclass",
     "to": "os.registry"
    },
    {
     "node": "os.registry",
     "action": "allocate uniaxialMaterial tag, register",
     "passes": "tag int",
     "to": "os.build"
    },
    {
     "node": "os.emit",
     "action": "_emit → emitter.uniaxialMaterial('Steel02',tag,fy,E,b,R0,cR1,cR2[,a1..a4[,sigInit]])",
     "passes": "tag int + params",
     "to": "external"
    }
   ],
   "inputs": "fy>0, E>0, b∈[0,1), R0>0, optional a1..a4 quad + sig_init",
   "outputs": "Steel02 (UniaxialMaterial)",
   "reads": [],
   "writes": [
    "os.registry"
   ],
   "_cluster": "F_os_prim"
  },
  {
   "id": "apegmsh.exports",
   "label": "top-level exports",
   "composite": "apegmsh",
   "signature": "from apeGmsh import apeGmsh, Part, FEMData, Results, Selection, ModelViewer, MeshViewer, ResultsViewer, preview, workdir, ... (see __all__)",
   "summary": "Package facade — apeGmsh/__init__.py re-exports the public classes/functions (apeGmsh session, Part/PartsRegistry/Instance, ConstraintsComposite, FEMData/MeshInfo, mesh algorithm enums, MshLoader, Results, Numberer, Selection/SelectionComposite, the three viewers + SelectionPicker alias, Constraints module, settings/theme_editor, preview, workdir) and computes __version__ + prints the import banner (suppress with APEGMSH_QUIET=1).",
   "file": "src/apeGmsh/__init__.py:163",
   "flow": [
    {
     "node": "apegmsh",
     "action": "import & bind public symbols into package namespace, resolve __version__, print banner",
     "passes": "class/function objects",
     "to": "session"
    }
   ],
   "inputs": "(import time)",
   "outputs": "public names: apeGmsh, Part, FEMData, Results, Selection, viewers, preview, workdir, __version__, ...",
   "reads": [
    "pyproject.toml or importlib.metadata for version"
   ],
   "writes": [
    "banner to stderr unless APEGMSH_QUIET set"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "apegmsh.workdir",
   "label": "workdir",
   "composite": "apegmsh",
   "signature": "workdir(name: str|Path='outputs') -> Path",
   "summary": "from apeGmsh import workdir — return Path(name) after mkdir(parents=True, exist_ok=True); the example-script convention for a self-contained per-script outputs/ folder.",
   "file": "src/apeGmsh/_workdir.py:7",
   "flow": [
    {
     "node": "apegmsh",
     "action": "Path(name).mkdir(parents,exist_ok) then return",
     "passes": "Path",
     "to": "session"
    }
   ],
   "inputs": "directory name/path",
   "outputs": "Path to the ensured directory",
   "reads": [],
   "writes": [
    "creates output directory on disk"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "chain.SelectionChain",
   "label": "SelectionChain",
   "composite": "chain",
   "signature": "class SelectionChain (FAMILY ClassVar; in_box/in_sphere/on_plane/nearest_to/where; union/intersect/difference/symmetric_difference + |&-^; result(); __init_subclass__ enforcement)",
   "summary": "Generic stdlib-only fluent-selection mixin shared by the geometry/mesh/broker/results selection chains. Carries an ordered de-duplicated atom tuple + opaque engine; refining/set-algebra verbs return the same concrete subclass (covariant); __init_subclass__ enforces verb names/hooks/FAMILY at import. Spatial behaviour delegated to per-family abstract hooks; result() materializes the domain terminal.",
   "file": "src/apeGmsh/_chain.py:60",
   "flow": [
    {
     "node": "chain",
     "action": "refining verb delegates to subclass spatial hook, _wrap new same-type chain",
     "passes": "atom tuple (node ids / element ids / DimTags)",
     "to": "chain"
    },
    {
     "node": "chain",
     "action": "set algebra (_compatible guard) combines atom tuples one dedup law",
     "passes": "atom tuple",
     "to": "chain"
    },
    {
     "node": "chain",
     "action": "result() calls subclass _materialize",
     "passes": "domain terminal value",
     "to": "selection"
    }
   ],
   "inputs": "atoms iterable + opaque _engine (set by concrete subclass)",
   "outputs": "covariant chain instances; result() → domain-specific terminal",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.FEMData.__init__",
   "label": "FEMData(...)",
   "composite": "femdata",
   "signature": "FEMData(nodes: NodeComposite, elements: ElementComposite, info: MeshInfo, mesh_selection: MeshSelectionStore | None = None)",
   "summary": "Direct constructor: stores the three composites, builds InspectComposite, and wires the sibling NodeComposite onto the ElementComposite so element-chain centroids work in-memory.",
   "file": "src/apeGmsh/mesh/FEMData.py:1257",
   "flow": [
    {
     "node": "femdata",
     "action": "stores nodes/elements/info, builds fem.inspect",
     "passes": "NodeComposite, ElementComposite, MeshInfo, MeshSelectionStore|None",
     "to": "femdata"
    },
    {
     "node": "femdata",
     "action": "wires elements._apegmsh_nodes_ref = nodes (centroid back-ref contract for ElementChain)",
     "passes": "NodeComposite ref",
     "to": "fem.elements"
    }
   ],
   "inputs": "NodeComposite, ElementComposite, MeshInfo, optional MeshSelectionStore",
   "outputs": "FEMData broker instance (.nodes/.elements/.info/.inspect/.mesh_selection)",
   "reads": [
    "NodeComposite",
    "ElementComposite",
    "MeshInfo"
   ],
   "writes": [
    "self.nodes",
    "self.elements",
    "self.info",
    "self.inspect",
    "self.mesh_selection",
    "elements._apegmsh_nodes_ref"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.__repr__",
   "label": "repr(fem)",
   "composite": "femdata",
   "signature": "__repr__() -> str",
   "summary": "Returns fem.inspect.summary() — the multi-line mesh + sub-composite breakdown.",
   "file": "src/apeGmsh/mesh/FEMData.py:1448",
   "flow": [
    {
     "node": "femdata",
     "action": "return self.inspect.summary()",
     "passes": "summary str",
     "to": "fem.inspect"
    }
   ],
   "inputs": "none",
   "outputs": "multi-line summary string",
   "reads": [
    "fem.inspect"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.elements",
   "label": "elements",
   "composite": "femdata",
   "signature": "attribute elements -> ElementComposite",
   "summary": "The ElementComposite — per-type element groups, selection chain, and constraints/loads sub-composites.",
   "file": "src/apeGmsh/mesh/FEMData.py:1265",
   "flow": [
    {
     "node": "femdata",
     "action": "expose stored ElementComposite",
     "passes": "ElementComposite",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ElementComposite",
   "reads": [
    "self.elements"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.from_gmsh",
   "label": "from_gmsh",
   "composite": "femdata",
   "signature": "classmethod from_gmsh(dim: int | None = None, *, session=None, ndf: int = 6, remove_orphans: bool = False) -> FEMData",
   "summary": "Extract a solver-ready FEMData from the live Gmsh session, auto-resolving constraints/loads/masses when a session is supplied.",
   "file": "src/apeGmsh/mesh/FEMData.py:1301",
   "flow": [
    {
     "node": "femdata",
     "action": "delegates to _from_gmsh(cls, dim, session, ndf, remove_orphans)",
     "passes": "cls, dim:int|None, session, ndf:int, remove_orphans:bool",
     "to": "fem.extract"
    },
    {
     "node": "fem.extract",
     "action": "_extract_mesh_core → extract_raw/extract_physical_groups/extract_labels/extract_partitions pull node_tags/coords + per-type groups from gmsh",
     "passes": "gmsh.model.mesh.getNodes/getElements arrays",
     "to": "gmsh"
    },
    {
     "node": "fem.extract",
     "action": "if session: build_node_map/build_face_map then resolve constraints/loads/masses",
     "passes": "node_ids:int64(N,), node_coords:float64(N,3), elem_tags, flat_conn",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "constraints_comp.resolve(...) + resolve_bcs(...)",
     "passes": "list[ConstraintRecord], list[SPRecord]",
     "to": "fem.extract"
    },
    {
     "node": "load.resolver",
     "action": "loads_comp.resolve(...) split into nodal/element/sp",
     "passes": "list[NodalLoadRecord/ElementLoadRecord/SPRecord]",
     "to": "fem.extract"
    },
    {
     "node": "mass.resolver",
     "action": "masses_comp.resolve(node_ids, coords, ndf=)",
     "passes": "list[MassRecord]",
     "to": "fem.extract"
    },
    {
     "node": "fem.extract",
     "action": "build NodeComposite/ElementComposite/MeshInfo, snapshot parts maps + mesh_selection",
     "passes": "NodeComposite, ElementComposite, MeshInfo, MeshSelectionStore|None",
     "to": "femdata"
    }
   ],
   "inputs": "dim (None=all), session (for BC resolution), ndf, remove_orphans",
   "outputs": "fully-populated FEMData snapshot",
   "reads": [
    "gmsh.model.mesh",
    "session.constraints",
    "session.loads",
    "session.masses",
    "session.parts",
    "session.mesh_selection"
   ],
   "writes": [
    "NodeComposite",
    "ElementComposite",
    "MeshInfo"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.from_h5",
   "label": "from_h5",
   "composite": "femdata.h5",
   "signature": "classmethod from_h5(path: str) -> FEMData",
   "summary": "Inverse of to_h5: read the root-layout model.h5 neutral zone (nodes, per-type elements, PGs, labels, mesh_selections, constraints, loads, masses) plus /meta and rebuild a fully-populated FEMData.",
   "file": "src/apeGmsh/mesh/FEMData.py:1341",
   "flow": [
    {
     "node": "femdata",
     "action": "delegates to read_fem_h5(path)",
     "passes": "path:str",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "h5py.File open; check /meta schema_version major==2 (else SchemaVersionError/MalformedH5Error)",
     "passes": "schema_version:str",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "read /nodes ids+coords, /elements/{type} ids+connectivity, /physical_groups, /labels, /mesh_selections, /constraints, /loads, /masses",
     "passes": "int64 ids, float64(N,3) coords, ElementGroup dicts, record lists",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "assemble NodeComposite/ElementComposite/MeshInfo + MeshSelectionStore, build FEMData",
     "passes": "NodeComposite, ElementComposite, MeshInfo",
     "to": "femdata"
    }
   ],
   "inputs": "path to a root-layout model.h5",
   "outputs": "reconstructed FEMData (constraints/loads/masses round-tripped; bandwidth=0)",
   "reads": [
    "model.h5 /meta",
    "/nodes",
    "/elements",
    "/physical_groups",
    "/labels",
    "/mesh_selections",
    "/constraints",
    "/loads",
    "/masses"
   ],
   "writes": [
    "NodeComposite",
    "ElementComposite",
    "MeshInfo"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.from_mpco_model",
   "label": "from_mpco_model",
   "composite": "femdata.h5",
   "signature": "classmethod from_mpco_model(group) -> FEMData",
   "summary": "Synthesize a partial FEMData from an MPCO MODEL/ group (nodes, elements by OpenSees class tag, PGs from MPCO Regions); snapshot_id will not match a native FEMData.",
   "file": "src/apeGmsh/mesh/FEMData.py:1413",
   "flow": [
    {
     "node": "femdata",
     "action": "delegates to read_fem_from_mpco(group)",
     "passes": "h5py.Group (MPCO MODEL/)",
     "to": "external",
     "to_raw": "_femdata_mpco_io"
    },
    {
     "node": "external",
     "action": "module mesh/_femdata_mpco_io.py builds nodes/elements/PGs from MPCO sets (outside Cluster E scope)",
     "passes": "FEMData",
     "to": "femdata",
     "node_raw": "_femdata_mpco_io"
    }
   ],
   "inputs": "open h5py group (MPCO MODEL/)",
   "outputs": "partial FEMData (no apeGmsh labels, no pre-mesh declarations); intentional snapshot_id mismatch",
   "reads": [
    "MPCO MODEL/ group"
   ],
   "writes": [
    "NodeComposite",
    "ElementComposite",
    "MeshInfo"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.from_msh",
   "label": "from_msh",
   "composite": "femdata",
   "signature": "classmethod from_msh(path: str, dim: int | None = 2, *, remove_orphans: bool = False) -> FEMData",
   "summary": "Load a FEMData from an external .msh file by initializing Gmsh, merging the file, and running the shared extraction core (no BC resolution).",
   "file": "src/apeGmsh/mesh/FEMData.py:1328",
   "flow": [
    {
     "node": "femdata",
     "action": "delegates to _from_msh(cls, path, dim, remove_orphans)",
     "passes": "cls, path:str, dim:int|None, remove_orphans:bool",
     "to": "loader"
    },
    {
     "node": "loader",
     "action": "gmsh.initialize/merge(path) then _extract_mesh_core(dim)",
     "passes": ".msh path string",
     "to": "gmsh"
    },
    {
     "node": "fem.extract",
     "action": "extract_raw/PG/labels/partitions → groups + node arrays",
     "passes": "node_tags:int64, node_coords:float64(N,3), groups dict",
     "to": "loader"
    },
    {
     "node": "loader",
     "action": "build NodeComposite/ElementComposite/MeshInfo, gmsh.finalize()",
     "passes": "NodeComposite, ElementComposite, MeshInfo",
     "to": "femdata"
    }
   ],
   "inputs": "path to .msh, dim, remove_orphans",
   "outputs": "FEMData snapshot (no constraints/loads/masses — file has none)",
   "reads": [
    "gmsh",
    ".msh file"
   ],
   "writes": [
    "NodeComposite",
    "ElementComposite",
    "MeshInfo"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.from_native_h5",
   "label": "from_native_h5",
   "composite": "femdata.h5",
   "signature": "classmethod from_native_h5(group) -> FEMData",
   "summary": "Reconstruct a FEMData from its embedded /model/ group (nodes, per-type elements, PGs, labels, mesh_selection); loads/masses/constraints are not round-tripped.",
   "file": "src/apeGmsh/mesh/FEMData.py:1401",
   "flow": [
    {
     "node": "femdata",
     "action": "delegates to read_fem_from_h5(group)",
     "passes": "h5py.Group",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "read /model/nodes + /model/elements + named groups, build composites; snapshot_id matches the embedded one",
     "passes": "int64 ids, float64(N,3) coords, ElementGroup dict",
     "to": "femdata"
    }
   ],
   "inputs": "open h5py group (/model/)",
   "outputs": "FEMData with empty BC composites; snapshot_id parity for Results.bind()",
   "reads": [
    "/model/ HDF5 sub-group"
   ],
   "writes": [
    "NodeComposite",
    "ElementComposite",
    "MeshInfo"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.info",
   "label": "info",
   "composite": "femdata",
   "signature": "attribute info -> MeshInfo",
   "summary": "The MeshInfo — read-only mesh statistics (n_nodes, n_elems, bandwidth, types).",
   "file": "src/apeGmsh/mesh/FEMData.py:1266",
   "flow": [
    {
     "node": "femdata",
     "action": "expose stored MeshInfo",
     "passes": "MeshInfo",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "MeshInfo",
   "reads": [
    "self.info"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.inspect",
   "label": "inspect",
   "composite": "femdata",
   "signature": "attribute inspect -> InspectComposite",
   "summary": "The InspectComposite — summaries and DataFrame tables for nodes/elements/PGs/labels/constraints/loads/masses.",
   "file": "src/apeGmsh/mesh/FEMData.py:1268",
   "flow": [
    {
     "node": "femdata",
     "action": "expose InspectComposite(self) built in __init__",
     "passes": "InspectComposite",
     "to": "fem.inspect"
    }
   ],
   "inputs": "none",
   "outputs": "InspectComposite",
   "reads": [
    "self.inspect"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.mesh_selection",
   "label": "mesh_selection",
   "composite": "femdata",
   "signature": "attribute mesh_selection -> MeshSelectionStore | None",
   "summary": "Snapshot of post-mesh selection sets captured at get_fem_data() time (None if no selections); round-tripped through model.h5.",
   "file": "src/apeGmsh/mesh/FEMData.py:1267",
   "flow": [
    {
     "node": "femdata",
     "action": "expose stored MeshSelectionStore (or None)",
     "passes": "MeshSelectionStore|None",
     "to": "femdata"
    }
   ],
   "inputs": "none",
   "outputs": "MeshSelectionStore or None",
   "reads": [
    "self.mesh_selection"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.nodes",
   "label": "nodes",
   "composite": "femdata",
   "signature": "attribute nodes -> NodeComposite",
   "summary": "The NodeComposite — IDs/coords access, selection chain, and constraints/loads/sp/masses sub-composites.",
   "file": "src/apeGmsh/mesh/FEMData.py:1264",
   "flow": [
    {
     "node": "femdata",
     "action": "expose stored NodeComposite",
     "passes": "NodeComposite",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "NodeComposite",
   "reads": [
    "self.nodes"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.partitions",
   "label": "partitions",
   "composite": "femdata",
   "signature": "property partitions -> list[int]",
   "summary": "Sorted list of partition IDs (delegates to fem.nodes.partitions; empty if the mesh is not partitioned).",
   "file": "src/apeGmsh/mesh/FEMData.py:1279",
   "flow": [
    {
     "node": "femdata",
     "action": "return self.nodes.partitions",
     "passes": "list[int]",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "sorted list of partition IDs",
   "reads": [
    "fem.nodes._partitions"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.snapshot_id",
   "label": "snapshot_id",
   "composite": "femdata",
   "signature": "property snapshot_id -> str",
   "summary": "Deterministic 32-hex blake2b content hash over sorted nodes (ids+coords), per-type elements (ids+conn), and physical-group membership; computed once and cached; the binding contract for the Results module.",
   "file": "src/apeGmsh/mesh/FEMData.py:1285",
   "flow": [
    {
     "node": "femdata",
     "action": "return _snapshot_id_cache if set, else compute_snapshot_id(self)",
     "passes": "FEMData",
     "to": "external",
     "to_raw": "_femdata_hash"
    },
    {
     "node": "external",
     "action": "_hash_nodes (argsort node ids, tobytes coords) + _hash_elements (type-name order, argsort eids+conn) + _hash_physical_groups (sorted (dim,tag), sorted member nids) → blake2b.hexdigest()",
     "passes": "blake2b digest -> 32-char hex string",
     "to": "femdata",
     "node_raw": "_femdata_hash"
    },
    {
     "node": "femdata",
     "action": "cache digest on self._snapshot_id_cache",
     "passes": "hash string",
     "to": "femdata"
    }
   ],
   "inputs": "none (reads own nodes/elements/physical)",
   "outputs": "32-char hex digest string",
   "reads": [
    "fem.nodes.ids",
    "fem.nodes.coords",
    "fem.elements groups",
    "fem.nodes.physical"
   ],
   "writes": [
    "self._snapshot_id_cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.to_h5",
   "label": "to_h5",
   "composite": "femdata.h5",
   "signature": "to_h5(path: str, *, model_name: str = '', apegmsh_version: str = '', ndf: int = 0) -> None",
   "summary": "Write a fresh root-level model.h5 containing only the neutral zone (no /opensees) — everything the broker knows about the model.",
   "file": "src/apeGmsh/mesh/FEMData.py:1374",
   "flow": [
    {
     "node": "femdata",
     "action": "delegates to write_fem_h5(self, path, model_name, apegmsh_version, ndf)",
     "passes": "FEMData, path:str, model_name, ndf:int",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "h5py.File('w'); write_meta stamps schema_version 2.4.0 + snapshot_id; write_neutral_zone writes 8 groups",
     "passes": "snapshot_id hash str, schema_version str",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "_write_nodes/_write_elements/_write_physical_groups/_write_labels/_write_mesh_selections/_write_constraints/_write_loads/_write_masses",
     "passes": "int64/float64 datasets, symmetric-compound record rows",
     "to": "femdata.h5"
    }
   ],
   "inputs": "path, optional model_name/apegmsh_version/ndf",
   "outputs": "model.h5 file on disk (neutral zone only)",
   "reads": [
    "fem.nodes",
    "fem.elements",
    "fem.snapshot_id",
    "fem.mesh_selection"
   ],
   "writes": [
    "model.h5 /meta + neutral zone"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.to_native_h5",
   "label": "to_native_h5",
   "composite": "femdata.h5",
   "signature": "to_native_h5(group) -> None",
   "summary": "Embed this FEMData into an open HDF5 /model/ sub-group (used by NativeWriter to snapshot geometry alongside results); reconstruction yields the same snapshot_id.",
   "file": "src/apeGmsh/mesh/FEMData.py:1363",
   "flow": [
    {
     "node": "femdata",
     "action": "delegates to write_fem_to_h5(self, group)",
     "passes": "FEMData, h5py.Group",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "_write_attrs(snapshot_id/ndm/ndf), _write_nodes, _write_elements, _write_mesh_selection under /model/",
     "passes": "snapshot_id str, int64 ids, float64 coords, per-type groups",
     "to": "femdata.h5"
    }
   ],
   "inputs": "open h5py group (/model/)",
   "outputs": "FEMData embedded in the group (nodes, per-type elements, PGs, labels, mesh_selection)",
   "reads": [
    "fem.snapshot_id",
    "fem.nodes",
    "fem.elements",
    "fem.mesh_selection"
   ],
   "writes": [
    "/model/ HDF5 sub-group"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.FEMData.viewer",
   "label": "viewer",
   "composite": "femdata",
   "signature": "viewer(*, blocking: bool = False) -> None",
   "summary": "Currently disabled — raises NotImplementedError; the legacy Results.from_fem viewer path was removed and the replacement is part of the viewer rebuild project.",
   "file": "src/apeGmsh/mesh/FEMData.py:1433",
   "flow": [
    {
     "node": "femdata",
     "action": "raise NotImplementedError (use g.mesh.viewer() instead)",
     "passes": "NotImplementedError",
     "to": "femdata"
    }
   ],
   "inputs": "blocking flag (ignored)",
   "outputs": "raises NotImplementedError",
   "reads": [],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.__iter__",
   "label": "iter(fem.elements)",
   "composite": "fem.elements",
   "signature": "__iter__() -> Iterator[ElementGroup]",
   "summary": "Iterate the per-type ElementGroup objects (one per element type).",
   "file": "src/apeGmsh/mesh/FEMData.py:665",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return iter(self._groups.values())",
     "passes": "Iterator[ElementGroup]",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of ElementGroup",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.__len__",
   "label": "len(fem.elements)",
   "composite": "fem.elements",
   "signature": "__len__() -> int",
   "summary": "Total element count across all per-type groups.",
   "file": "src/apeGmsh/mesh/FEMData.py:669",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return sum(len(g) for g in _groups.values())",
     "passes": "int",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "element count int",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.__repr__",
   "label": "repr(fem.elements)",
   "composite": "fem.elements",
   "signature": "__repr__() -> str",
   "summary": "ElementComposite summary listing per-type counts plus non-empty constraints/loads reprs.",
   "file": "src/apeGmsh/mesh/FEMData.py:1038",
   "flow": [
    {
     "node": "fem.elements",
     "action": "format per-type counts + sub-composite reprs",
     "passes": "str",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "summary string",
   "reads": [
    "ElementComposite._groups",
    "constraints",
    "loads"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.connectivity",
   "label": "connectivity",
   "composite": "fem.elements",
   "signature": "property connectivity -> ndarray",
   "summary": "Flat connectivity ndarray — only valid for a single-type mesh; raises TypeError when multiple element types are present.",
   "file": "src/apeGmsh/mesh/FEMData.py:689",
   "flow": [
    {
     "node": "fem.elements",
     "action": "if 1 group return its connectivity, else raise TypeError",
     "passes": "ndarray(N,npe) int64 | TypeError",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,npe) int64 (single-type only)",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.constraints",
   "label": "constraints",
   "composite": "fem.elements",
   "signature": "attribute constraints -> SurfaceConstraintSet",
   "summary": "Surface coupling constraint records (tie, distributing, embedded, tied_contact, mortar) with atomic/compound iterators.",
   "file": "src/apeGmsh/mesh/FEMData.py:649",
   "flow": [
    {
     "node": "fem.elements",
     "action": "SurfaceConstraintSet(surface_constraints) from resolver split",
     "passes": "list[ConstraintRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "SurfaceConstraintSet",
   "reads": [
    "ElementComposite.constraints"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.constraints.by_kind",
   "label": "SurfaceConstraintSet.by_kind",
   "composite": "records",
   "signature": "by_kind(kind: str) -> list[ConstraintRecord]",
   "summary": "_RecordSetBase filter on fem.elements.constraints: returns records whose .kind matches (tie/embedded/tied_contact/mortar etc.), compound records not expanded.",
   "file": "src/apeGmsh/mesh/_record_set.py:76",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.constraints",
     "passes": "SurfaceConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter self._records by kind",
     "passes": "list[ConstraintRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "kind (str, e.g. Kind.TIE)",
   "outputs": "list[ConstraintRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.constraints.couplings",
   "label": "SurfaceConstraintSet.couplings",
   "composite": "records",
   "signature": "couplings() -> Iterator[SurfaceCouplingRecord]",
   "summary": "Yields top-level SurfaceCouplingRecord objects on fem.elements.constraints (tied_contact, mortar) without expanding slave_records, preserving compound-only fields like mortar_operator.",
   "file": "src/apeGmsh/mesh/_record_set.py:835",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.constraints",
     "passes": "SurfaceConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter isinstance SurfaceCouplingRecord",
     "passes": "Iterator[SurfaceCouplingRecord(slave_records,mortar_operator,master_nodes,slave_nodes,dofs)]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "Iterator[SurfaceCouplingRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.constraints.interpolations",
   "label": "SurfaceConstraintSet.interpolations",
   "composite": "records",
   "signature": "interpolations() -> Iterator[InterpolationRecord]",
   "summary": "Yields InterpolationRecords on fem.elements.constraints (tie, distributing, embedded) plus the slave_records inside SurfaceCouplingRecord (tied_contact/mortar) expanded automatically.",
   "file": "src/apeGmsh/mesh/_record_set.py:818",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.constraints (SurfaceConstraintSet)",
     "passes": "SurfaceConstraintSet over list[ConstraintRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "yield InterpolationRecord directly; SurfaceCouplingRecord.slave_records expanded",
     "passes": "Iterator[InterpolationRecord(slave_node,master_nodes,weights,dofs,projected_point,parametric_coords)]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "Iterator[InterpolationRecord] (coupling records expanded)",
   "reads": [
    "self._records",
    "SurfaceCouplingRecord.slave_records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.constraints.iter_index",
   "label": "SurfaceConstraintSet.__iter__/__getitem__/__len__/__bool__/Kind",
   "composite": "records",
   "signature": "__iter__() -> Iterator[ConstraintRecord]; __getitem__(idx); __len__(); __bool__(); Kind = ConstraintKind",
   "summary": "_RecordSetBase dunders + Kind constant on fem.elements.constraints: direct iteration yields mixed InterpolationRecord/SurfaceCouplingRecord (not expanded), plus indexing/length/truthiness and the ConstraintKind class.",
   "file": "src/apeGmsh/mesh/_record_set.py:816",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.constraints",
     "passes": "SurfaceConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "iter/index/len/bool over raw mixed records; .Kind exposes ConstraintKind",
     "passes": "ConstraintRecord(s)/int/bool/ConstraintKind",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "idx for __getitem__",
   "outputs": "mixed ConstraintRecord(s), int, bool, ConstraintKind",
   "reads": [
    "self._records",
    "ConstraintKind"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.constraints.summary",
   "label": "SurfaceConstraintSet.summary",
   "composite": "records",
   "signature": "summary() -> pandas.DataFrame",
   "summary": "Returns a DataFrame indexed by kind with columns count and n_interpolations (SurfaceCouplingRecord counted by slave_records length) for the resolved surface-constraint set.",
   "file": "src/apeGmsh/mesh/_record_set.py:847",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.constraints",
     "passes": "SurfaceConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "tally count/n_interpolations per kind",
     "passes": "DataFrame[kind,count,n_interpolations]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "pandas.DataFrame summary of surface constraints",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.get",
   "label": "get",
   "composite": "fem.elements",
   "signature": "get(target=None, *, pg=None, label=None, tag=None, dim: int | None = None, element_type: str | int | None = None, partition: int | None = None) -> GroupResult",
   "summary": "Select elements by PG/label/tag/target plus dim/element_type/partition (all AND-intersected) and return a GroupResult of per-type ElementGroup blocks.",
   "file": "src/apeGmsh/mesh/FEMData.py:746",
   "flow": [
    {
     "node": "fem.elements",
     "action": "_resolve_elem_ids dispatches tag/pg/label/target → id set or None",
     "passes": "selector str/list/(dim,tag)",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "NamedGroupSet.element_ids(name) per item, union into set[int]",
     "passes": "element_ids object ndarray",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "target string falls through label→PG→part_elem_map ((KeyError,ValueError) swallow); raw (dim,tag) → live gmsh getElements",
     "passes": "set[int] / DimTag",
     "to": "gmsh"
    },
    {
     "node": "fem.elements",
     "action": "partition intersect; resolve element_type via resolve_type_filter; per-group dim/type/np.isin id mask → ElementGroup blocks",
     "passes": "type_codes set[int], np.isin mask",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "wrap filtered ElementGroups in GroupResult",
     "passes": "list[ElementGroup]",
     "to": "fem.elements"
    }
   ],
   "inputs": "target/pg/label/tag/dim/element_type/partition",
   "outputs": "GroupResult (iterable ElementGroup; .ids/.connectivity/.resolve())",
   "reads": [
    "ElementComposite._groups",
    "fem.elements.physical/labels",
    "_part_elem_map",
    "_partitions",
    "gmsh (raw DimTag only)"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.get_ids",
   "label": "get_ids",
   "composite": "fem.elements",
   "signature": "get_ids(target=None, *, pg=None, label=None, tag=None, dim=None, element_type=None, partition=None) -> ndarray",
   "summary": "Same selection as get() but returns only the concatenated element-ID array (.get(...).ids).",
   "file": "src/apeGmsh/mesh/FEMData.py:908",
   "flow": [
    {
     "node": "fem.elements",
     "action": "delegates to self.get(...).ids",
     "passes": "selectors",
     "to": "fem.elements"
    }
   ],
   "inputs": "target/pg/label/tag/dim/element_type/partition",
   "outputs": "ndarray(E,) int64 element IDs",
   "reads": [
    "see fem.elements.get"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.ids",
   "label": "ids",
   "composite": "fem.elements",
   "signature": "property ids -> ndarray",
   "summary": "All element IDs concatenated across per-type groups as ndarray(E,) int64 (lazily cached).",
   "file": "src/apeGmsh/mesh/FEMData.py:678",
   "flow": [
    {
     "node": "fem.elements",
     "action": "concatenate g.ids for each group, cache _cached_ids",
     "passes": "ndarray(E,) int64",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(E,) int64 element IDs",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [
    "ElementComposite._cached_ids"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.index",
   "label": "index",
   "composite": "fem.elements",
   "signature": "index(eid: int) -> int",
   "summary": "Array index for an element ID (O(1) after first call builds the id→idx map); raises KeyError with the valid range on miss.",
   "file": "src/apeGmsh/mesh/FEMData.py:1018",
   "flow": [
    {
     "node": "fem.elements",
     "action": "lazily build _id_to_idx over self.ids then lookup int(eid)",
     "passes": "element id int",
     "to": "fem.elements"
    }
   ],
   "inputs": "element ID",
   "outputs": "int array index (KeyError if absent)",
   "reads": [
    "ElementComposite.ids",
    "_id_to_idx cache"
   ],
   "writes": [
    "ElementComposite._id_to_idx"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.is_homogeneous",
   "label": "is_homogeneous",
   "composite": "fem.elements",
   "signature": "property is_homogeneous -> bool",
   "summary": "True if all elements are the same type (<=1 group).",
   "file": "src/apeGmsh/mesh/FEMData.py:720",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return len(self._groups) <= 1",
     "passes": "bool",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "bool",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.labels",
   "label": "labels",
   "composite": "fem.elements",
   "signature": "attribute labels -> LabelSet",
   "summary": "Snapshot of geometry-time Tier-1 labels (shared reference with fem.nodes.labels); element_ids access for dim>=1.",
   "file": "src/apeGmsh/mesh/FEMData.py:647",
   "flow": [
    {
     "node": "fem.elements",
     "action": "expose shared LabelSet",
     "passes": "LabelSet",
     "to": "labels"
    }
   ],
   "inputs": "none",
   "outputs": "LabelSet",
   "reads": [
    "ElementComposite.labels"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.loads",
   "label": "loads",
   "composite": "fem.elements",
   "signature": "attribute loads -> ElementLoadSet",
   "summary": "Element load records (surface pressure, body forces / beamUniform) grouped by pattern.",
   "file": "src/apeGmsh/mesh/FEMData.py:650",
   "flow": [
    {
     "node": "fem.elements",
     "action": "ElementLoadSet(element_loads) from resolver split",
     "passes": "list[ElementLoadRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "ElementLoadSet",
   "reads": [
    "ElementComposite.loads"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.loads.by_kind",
   "label": "ElementLoadSet.by_kind",
   "composite": "records",
   "signature": "by_kind(kind: str) -> list[ElementLoadRecord]",
   "summary": "_RecordSetBase filter on fem.elements.loads by record .kind (always 'element'; Kind=LoadKind exposed).",
   "file": "src/apeGmsh/mesh/_record_set.py:76",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.loads",
     "passes": "ElementLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter by kind",
     "passes": "list[ElementLoadRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "kind (str; fem.elements.loads.Kind.ELEMENT)",
   "outputs": "list[ElementLoadRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.loads.by_pattern",
   "label": "ElementLoadSet.by_pattern",
   "composite": "records",
   "signature": "by_pattern(name: str) -> list[ElementLoadRecord]",
   "summary": "Returns all resolved element load records on fem.elements.loads belonging to the named pattern.",
   "file": "src/apeGmsh/mesh/_record_set.py:928",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.loads",
     "passes": "ElementLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter self._records by r.pattern==name",
     "passes": "list[ElementLoadRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "name (pattern label)",
   "outputs": "list[ElementLoadRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.loads.iter",
   "label": "ElementLoadSet.__iter__/__getitem__/__len__/__bool__",
   "composite": "records",
   "signature": "__iter__() -> Iterator[ElementLoadRecord]; __getitem__(idx); __len__(); __bool__()",
   "summary": "_RecordSetBase dunders on fem.elements.loads: iterate resolved ElementLoadRecords (element_id, load_type, params, pattern) for ops.eleLoad emission, plus indexing/length/truthiness.",
   "file": "src/apeGmsh/mesh/_record_set.py:891",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.loads (ElementLoadSet from LoadsComposite.resolve element-form)",
     "passes": "ElementLoadSet over list[ElementLoadRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "iter/index/len/bool over self._records",
     "passes": "ElementLoadRecord(element_id,load_type,params,pattern) / int / bool",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "idx for __getitem__",
   "outputs": "ElementLoadRecord(s), int, bool",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.loads.patterns",
   "label": "ElementLoadSet.patterns",
   "composite": "records",
   "signature": "patterns() -> list[str]",
   "summary": "Returns unique load-pattern names across resolved element load records (insertion order).",
   "file": "src/apeGmsh/mesh/_record_set.py:920",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.loads",
     "passes": "ElementLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collect distinct r.pattern in order",
     "passes": "list[str]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "list[str] pattern names",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.loads.summary",
   "label": "ElementLoadSet.summary",
   "composite": "records",
   "signature": "summary() -> pandas.DataFrame",
   "summary": "Returns a DataFrame of (pattern, load_type) -> count for the resolved element load set.",
   "file": "src/apeGmsh/mesh/_record_set.py:932",
   "flow": [
    {
     "node": "fem.elements",
     "action": "access fem.elements.loads",
     "passes": "ElementLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "tally (pattern,load_type) counts",
     "passes": "DataFrame[pattern,kind,count]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "pandas.DataFrame",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.elements.partitions",
   "label": "partitions",
   "composite": "fem.elements",
   "signature": "property partitions -> list[int]",
   "summary": "Sorted list of partition IDs on the element side.",
   "file": "src/apeGmsh/mesh/FEMData.py:715",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return sorted(self._partitions.keys())",
     "passes": "list[int]",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "sorted list of partition IDs",
   "reads": [
    "ElementComposite._partitions"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.physical",
   "label": "physical",
   "composite": "fem.elements",
   "signature": "attribute physical -> PhysicalGroupSet",
   "summary": "Snapshot of solver-facing physical groups (shared reference with fem.nodes.physical); element_ids access for dim>=1 groups.",
   "file": "src/apeGmsh/mesh/FEMData.py:646",
   "flow": [
    {
     "node": "fem.elements",
     "action": "expose shared PhysicalGroupSet",
     "passes": "PhysicalGroupSet",
     "to": "physical"
    }
   ],
   "inputs": "none",
   "outputs": "PhysicalGroupSet",
   "reads": [
    "ElementComposite.physical"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.resolve",
   "label": "resolve",
   "composite": "fem.elements",
   "signature": "resolve(target=None, *, pg=None, label=None, tag=None, dim=None, element_type=None, partition=None) -> tuple[ndarray, ndarray]",
   "summary": "Convenience for single-type results: flatten the selection to (ids, connectivity) arrays via .get(...).resolve().",
   "file": "src/apeGmsh/mesh/FEMData.py:920",
   "flow": [
    {
     "node": "fem.elements",
     "action": "delegates to self.get(...).resolve()",
     "passes": "selectors",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "GroupResult.resolve enforces homogeneity (TypeError if mixed)",
     "passes": "(ids int64(N,), connectivity int64(N,npe))",
     "to": "fem.elements"
    }
   ],
   "inputs": "target/pg/label/tag/dim/element_type/partition",
   "outputs": "(ids ndarray, connectivity ndarray) — TypeError if multiple types",
   "reads": [
    "see fem.elements.get"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.select",
   "label": "select",
   "composite": "selection",
   "signature": "select(target=None, *, pg=None, label=None, tag=None, dim: int | None = None, element_type: str | int | None = None, partition: int | None = None, ids=None) -> ElementChain",
   "summary": "Start a daisy-chainable ElementChain (atoms are element ids, spatial verbs operate on centroids) seeded id-for-id like get(); pure name seed reuses _resolve_elem_ids, aux filters reuse get() verbatim.",
   "file": "src/apeGmsh/mesh/FEMData.py:935",
   "flow": [
    {
     "node": "fem.elements",
     "action": "ids= → int atoms; no-arg → all .ids; pure name → _resolve_elem_ids; aux dim/type/partition → reuse get().ids",
     "passes": "element ids → list[int] atoms",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "deferred import ElementChain, construct with _engine=self (ElementComposite)",
     "passes": "list[int] atoms, _engine=ElementComposite",
     "to": "selection"
    }
   ],
   "inputs": "same selectors as get() plus ids=",
   "outputs": "ElementChain (point family, centroid-based) bound to this ElementComposite",
   "reads": [
    "ElementComposite._groups",
    "fem.elements.physical/labels",
    "_part_elem_map",
    "_partitions"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.type_table",
   "label": "type_table",
   "composite": "fem.elements",
   "signature": "type_table() -> pd.DataFrame",
   "summary": "DataFrame of element types in the mesh (code, name, gmsh_name, dim, order, npe, count).",
   "file": "src/apeGmsh/mesh/FEMData.py:727",
   "flow": [
    {
     "node": "fem.elements",
     "action": "build rows from each group's ElementTypeInfo",
     "passes": "pd.DataFrame",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.elements.types",
   "label": "types",
   "composite": "fem.elements",
   "signature": "property types -> list[ElementTypeInfo]",
   "summary": "List of ElementTypeInfo metadata objects for the element types present in the mesh.",
   "file": "src/apeGmsh/mesh/FEMData.py:710",
   "flow": [
    {
     "node": "fem.elements",
     "action": "return [g.element_type for g in _groups]",
     "passes": "list[ElementTypeInfo]",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "list[ElementTypeInfo]",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.hrz.hrz_weights",
   "label": "hrz_weights",
   "composite": "fem.hrz",
   "signature": "hrz_weights(gmsh_code: int) -> tuple[float, ...]",
   "summary": "from apeGmsh.fem import _hrz — normalized per-node HRZ (Hinton-Rock-Zienkiewicz) lumping weights = diag(∫N_I²) / Σ on the reference element, summing to 1.0 (1/n for first-order). lru_cached. Used by the mass resolver's consistent-reduction path.",
   "file": "src/apeGmsh/fem/_hrz.py:108",
   "flow": [
    {
     "node": "fem.hrz",
     "action": "look up _QUAD_SPEC + shape fn, evaluate N at quadrature pts",
     "passes": "N (n_qp,n_nodes)",
     "to": "fem.shape"
    },
    {
     "node": "fem.shape",
     "action": "return N_fn from catalog",
     "passes": "ShapeFn",
     "to": "fem.hrz"
    },
    {
     "node": "fem.hrz",
     "action": "get quadrature pts/wts, compute ∫N_I², normalize",
     "passes": "weight tuple",
     "to": "fem.quadrature"
    },
    {
     "node": "fem.quadrature",
     "action": "return reference rule pts/wts",
     "passes": "ndarray pts/wts",
     "to": "mass.resolver"
    }
   ],
   "inputs": "Gmsh element-type code",
   "outputs": "tuple of per-node weights summing to 1.0",
   "reads": [
    "SHAPE_FUNCTIONS_BY_GMSH_CODE",
    "_quadrature rules"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.hrz.reference_quadrature",
   "label": "reference_quadrature",
   "composite": "fem.hrz",
   "signature": "reference_quadrature(gmsh_code: int) -> tuple[np.ndarray, np.ndarray]",
   "summary": "Return the (points, weights) reference-element rule HRZ uses to integrate N_I² for a Gmsh code — also exactly sufficient for element-volume integration. Raises if the code has no quadrature spec.",
   "file": "src/apeGmsh/fem/_hrz.py:86",
   "flow": [
    {
     "node": "fem.hrz",
     "action": "resolve _QUAD_SPEC[code], call _quadrature_for",
     "passes": "(kind,n) spec",
     "to": "fem.quadrature"
    },
    {
     "node": "fem.quadrature",
     "action": "build the matching reference rule",
     "passes": "ndarray pts/wts",
     "to": "mass.resolver"
    }
   ],
   "inputs": "Gmsh element-type code",
   "outputs": "(points, weights) ndarrays on the reference element",
   "reads": [
    "_QUAD_SPEC",
    "_quadrature rules"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.hrz.volume_code",
   "label": "volume_code / surface_code / line_code",
   "composite": "fem.hrz",
   "signature": "volume_code(n)/surface_code(n)/line_code(n) -> int|None",
   "summary": "Node-count → Gmsh element-type code helpers by parent dimension (volume {4,8,6,10,20,27}, surface {3,4,6,8,9}, line {2}). Lets the mass resolver infer element type from connectivity width alone.",
   "file": "src/apeGmsh/fem/_hrz.py:166",
   "flow": [
    {
     "node": "fem.hrz",
     "action": "dict lookup n_nodes→gmsh code",
     "passes": "int code or None",
     "to": "mass.resolver"
    }
   ],
   "inputs": "n_nodes int",
   "outputs": "Gmsh element-type code int or None",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.info.__repr__",
   "label": "repr(fem.info)",
   "composite": "fem.info",
   "signature": "__repr__() -> str",
   "summary": "MeshInfo repr with counts, bandwidth, and type-name list.",
   "file": "src/apeGmsh/mesh/FEMData.py:206",
   "flow": [
    {
     "node": "fem.info",
     "action": "format counts/bandwidth/type names",
     "passes": "str",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "repr string",
   "reads": [
    "MeshInfo fields"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.info.bandwidth",
   "label": "bandwidth",
   "composite": "fem.info",
   "signature": "attribute bandwidth -> int",
   "summary": "Semi-bandwidth = max over all elements of (max_node − min_node); 0 when reconstructed from HDF5 (writer doesn't store it).",
   "file": "src/apeGmsh/mesh/FEMData.py:180",
   "flow": [
    {
     "node": "fem.info",
     "action": "expose bandwidth computed by _compute_bandwidth at factory time",
     "passes": "int",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "int semi-bandwidth",
   "reads": [
    "MeshInfo.bandwidth"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.info.elem_type_name",
   "label": "elem_type_name",
   "composite": "fem.info",
   "signature": "property elem_type_name -> str",
   "summary": "Backward-compat: first element type's name, or empty string.",
   "file": "src/apeGmsh/mesh/FEMData.py:201",
   "flow": [
    {
     "node": "fem.info",
     "action": "return self.types[0].name or ''",
     "passes": "str",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "str type name",
   "reads": [
    "MeshInfo.types"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.info.n_elems",
   "label": "n_elems",
   "composite": "fem.info",
   "signature": "attribute n_elems -> int",
   "summary": "Number of elements in the mesh.",
   "file": "src/apeGmsh/mesh/FEMData.py:179",
   "flow": [
    {
     "node": "fem.info",
     "action": "expose stored n_elems int",
     "passes": "int",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "int element count",
   "reads": [
    "MeshInfo.n_elems"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.info.n_nodes",
   "label": "n_nodes",
   "composite": "fem.info",
   "signature": "attribute n_nodes -> int",
   "summary": "Number of nodes in the mesh.",
   "file": "src/apeGmsh/mesh/FEMData.py:178",
   "flow": [
    {
     "node": "fem.info",
     "action": "expose stored n_nodes int",
     "passes": "int",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "int node count",
   "reads": [
    "MeshInfo.n_nodes"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.info.nodes_per_elem",
   "label": "nodes_per_elem",
   "composite": "fem.info",
   "signature": "property nodes_per_elem -> int",
   "summary": "Backward-compat: first element type's npe, or 0 if empty.",
   "file": "src/apeGmsh/mesh/FEMData.py:196",
   "flow": [
    {
     "node": "fem.info",
     "action": "return self.types[0].npe or 0",
     "passes": "int",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "int nodes-per-element",
   "reads": [
    "MeshInfo.types"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.info.summary",
   "label": "summary",
   "composite": "fem.info",
   "signature": "summary() -> str",
   "summary": "One-line mesh summary: node/element counts, per-type counts, and bandwidth.",
   "file": "src/apeGmsh/mesh/FEMData.py:216",
   "flow": [
    {
     "node": "fem.info",
     "action": "format n_nodes/n_elems + per-type + bandwidth",
     "passes": "str",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "one-line summary string",
   "reads": [
    "MeshInfo.n_nodes/n_elems/types/bandwidth"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.info.types",
   "label": "types",
   "composite": "fem.info",
   "signature": "attribute types -> list[ElementTypeInfo]",
   "summary": "Element types present in the mesh (list of ElementTypeInfo).",
   "file": "src/apeGmsh/mesh/FEMData.py:181",
   "flow": [
    {
     "node": "fem.info",
     "action": "expose stored types list",
     "passes": "list[ElementTypeInfo]",
     "to": "fem.info"
    }
   ],
   "inputs": "none",
   "outputs": "list[ElementTypeInfo]",
   "reads": [
    "MeshInfo.types"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.constraint_summary",
   "label": "constraint_summary",
   "composite": "fem.inspect",
   "signature": "constraint_summary() -> str",
   "summary": "Human-readable per-kind breakdown of node + surface constraints, including phantom-node count from node_to_surface.",
   "file": "src/apeGmsh/mesh/FEMData.py:1155",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "tally kinds across fem.nodes.constraints + fem.elements.constraints + phantom_nodes()",
     "passes": "str lines",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "constraint breakdown string",
   "reads": [
    "fem.nodes.constraints",
    "fem.elements.constraints"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.element_table",
   "label": "element_table",
   "composite": "fem.inspect",
   "signature": "element_table() -> pd.DataFrame",
   "summary": "DataFrame of all elements (elem_id index, type column, n0..nk connectivity columns) built by iterating every group.",
   "file": "src/apeGmsh/mesh/FEMData.py:1135",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "iterate fem.elements groups, expand (eid, conn_row) rows",
     "passes": "pd.DataFrame",
     "to": "fem.elements"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame (elem_id → type, n0..nk)",
   "reads": [
    "fem.elements groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.label_table",
   "label": "label_table",
   "composite": "fem.inspect",
   "signature": "label_table() -> pd.DataFrame",
   "summary": "Delegates to fem.nodes.labels.summary() — DataFrame of every label.",
   "file": "src/apeGmsh/mesh/FEMData.py:1150",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "return fem.nodes.labels.summary()",
     "passes": "pd.DataFrame",
     "to": "labels"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame (dim,pg_tag → name,n_nodes,n_elems)",
   "reads": [
    "fem.nodes.labels"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.load_summary",
   "label": "load_summary",
   "composite": "fem.inspect",
   "signature": "load_summary() -> str",
   "summary": "Human-readable per-pattern breakdown of nodal and element loads.",
   "file": "src/apeGmsh/mesh/FEMData.py:1189",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "iterate fem.nodes.loads.patterns() + fem.elements.loads.patterns()",
     "passes": "str lines",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "load breakdown string",
   "reads": [
    "fem.nodes.loads",
    "fem.elements.loads"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.mass_summary",
   "label": "mass_summary",
   "composite": "fem.inspect",
   "signature": "mass_summary() -> str",
   "summary": "Human-readable nodal-mass breakdown including total translational mass.",
   "file": "src/apeGmsh/mesh/FEMData.py:1227",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "read fem.nodes.masses count + total_mass()",
     "passes": "str lines",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "mass breakdown string",
   "reads": [
    "fem.nodes.masses"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.node_table",
   "label": "node_table",
   "composite": "fem.inspect",
   "signature": "node_table() -> pd.DataFrame",
   "summary": "DataFrame of all nodes indexed by node_id with x/y/z columns.",
   "file": "src/apeGmsh/mesh/FEMData.py:1124",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "build DataFrame from fem.nodes.coords/ids",
     "passes": "pd.DataFrame",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame (node_id → x,y,z)",
   "reads": [
    "fem.nodes.coords",
    "fem.nodes.ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.physical_table",
   "label": "physical_table",
   "composite": "fem.inspect",
   "signature": "physical_table() -> pd.DataFrame",
   "summary": "Delegates to fem.nodes.physical.summary() — DataFrame of every physical group.",
   "file": "src/apeGmsh/mesh/FEMData.py:1147",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "return fem.nodes.physical.summary()",
     "passes": "pd.DataFrame",
     "to": "physical"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame (dim,pg_tag → name,n_nodes,n_elems)",
   "reads": [
    "fem.nodes.physical"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.inspect.summary",
   "label": "summary",
   "composite": "fem.inspect",
   "signature": "summary() -> str",
   "summary": "Multi-line mesh summary plus physical-group, label, element-type, constraint, load, and mass sub-composite breakdowns.",
   "file": "src/apeGmsh/mesh/FEMData.py:1065",
   "flow": [
    {
     "node": "fem.inspect",
     "action": "read fem.info.summary + iterate physical/labels/types/constraints/loads/masses",
     "passes": "str lines",
     "to": "fem.inspect"
    }
   ],
   "inputs": "none",
   "outputs": "multi-line summary string (also fem.__repr__)",
   "reads": [
    "fem.info",
    "fem.nodes.physical/labels/constraints/loads/masses",
    "fem.elements.constraints/loads"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.__len__",
   "label": "len(fem.nodes)",
   "composite": "fem.nodes",
   "signature": "__len__() -> int",
   "summary": "Number of domain nodes.",
   "file": "src/apeGmsh/mesh/FEMData.py:597",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return len(self._ids)",
     "passes": "int",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "node count int",
   "reads": [
    "NodeComposite._ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.__repr__",
   "label": "repr(fem.nodes)",
   "composite": "fem.nodes",
   "signature": "__repr__() -> str",
   "summary": "Multi-line NodeComposite summary including non-empty constraints/loads/masses sub-composite reprs.",
   "file": "src/apeGmsh/mesh/FEMData.py:600",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "format node count + sub-composite reprs",
     "passes": "str",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "summary string",
   "reads": [
    "NodeComposite._ids",
    "constraints",
    "loads",
    "masses"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.constraints",
   "label": "constraints",
   "composite": "fem.nodes",
   "signature": "attribute constraints -> NodeConstraintSet",
   "summary": "Node-to-node constraint records (equal_dof, rigid_beam, rigid_diaphragm, node_to_surface, …) with solver-ready typed iterators.",
   "file": "src/apeGmsh/mesh/FEMData.py:268",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "NodeConstraintSet(node_constraints) from resolver split",
     "passes": "list[ConstraintRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "NodeConstraintSet",
   "reads": [
    "NodeComposite.constraints"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.constraints.Kind",
   "label": "NodeConstraintSet.Kind",
   "composite": "records",
   "signature": "Kind = ConstraintKind (class attribute)",
   "summary": "ConstraintKind constant class re-exposed on fem.nodes.constraints for linter-friendly kind comparisons (EQUAL_DOF, RIGID_BEAM, RIGID_BEAM_STIFF, RIGID_ROD, RIGID_DIAPHRAGM, RIGID_BODY, KINEMATIC_COUPLING, PENALTY, NODE_TO_SURFACE, NODE_TO_SURFACE_SPRING, TIE, DISTRIBUTING, EMBEDDED, TIED_CONTACT, MORTAR).",
   "file": "src/apeGmsh/mesh/_record_set.py:223",
   "flow": [
    {
     "node": "records",
     "action": "expose ConstraintKind string-constant class as .Kind for use with by_kind / kind == comparisons",
     "passes": "ConstraintKind (ClassVar[str] constants + NODE_PAIR_KINDS/SURFACE_KINDS frozensets)",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none (attribute access)",
   "outputs": "ConstraintKind constants",
   "reads": [
    "apeGmsh.mesh.records._kinds.ConstraintKind"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.by_kind",
   "label": "NodeConstraintSet.by_kind",
   "composite": "records",
   "signature": "by_kind(kind: str) -> list[ConstraintRecord]",
   "summary": "Inherited from _RecordSetBase: returns all records in fem.nodes.constraints whose .kind matches (mixed subclasses, compound records NOT expanded).",
   "file": "src/apeGmsh/mesh/_record_set.py:76",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter self._records by getattr(r,'kind')==kind",
     "passes": "list[ConstraintRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "kind (str, e.g. fem.nodes.constraints.Kind.RIGID_BEAM)",
   "outputs": "list[ConstraintRecord] (mixed subtypes, not expanded)",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.equal_dofs",
   "label": "NodeConstraintSet.equal_dofs",
   "composite": "records",
   "signature": "equal_dofs() -> Iterator[NodePairRecord]",
   "summary": "Yields equal_dof NodePairRecords flat — direct kind==equal_dof records plus the phantom->slave equal_dof_records inside NodeToSurfaceRecord (each pair independent).",
   "file": "src/apeGmsh/mesh/_record_set.py:418",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "yield direct equal_dof pairs + NodeToSurfaceRecord.equal_dof_records",
     "passes": "Iterator[NodePairRecord(master_node,slave_node,dofs)]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "Iterator[NodePairRecord] (equal_dof)",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.iter_index",
   "label": "NodeConstraintSet.__iter__/__getitem__/__len__/__bool__",
   "composite": "records",
   "signature": "__iter__() -> Iterator[ConstraintRecord]; __getitem__(idx) -> ConstraintRecord; __len__() -> int; __bool__() -> bool",
   "summary": "_RecordSetBase dunders on fem.nodes.constraints: direct iteration yields the mixed concrete records (not expanded), plus indexing/length/truthiness.",
   "file": "src/apeGmsh/mesh/_record_set.py:83",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "iter/index/len/bool over self._records (raw mixed ConstraintRecord)",
     "passes": "ConstraintRecord(s) / int / bool",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "idx for __getitem__",
   "outputs": "mixed ConstraintRecord(s), int, bool",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.node_to_surfaces",
   "label": "NodeConstraintSet.node_to_surfaces",
   "composite": "records",
   "signature": "node_to_surfaces() -> Iterator[NodeToSurfaceRecord]",
   "summary": "Yields only the compound NodeToSurfaceRecord instances on fem.nodes.constraints so callers can reach side-band fields (phantom_nodes, phantom_coords, rigid_link_records, equal_dof_records) lost on flattening.",
   "file": "src/apeGmsh/mesh/_record_set.py:251",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter isinstance NodeToSurfaceRecord",
     "passes": "Iterator[NodeToSurfaceRecord(master_node,slave_nodes,phantom_nodes,phantom_coords,rigid_link_records,equal_dof_records,dofs)]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "Iterator[NodeToSurfaceRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.pairs",
   "label": "NodeConstraintSet.pairs",
   "composite": "records",
   "signature": "pairs() -> Iterator[NodePairRecord]",
   "summary": "Flat iterator over fem.nodes.constraints expanding compound NodeGroupRecord (expand_to_pairs) and NodeToSurfaceRecord (expand) into individual NodePairRecords — the natural emission order for OpenSees equalDOF/rigidLink.",
   "file": "src/apeGmsh/mesh/_record_set.py:227",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints (NodeConstraintSet built by ConstraintsComposite.resolve)",
     "passes": "NodeConstraintSet over list[ConstraintRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "yield NodePairRecord directly; NodeGroupRecord.expand_to_pairs(); NodeToSurfaceRecord.expand()",
     "passes": "Iterator[NodePairRecord(master_node,slave_node,dofs,offset)]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none (iterates the resolved set)",
   "outputs": "Iterator[NodePairRecord] (compound records expanded)",
   "reads": [
    "self._records",
    "NodeGroupRecord.expand_to_pairs",
    "NodeToSurfaceRecord.expand"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.phantom_nodes",
   "label": "NodeConstraintSet.phantom_nodes",
   "composite": "records",
   "signature": "phantom_nodes() -> NodeResult",
   "summary": "Returns a NodeResult of the phantom nodes solvers must create before emitting node_to_surface constraints; iterate as (node_id, xyz) pairs or pull .ids / .coords arrays.",
   "file": "src/apeGmsh/mesh/_record_set.py:448",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collect NodeToSurfaceRecord.phantom_nodes + phantom_coords into NodeResult",
     "passes": "NodeResult(ids ndarray(N,)object, coords ndarray(N,3)float64)",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "NodeResult (phantom node ids + coords; empty if none)",
   "reads": [
    "self._records",
    "apeGmsh.mesh.FEMData.NodeResult"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.rigid_diaphragms",
   "label": "NodeConstraintSet.rigid_diaphragms",
   "composite": "records",
   "signature": "rigid_diaphragms() -> Iterator[tuple[int, int, list[int]]]",
   "summary": "Yields (perp_dirn, master, slaves) for rigid_diaphragm NodeGroupRecords, deriving perp_dirn (1|2|3) from the resolved plane_normal via _perp_dirn so non-horizontal diaphragms emit correctly.",
   "file": "src/apeGmsh/mesh/_record_set.py:389",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter NodeGroupRecord kind==rigid_diaphragm, _perp_dirn(plane_normal)",
     "passes": "Iterator[(perp_dirn:int, master_node:int, [slave_node:int,...])]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "Iterator[(int, int, list[int])] (perpDirn, master, slaves)",
   "reads": [
    "self._records",
    "_perp_dirn"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.rigid_link_groups",
   "label": "NodeConstraintSet.rigid_link_groups",
   "composite": "records",
   "signature": "rigid_link_groups() -> Iterator[tuple[int, list[int]]]",
   "summary": "Yields (master, [slaves]) tuples grouping every rigid-link source (NodePairRecord rigid_beam/rigid_rod, NodeGroupRecord rigid_body, NodeToSurfaceRecord rigid_link_records) — excludes rigid_diaphragm/kinematic_coupling and rigid_beam_stiff.",
   "file": "src/apeGmsh/mesh/_record_set.py:266",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "accumulate slaves by master for kinds in {rigid_beam,rigid_rod} + rigid_body groups + NodeToSurface rigid_link_records (skipping rigid_beam_stiff)",
     "passes": "Iterator[(master_node:int, [slave_node:int,...])]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "Iterator[(int, list[int])] master->slaves",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.stiff_beam_groups",
   "label": "NodeConstraintSet.stiff_beam_groups",
   "composite": "records",
   "signature": "stiff_beam_groups() -> Iterator[tuple[int, list[int]]]",
   "summary": "Yields (master, [slaves]) tuples for rigid_beam_stiff pair records only (from node_to_surface_spring) — consumed downstream as stiff elasticBeamColumn elements; regular rigid_link_groups() skips these.",
   "file": "src/apeGmsh/mesh/_record_set.py:339",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "accumulate slaves by master for kind==rigid_beam_stiff (direct NodePairRecord + NodeToSurfaceRecord.rigid_link_records)",
     "passes": "Iterator[(master_node:int, [phantom_node:int,...])]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "Iterator[(int, list[int])] master->stiff-beam slaves",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.constraints.summary",
   "label": "NodeConstraintSet.summary",
   "composite": "records",
   "signature": "summary() -> pandas.DataFrame",
   "summary": "Returns a DataFrame indexed by kind with columns count and n_node_pairs (NodeGroupRecord counted by slave count, NodeToSurfaceRecord by rigid+equal record count) for the resolved node-constraint set.",
   "file": "src/apeGmsh/mesh/_record_set.py:491",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.constraints",
     "passes": "NodeConstraintSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "tally counts/n_node_pairs per kind across record subtypes",
     "passes": "DataFrame[kind,count,n_node_pairs]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "pandas.DataFrame summary of node constraints",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.coords",
   "label": "coords",
   "composite": "fem.nodes",
   "signature": "property coords -> ndarray",
   "summary": "All domain node coordinates as ndarray(N, 3) float64.",
   "file": "src/apeGmsh/mesh/FEMData.py:288",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return _coords (np.asarray float64 at __init__)",
     "passes": "ndarray(N,3) float64",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,3) float64 coordinates",
   "reads": [
    "NodeComposite._coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.get",
   "label": "get",
   "composite": "fem.nodes",
   "signature": "get(target=None, *, pg=None, label=None, tag=None, partition: int | None = None, dim: int | None = None) -> NodeResult",
   "summary": "Resolve a node selection (PG/label/tag/target/part-label, optional partition+dim) into a NodeResult bundling (ids, coords); target auto-resolves label→PG→part.",
   "file": "src/apeGmsh/mesh/FEMData.py:362",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "_resolve_nodes dispatches on tag/pg/label/target",
     "passes": "selector str/list/(dim,tag), dim:int|None",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "pg/label/tag → _union_nodes calls physical/labels group-set id_fn/coord_fn",
     "passes": "selector items",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "NamedGroupSet._resolve(name, dim=) → info dict (name-index lookup, multi-dim merge)",
     "passes": "node_ids object ndarray, node_coords float64(N,3)",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "target string falls through label→PG→part_node_map (KeyError-only swallow); raw (dim,tag) → live gmsh getNodes",
     "passes": "id set / DimTag",
     "to": "gmsh"
    },
    {
     "node": "fem.nodes",
     "action": "optional _intersect_partition mask vs partitions[node_ids]; wrap in NodeResult",
     "passes": "ids ndarray, coords float64(N,3)",
     "to": "fem.nodes"
    }
   ],
   "inputs": "target/pg/label/tag/partition/dim selectors",
   "outputs": "NodeResult (iterable (id,xyz) pairs; .ids/.coords/.to_dataframe)",
   "reads": [
    "NodeComposite._ids/_coords",
    "fem.nodes.physical",
    "fem.nodes.labels",
    "_part_node_map",
    "_partitions",
    "gmsh (raw DimTag only)"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.get_coords",
   "label": "get_coords",
   "composite": "fem.nodes",
   "signature": "get_coords(target=None, *, pg=None, label=None, tag=None, partition=None, dim=None) -> ndarray",
   "summary": "Same selection as get() but returns only the coordinate array (.get(...).coords).",
   "file": "src/apeGmsh/mesh/FEMData.py:570",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "delegates to self.get(...).coords",
     "passes": "selectors",
     "to": "fem.nodes"
    }
   ],
   "inputs": "target/pg/label/tag/partition/dim",
   "outputs": "ndarray(N,3) float64 coordinates",
   "reads": [
    "see fem.nodes.get"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.get_ids",
   "label": "get_ids",
   "composite": "fem.nodes",
   "signature": "get_ids(target=None, *, pg=None, label=None, tag=None, partition=None, dim=None) -> ndarray",
   "summary": "Same selection as get() but returns only the node-ID array (.get(...).ids).",
   "file": "src/apeGmsh/mesh/FEMData.py:564",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "delegates to self.get(...).ids",
     "passes": "selectors",
     "to": "fem.nodes"
    }
   ],
   "inputs": "target/pg/label/tag/partition/dim",
   "outputs": "ndarray(N,) object dtype node IDs",
   "reads": [
    "see fem.nodes.get"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.ids",
   "label": "ids",
   "composite": "fem.nodes",
   "signature": "property ids -> ndarray",
   "summary": "All domain node IDs as ndarray(N,) object dtype (iterates as plain Python int for solver APIs).",
   "file": "src/apeGmsh/mesh/FEMData.py:283",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return _ids (coerced to object dtype once at __init__ via _to_object)",
     "passes": "ndarray(N,) object",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "ndarray(N,) object dtype node IDs",
   "reads": [
    "NodeComposite._ids"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.index",
   "label": "index",
   "composite": "fem.nodes",
   "signature": "index(nid: int) -> int",
   "summary": "Array index for a node ID (O(1) after first call builds the id→idx map); raises KeyError with the valid range on miss.",
   "file": "src/apeGmsh/mesh/FEMData.py:578",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "lazily build _id_to_idx dict then lookup int(nid)",
     "passes": "node id int",
     "to": "fem.nodes"
    }
   ],
   "inputs": "node ID",
   "outputs": "int array index (KeyError if absent)",
   "reads": [
    "NodeComposite._ids",
    "_id_to_idx cache"
   ],
   "writes": [
    "NodeComposite._id_to_idx"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.labels",
   "label": "labels",
   "composite": "fem.nodes",
   "signature": "attribute labels -> LabelSet",
   "summary": "Snapshot of geometry-time Tier-1 labels (shared reference with fem.elements.labels); supports multi-dim merged views.",
   "file": "src/apeGmsh/mesh/FEMData.py:266",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "expose LabelSet built from extract_labels()",
     "passes": "LabelSet",
     "to": "labels"
    }
   ],
   "inputs": "none",
   "outputs": "LabelSet",
   "reads": [
    "NodeComposite.labels"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.loads",
   "label": "loads",
   "composite": "fem.nodes",
   "signature": "attribute loads -> NodalLoadSet",
   "summary": "Resolved nodal point-force/moment records grouped by pattern.",
   "file": "src/apeGmsh/mesh/FEMData.py:269",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "NodalLoadSet(nodal_loads) from resolver split",
     "passes": "list[NodalLoadRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "NodalLoadSet",
   "reads": [
    "NodeComposite.loads"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.loads.by_kind",
   "label": "NodalLoadSet.by_kind",
   "composite": "records",
   "signature": "by_kind(kind: str) -> list[NodalLoadRecord]",
   "summary": "_RecordSetBase filter on fem.nodes.loads by record .kind (always 'nodal' for this set; Kind=LoadKind exposed).",
   "file": "src/apeGmsh/mesh/_record_set.py:76",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.loads",
     "passes": "NodalLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter by kind",
     "passes": "list[NodalLoadRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "kind (str; fem.nodes.loads.Kind.NODAL)",
   "outputs": "list[NodalLoadRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.loads.by_pattern",
   "label": "NodalLoadSet.by_pattern",
   "composite": "records",
   "signature": "by_pattern(name: str) -> list[NodalLoadRecord]",
   "summary": "Returns all resolved nodal load records on fem.nodes.loads belonging to the named load pattern.",
   "file": "src/apeGmsh/mesh/_record_set.py:595",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.loads",
     "passes": "NodalLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter self._records by r.pattern==name",
     "passes": "list[NodalLoadRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "name (pattern label)",
   "outputs": "list[NodalLoadRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.loads.iter",
   "label": "NodalLoadSet.__iter__/__getitem__/__len__/__bool__",
   "composite": "records",
   "signature": "__iter__() -> Iterator[NodalLoadRecord]; __getitem__(idx); __len__(); __bool__()",
   "summary": "_RecordSetBase dunders on fem.nodes.loads: iterate resolved NodalLoadRecords (node_id, force_xyz, moment_xyz, pattern, name), plus indexing/length/truthiness.",
   "file": "src/apeGmsh/mesh/_record_set.py:537",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.loads (NodalLoadSet from LoadsComposite.resolve)",
     "passes": "NodalLoadSet over list[NodalLoadRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "iter/index/len/bool over self._records",
     "passes": "NodalLoadRecord(node_id,force_xyz,moment_xyz,pattern,name) / int / bool",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "idx for __getitem__",
   "outputs": "NodalLoadRecord(s), int, bool",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.loads.patterns",
   "label": "NodalLoadSet.patterns",
   "composite": "records",
   "signature": "patterns() -> list[str]",
   "summary": "Returns unique load-pattern names across resolved nodal load records (insertion order) — pairs with by_pattern for OpenSees timeSeries/pattern emission.",
   "file": "src/apeGmsh/mesh/_record_set.py:587",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.loads",
     "passes": "NodalLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collect distinct r.pattern in order",
     "passes": "list[str]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "list[str] pattern names",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.loads.summary",
   "label": "NodalLoadSet.summary",
   "composite": "records",
   "signature": "summary() -> pandas.DataFrame",
   "summary": "Returns a DataFrame of (pattern, kind) -> count for the resolved nodal load set.",
   "file": "src/apeGmsh/mesh/_record_set.py:599",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.loads",
     "passes": "NodalLoadSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "tally (pattern,kind) counts",
     "passes": "DataFrame[pattern,kind,count]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "pandas.DataFrame",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.masses",
   "label": "masses",
   "composite": "fem.nodes",
   "signature": "attribute masses -> MassSet",
   "summary": "Resolved per-node 6-DOF lumped mass records.",
   "file": "src/apeGmsh/mesh/FEMData.py:271",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "MassSet(masses) from mass.resolver",
     "passes": "list[MassRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "MassSet",
   "reads": [
    "NodeComposite.masses"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.masses.by_kind",
   "label": "MassSet.by_kind",
   "composite": "records",
   "signature": "by_kind(kind: str) -> list[MassRecord]",
   "summary": "_RecordSetBase filter on fem.nodes.masses by record .kind (MassRecord has no kind attr, so getattr returns None — typically returns empty unless kind matches None).",
   "file": "src/apeGmsh/mesh/_record_set.py:76",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.masses",
     "passes": "MassSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter by getattr(r,'kind',None)==kind",
     "passes": "list[MassRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "kind (str)",
   "outputs": "list[MassRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.masses.by_node",
   "label": "MassSet.by_node",
   "composite": "records",
   "signature": "by_node(node_id: int) -> MassRecord | None",
   "summary": "Returns the single accumulated MassRecord for a node id on fem.nodes.masses, or None if that node carries no mass.",
   "file": "src/apeGmsh/mesh/_record_set.py:698",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.masses",
     "passes": "MassSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "linear scan self._records for r.node_id==node_id",
     "passes": "MassRecord | None",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "node_id (int)",
   "outputs": "MassRecord or None",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.masses.iter",
   "label": "MassSet.__iter__/__getitem__/__len__/__bool__",
   "composite": "records",
   "signature": "__iter__() -> Iterator[MassRecord]; __getitem__(idx); __len__(); __bool__()",
   "summary": "_RecordSetBase dunders on fem.nodes.masses: iterate resolved MassRecords (node_id, length-6 mass tuple), plus indexing/length/truthiness — one record per node after accumulation.",
   "file": "src/apeGmsh/mesh/_record_set.py:667",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.masses (MassSet from MassesComposite.resolve)",
     "passes": "MassSet over list[MassRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "iter/index/len/bool over self._records",
     "passes": "MassRecord(node_id,mass=(mx,my,mz,Ixx,Iyy,Izz)) / int / bool",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "idx for __getitem__",
   "outputs": "MassRecord(s), int, bool",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.masses.summary",
   "label": "MassSet.summary",
   "composite": "records",
   "signature": "summary() -> pandas.DataFrame",
   "summary": "Returns a DataFrame with one row per node (node_id, mx, my, mz, Ixx, Iyy, Izz) for the resolved mass set, sorted by node id.",
   "file": "src/apeGmsh/mesh/_record_set.py:712",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.masses",
     "passes": "MassSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "expand each MassRecord.mass tuple into columns",
     "passes": "DataFrame[node_id,mx,my,mz,Ixx,Iyy,Izz]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "pandas.DataFrame per-node mass",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.masses.total_mass",
   "label": "MassSet.total_mass",
   "composite": "records",
   "signature": "total_mass() -> float",
   "summary": "Returns the sum of translational mass (mx component) over all MassRecords on fem.nodes.masses (assumes isotropic mx==my==mz).",
   "file": "src/apeGmsh/mesh/_record_set.py:705",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.masses",
     "passes": "MassSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "sum float(r.mass[0]) over self._records",
     "passes": "float total translational mass",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "float (Σ mx)",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.partitions",
   "label": "partitions",
   "composite": "fem.nodes",
   "signature": "property partitions -> list[int]",
   "summary": "Sorted list of partition IDs (empty if the mesh is not partitioned).",
   "file": "src/apeGmsh/mesh/FEMData.py:293",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "return sorted(self._partitions.keys())",
     "passes": "list[int]",
     "to": "fem.nodes"
    }
   ],
   "inputs": "none",
   "outputs": "sorted list of partition IDs",
   "reads": [
    "NodeComposite._partitions"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.physical",
   "label": "physical",
   "composite": "fem.nodes",
   "signature": "attribute physical -> PhysicalGroupSet",
   "summary": "Snapshot of solver-facing physical groups (shared reference with fem.elements.physical); name-first node_ids/node_coords/element_ids access.",
   "file": "src/apeGmsh/mesh/FEMData.py:265",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "expose PhysicalGroupSet built from extract_physical_groups()",
     "passes": "PhysicalGroupSet",
     "to": "physical"
    }
   ],
   "inputs": "none",
   "outputs": "PhysicalGroupSet",
   "reads": [
    "NodeComposite.physical"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.select",
   "label": "select",
   "composite": "selection",
   "signature": "select(target=None, *, pg=None, label=None, tag=None, partition: int | None = None, dim: int | None = None, ids=None) -> NodeChain",
   "summary": "Start a daisy-chainable NodeChain seeded id-for-id like get() (same _resolve_nodes + _intersect_partition); ids= gives an explicit seed, no-arg seeds every domain node.",
   "file": "src/apeGmsh/mesh/FEMData.py:300",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "ids= → int atoms; no-arg → all _ids; else _resolve_nodes(target,pg,label,tag,dim) (+ _intersect_partition)",
     "passes": "seed_ids ndarray → list[int] atoms",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "deferred import NodeChain, construct with _engine=self (NodeComposite)",
     "passes": "list[int] atoms, _engine=NodeComposite",
     "to": "selection"
    }
   ],
   "inputs": "same selectors as get() plus ids=",
   "outputs": "NodeChain (point family) bound to this NodeComposite",
   "reads": [
    "NodeComposite._ids",
    "fem.nodes.physical/labels",
    "_part_node_map",
    "_partitions"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.sp",
   "label": "sp",
   "composite": "fem.nodes",
   "signature": "attribute sp -> SPSet",
   "summary": "Single-point constraint records (fix / prescribed displacement) resolved from g.constraints.bc().",
   "file": "src/apeGmsh/mesh/FEMData.py:270",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "SPSet(sp) from resolver split + resolve_bcs",
     "passes": "list[SPRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "SPSet",
   "reads": [
    "NodeComposite.sp"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "fem.nodes.sp.by_kind",
   "label": "SPSet.by_kind",
   "composite": "records",
   "signature": "by_kind(kind: str) -> list[SPRecord]",
   "summary": "_RecordSetBase filter on fem.nodes.sp by record .kind (always 'sp' for SPRecord).",
   "file": "src/apeGmsh/mesh/_record_set.py:76",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.sp",
     "passes": "SPSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter by kind",
     "passes": "list[SPRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "kind (str)",
   "outputs": "list[SPRecord]",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.sp.by_node",
   "label": "SPSet.by_node",
   "composite": "records",
   "signature": "by_node(node_id: int) -> list[SPRecord]",
   "summary": "Returns all SP records on fem.nodes.sp for a given mesh node id (one record per constrained DOF).",
   "file": "src/apeGmsh/mesh/_record_set.py:655",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.sp",
     "passes": "SPSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter self._records by r.node_id==node_id",
     "passes": "list[SPRecord]",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "node_id (int)",
   "outputs": "list[SPRecord] for that node",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.sp.homogeneous",
   "label": "SPSet.homogeneous",
   "composite": "records",
   "signature": "homogeneous() -> list[SPRecord]",
   "summary": "Returns only the homogeneous (fix, value≈0) SP records on fem.nodes.sp — the ones emittable as ops.fix.",
   "file": "src/apeGmsh/mesh/_record_set.py:647",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.sp",
     "passes": "SPSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter self._records by r.is_homogeneous",
     "passes": "list[SPRecord] (is_homogeneous=True)",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "list[SPRecord] homogeneous fixes",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.sp.iter",
   "label": "SPSet.__iter__/__getitem__/__len__/__bool__",
   "composite": "records",
   "signature": "__iter__() -> Iterator[SPRecord]; __getitem__(idx); __len__(); __bool__()",
   "summary": "_RecordSetBase dunders on fem.nodes.sp: iterate resolved SPRecords (node_id, dof, value, is_homogeneous) from g.constraints.bc and g.loads.face_sp, plus indexing/length/truthiness.",
   "file": "src/apeGmsh/mesh/_record_set.py:624",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.sp (SPSet — populated by ConstraintsComposite.resolve_bcs and LoadsComposite face_sp resolution)",
     "passes": "SPSet over list[SPRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "iter/index/len/bool over self._records",
     "passes": "SPRecord(node_id,dof,value,is_homogeneous,pattern,name) / int / bool",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "idx for __getitem__",
   "outputs": "SPRecord(s), int, bool",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.nodes.sp.prescribed",
   "label": "SPSet.prescribed",
   "composite": "records",
   "signature": "prescribed() -> list[SPRecord]",
   "summary": "Returns only the non-homogeneous (non-zero prescribed displacement) SP records on fem.nodes.sp — the ones requiring ops.sp(node,dof,value).",
   "file": "src/apeGmsh/mesh/_record_set.py:651",
   "flow": [
    {
     "node": "fem.nodes",
     "action": "access fem.nodes.sp",
     "passes": "SPSet",
     "to": "records"
    },
    {
     "node": "records",
     "action": "filter self._records by not r.is_homogeneous",
     "passes": "list[SPRecord] (is_homogeneous=False)",
     "to": "external",
     "to_raw": "user"
    }
   ],
   "inputs": "none",
   "outputs": "list[SPRecord] prescribed displacements",
   "reads": [
    "self._records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "fem.quadrature.gauss_hex_3d",
   "label": "gauss_hex_3d",
   "composite": "fem.quadrature",
   "signature": "gauss_hex_3d(n: int) -> tuple[np.ndarray, np.ndarray]",
   "summary": "n×n×n tensor-product Gauss-Legendre on [-1,+1]^3; weights sum to 8. Reference hex domain matching hex8/20/27 shape functions.",
   "file": "src/apeGmsh/fem/_quadrature.py:57",
   "flow": [
    {
     "node": "fem.quadrature",
     "action": "tensor 1-D rule into (n³,3) pts + (n³,) wts",
     "passes": "ndarray pts/wts",
     "to": "fem.hrz"
    }
   ],
   "inputs": "n per-axis points",
   "outputs": "(points (n³,3), weights (n³,)) ndarrays",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.quadrature.gauss_legendre_1d",
   "label": "gauss_legendre_1d",
   "composite": "fem.quadrature",
   "signature": "gauss_legendre_1d(n: int) -> tuple[np.ndarray, np.ndarray]",
   "summary": "from apeGmsh.fem import _quadrature — n-point Gauss-Legendre on [-1,+1] (np.polynomial.legendre.leggauss), exact to degree 2n-1. Returns (points (n,), weights (n,)) matching the line shape-function parent domain.",
   "file": "src/apeGmsh/fem/_quadrature.py:34",
   "flow": [
    {
     "node": "fem.quadrature",
     "action": "leggauss(n) → points+weights float64",
     "passes": "ndarray pts/wts",
     "to": "fem.hrz"
    }
   ],
   "inputs": "n points",
   "outputs": "(points (n,), weights (n,)) ndarrays",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.quadrature.gauss_quad_2d",
   "label": "gauss_quad_2d",
   "composite": "fem.quadrature",
   "signature": "gauss_quad_2d(n: int) -> tuple[np.ndarray, np.ndarray]",
   "summary": "n×n tensor-product Gauss-Legendre on [-1,+1]^2; weights sum to 4. Reference quad domain matching quad4/8/9 shape functions.",
   "file": "src/apeGmsh/fem/_quadrature.py:49",
   "flow": [
    {
     "node": "fem.quadrature",
     "action": "tensor 1-D rule into (n²,2) pts + (n²,) wts",
     "passes": "ndarray pts/wts",
     "to": "fem.hrz"
    }
   ],
   "inputs": "n per-axis points",
   "outputs": "(points (n²,2), weights (n²,)) ndarrays",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.quadrature.gauss_tet",
   "label": "gauss_tet",
   "composite": "fem.quadrature",
   "signature": "gauss_tet() -> tuple[np.ndarray, np.ndarray]",
   "summary": "11-point Keast rule on the reference tet (origin + 3 unit-axis vertices), degree-4 exact; weights normalized to volume 1/6.",
   "file": "src/apeGmsh/fem/_quadrature.py:144",
   "flow": [
    {
     "node": "fem.quadrature",
     "action": "return precomputed _TET_PTS/_TET_WTS copies",
     "passes": "ndarray (11,3)/(11,)",
     "to": "fem.hrz"
    }
   ],
   "inputs": "(none)",
   "outputs": "(points (11,3), weights (11,)) ndarrays",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.quadrature.gauss_tri",
   "label": "gauss_tri",
   "composite": "fem.quadrature",
   "signature": "gauss_tri() -> tuple[np.ndarray, np.ndarray]",
   "summary": "7-point Strang-Fix/Hammer rule on the reference triangle (0,0)/(1,0)/(0,1), degree-5 exact; weights sum to area 1/2.",
   "file": "src/apeGmsh/fem/_quadrature.py:95",
   "flow": [
    {
     "node": "fem.quadrature",
     "action": "return precomputed _TRI_PTS/_TRI_WTS copies",
     "passes": "ndarray (7,2)/(7,)",
     "to": "fem.hrz"
    }
   ],
   "inputs": "(none)",
   "outputs": "(points (7,2), weights (7,)) ndarrays",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.quadrature.gauss_wedge",
   "label": "gauss_wedge",
   "composite": "fem.quadrature",
   "signature": "gauss_wedge(n_line: int=3) -> tuple[np.ndarray, np.ndarray]",
   "summary": "Tensor product of the 7-point triangle rule and an n_line-point 1-D line rule for the reference wedge; weights sum to 1.",
   "file": "src/apeGmsh/fem/_quadrature.py:153",
   "flow": [
    {
     "node": "fem.quadrature",
     "action": "tensor gauss_tri × gauss_legendre_1d(n_line)",
     "passes": "ndarray pts/wts",
     "to": "fem.hrz"
    }
   ],
   "inputs": "n_line points along the prism axis",
   "outputs": "(points (7·n_line,3), weights (7·n_line,)) ndarrays",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.shape.compute_jacobian_dets",
   "label": "compute_jacobian_dets",
   "composite": "fem.shape",
   "signature": "compute_jacobian_dets(natural_coords, element_node_coords, dN_fn, geom_kind) -> np.ndarray",
   "summary": "Per-element per-IP integration measures: |det J| for 'solid', ‖∂x/∂ξ×∂x/∂η‖ for 'shell', ‖∂x/∂ξ‖ for 'line'. Always non-negative; raises on unknown geom_kind.",
   "file": "src/apeGmsh/fem/_shape_functions.py:884",
   "flow": [
    {
     "node": "fem.shape",
     "action": "dN_fn(nat) → einsum Jacobian, det/cross/norm by geom_kind",
     "passes": "ndarray (n_elem, n_ip) measures",
     "to": "fem.extract"
    }
   ],
   "inputs": "natural_coords, element_node_coords, dN_fn, geom_kind",
   "outputs": "ndarray (n_elements, n_ip) Jacobian determinants/measures",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.shape.compute_physical_coords",
   "label": "compute_physical_coords",
   "composite": "fem.shape",
   "signature": "compute_physical_coords(natural_coords, element_node_coords, N_fn) -> np.ndarray",
   "summary": "Vectorized isoparametric map: einsum('in,enj->eij', N(nat), node_coords) → physical (x,y,z) at every IP for every element. Batched over elements sharing an IP layout.",
   "file": "src/apeGmsh/fem/_shape_functions.py:859",
   "flow": [
    {
     "node": "fem.shape",
     "action": "evaluate N_fn(natural_coords), einsum with node coords",
     "passes": "ndarray (n_elem, n_ip, 3)",
     "to": "fem.extract"
    }
   ],
   "inputs": "natural_coords (n_ip,parent_dim), element_node_coords (n_el,n_node,3), N_fn",
   "outputs": "ndarray (n_elements, n_ip, 3) physical coords",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.shape.element_primitives",
   "label": "shape-fn primitives",
   "composite": "fem.shape",
   "signature": "{line2,tri3,quad4,tet4,hex8,wedge6,tri6,quad9,tet10,hex27,quad8,hex20}_{N,dN}(nat: np.ndarray) -> np.ndarray",
   "summary": "Per-element-type shape-function (N: (n_ip,n_nodes)) and derivative (dN: (n_ip,n_nodes,parent_dim)) primitives, exported so users can plug them into custom catalog keys. Natural-coord conventions match Gmsh exactly.",
   "file": "src/apeGmsh/fem/_shape_functions.py:123",
   "flow": [
    {
     "node": "fem.shape",
     "action": "evaluate basis/derivatives at natural coords (pure numpy)",
     "passes": "ndarray N or dN",
     "to": "fem.extract"
    }
   ],
   "inputs": "natural coords ndarray (n_ip,parent_dim)",
   "outputs": "N (n_ip,n_nodes) or dN (n_ip,n_nodes,parent_dim) ndarray",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "fem.shape.get_shape_functions",
   "label": "get_shape_functions",
   "composite": "fem.shape",
   "signature": "get_shape_functions(gmsh_code: int) -> Optional[Tuple[ShapeFn, ShapeFn, GeomKind, int]]",
   "summary": "from apeGmsh.fem import _shape_functions — dispatch by Gmsh element-type code to (N_fn, dN_fn, geom_kind, n_nodes); covers line2..hex27. Returns None for uncatalogued codes. Read by the mass resolver / FEM extract side.",
   "file": "src/apeGmsh/fem/_shape_functions.py:841",
   "flow": [
    {
     "node": "fem.shape",
     "action": "SHAPE_FUNCTIONS_BY_GMSH_CODE.get(code)",
     "passes": "(N_fn, dN_fn, geom_kind, n_nodes) tuple",
     "to": "fem.extract"
    }
   ],
   "inputs": "Gmsh element-type code int",
   "outputs": "shape-function catalog tuple or None",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "g.constraints.bc",
   "label": "bc",
   "composite": "constraints",
   "signature": "bc(target=None, *, pg=None, label=None, tag=None, dofs=None, name=None) -> BCDef",
   "summary": "Declares a homogeneous single-point constraint (fix-to-ground) on a dimension-agnostic target; stored apart from constraint_defs and resolved to fem.nodes.sp, not fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:268",
   "flow": [
    {
     "node": "constraints",
     "action": "coalesce target via loads._coalesce_target, build BCDef, append to self._bc_defs (pre-mesh)",
     "passes": "BCDef(target,target_source,dofs=[1,1,1] default,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "BCDef held until get_fem_data() triggers resolve_bcs",
     "passes": "BCDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "resolve_bcs(): loads._target_nodes resolves target->mesh nodes (label->PG->tag->mesh-selection, any dim), fail-loud on zero nodes; emit one SPRecord per masked DOF per node",
     "passes": "list[SPRecord(node_id,dof,value=0.0,is_homogeneous=True)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "SPRecord list collected into SPSet",
     "passes": "SPSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.sp (same channel as g.loads.face_sp)",
     "passes": "SPSet on fem.nodes.sp",
     "to": "femdata"
    }
   ],
   "inputs": "target (str|list[(dim,tag)]) or pg=/label=/tag=; dofs restraint mask [ux,uy,uz,rx,ry,rz] default [1,1,1]; name",
   "outputs": "BCDef (also appended to self._bc_defs); resolves to homogeneous SPRecords on fem.nodes.sp",
   "reads": [
    "self._parent.loads._coalesce_target",
    "self._bc_defs"
   ],
   "writes": [
    "self._bc_defs",
    "fem.nodes.sp (deferred via resolve_bcs)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.clear",
   "label": "clear",
   "composite": "constraints",
   "signature": "clear() -> None",
   "summary": "Clears all stored constraint defs and resolved records (does not clear _bc_defs).",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1582",
   "flow": [
    {
     "node": "constraints",
     "action": "constraint_defs.clear() and constraint_records.clear()",
     "passes": "(none)",
     "to": "constraint.defs"
    }
   ],
   "inputs": "none",
   "outputs": "None (mutates state)",
   "reads": [],
   "writes": [
    "self.constraint_defs",
    "self.constraint_records"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.distributing_coupling",
   "label": "distributing_coupling",
   "composite": "constraints",
   "signature": "distributing_coupling(master_label, slave_label, *, master_point=(0.,0.,0.), dofs=None, weighting=\"uniform\", name=None) -> DistributingCouplingDef [raises NotImplementedError]",
   "summary": "Not implemented — always raises NotImplementedError (the prior RBE3 implementation silently emitted a mechanically wrong kinematic mean); parameters accepted only so the error message is actionable.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:840",
   "flow": [
    {
     "node": "constraints",
     "action": "raise NotImplementedError immediately (no def stored, no resolution); defence-in-depth twin in ConstraintResolver.resolve_distributing",
     "passes": "NotImplementedError (no data crosses)",
     "to": "constraints"
    }
   ],
   "inputs": "master_label, slave_label, master_point, dofs, weighting, name (all accepted but unused)",
   "outputs": "none — raises NotImplementedError",
   "reads": [],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.embedded",
   "label": "embedded",
   "composite": "constraints",
   "signature": "embedded(host_label, embedded_label, *, tolerance=1.0, host_entities=None, embedded_entities=None, name=None) -> EmbeddedDef",
   "summary": "Declares embedding of lower-dim elements (rebar/stiffeners) inside a tet4/tri3 host; resolver locates each embedded node via barycentric coords and emits InterpolationRecord(kind=embedded) onto fem.elements.constraints (label validation bypassed).",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:884",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def: _DISPATCH-checked but label validation skipped for EmbeddedDef; append EmbeddedDef to constraint_defs (pre-mesh)",
     "passes": "EmbeddedDef(master_label=host,slave_label=embedded,tolerance,host_entities,embedded_entities,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "EmbeddedDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_embedded: _entities_for_label (g.parts or PG), _collect_host_elems gathers tet4/tri3 conn, gmsh getNodes for embedded nodes minus host corners, ConstraintResolver.resolve_embedded barycentric-locates",
     "passes": "host_elems ndarray + embedded set[int] -> list[InterpolationRecord(kind=embedded,slave_node,master_nodes,weights,dofs=[1,2,3])]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve(); InterpolationRecords route to element-side set",
     "passes": "InterpolationRecord list",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "lands on fem.elements.constraints; consumed via interpolations()",
     "passes": "SurfaceConstraintSet on fem.elements.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "host_label, embedded_label; tolerance (reserved/unused); host_entities/embedded_entities [(dim,tag)]; name",
   "outputs": "EmbeddedDef (master_label=host, slave_label=embedded); resolves to InterpolationRecord(kind=embedded) on fem.elements.constraints",
   "reads": [
    "self._parent.parts._instances",
    "gmsh.model physical groups/entities"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.elements.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.equal_dof",
   "label": "equal_dof",
   "composite": "constraints",
   "signature": "equal_dof(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1e-6, name=None) -> EqualDOFDef",
   "summary": "Declares a co-located-node DOF equality between two parts; at mesh time the resolver pairs nodes within tolerance and emits one NodePairRecord per pair onto fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:382",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates _DISPATCH entry + both part labels in g.parts._instances, append EqualDOFDef to constraint_defs (pre-mesh)",
     "passes": "EqualDOFDef(master_label,slave_label,master_entities,slave_entities,dofs,tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "EqualDOFDef held until get_fem_data() calls ConstraintsComposite.resolve",
     "passes": "EqualDOFDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "resolve() dispatch _resolve_node_pair: _resolve_nodes(master/slave) via gmsh getNodes or node_map (fail-loud), then ConstraintResolver.resolve_equal_dof matches pairs within tolerance",
     "passes": "master/slave set[int] -> list[NodePairRecord(kind=equal_dof,master_node,slave_node,dofs)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "NodePairRecord list collected; ConstraintsComposite.resolve wraps in NodeConstraintSet",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label (part labels); master_entities/slave_entities [(dim,tag)]; dofs 1-based list (None=all); tolerance float; name",
   "outputs": "EqualDOFDef (appended to constraint_defs); resolves to NodePairRecord(kind=equal_dof) on fem.nodes.constraints",
   "reads": [
    "self._parent.parts._instances"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.kinematic_coupling",
   "label": "kinematic_coupling",
   "composite": "constraints",
   "signature": "kinematic_coupling(master_label, slave_label, *, master_point=(0.,0.,0.), dofs=None, name=None) -> KinematicCouplingDef",
   "summary": "Declares a one-master-many-slaves coupling on a chosen DOF subset (parent of rigid_diaphragm/rigid_body); resolver emits one NodeGroupRecord(kind=kinematic_coupling, dofs) onto fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:701",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates labels, dofs default [1,2,3,4,5,6], append KinematicCouplingDef to constraint_defs (pre-mesh)",
     "passes": "KinematicCouplingDef(master_label,slave_label,master_point,dofs,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "KinematicCouplingDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_kinematic -> ConstraintResolver.resolve_kinematic_coupling: closest master in set, slaves=set-{master}, dofs=defn.dofs, offsets",
     "passes": "NodeGroupRecord(kind=kinematic_coupling,master_node,slave_nodes,dofs,offsets)",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve()",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints; consumed via pairs() (excluded from rigid_link_groups, DOF-selective)",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label; master_point (x,y,z); dofs 1-based list (default [1..6]); name",
   "outputs": "KinematicCouplingDef; resolves to NodeGroupRecord(kind=kinematic_coupling) on fem.nodes.constraints",
   "reads": [
    "self._parent.parts._instances"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.list_defs",
   "label": "list_defs",
   "composite": "constraints",
   "signature": "list_defs() -> list[dict]",
   "summary": "Returns a list of {kind, master, slave, name} dicts, one per declared constraint def, for inspection of pre-mesh intent.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1530",
   "flow": [
    {
     "node": "constraints",
     "action": "map self.constraint_defs to summary dicts",
     "passes": "list[dict{kind,master,slave,name}]",
     "to": "constraints"
    }
   ],
   "inputs": "none",
   "outputs": "list[dict] of declared constraint defs",
   "reads": [
    "self.constraint_defs"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.list_records",
   "label": "list_records",
   "composite": "constraints",
   "signature": "list_records() -> list[dict]",
   "summary": "Returns per-resolved-record dicts (kind, name, master_node/slave_node/n_slaves where applicable) from the last resolve() call.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1569",
   "flow": [
    {
     "node": "records",
     "action": "map self.constraint_records to dicts (kind,name,master_node,slave_node,n_slaves)",
     "passes": "list[dict] from ConstraintRecord list",
     "to": "constraints"
    }
   ],
   "inputs": "none (reads post-resolve state)",
   "outputs": "list[dict] of resolved constraint records",
   "reads": [
    "self.constraint_records"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.mortar",
   "label": "mortar",
   "composite": "constraints",
   "signature": "mortar(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, integration_order=2, name=None) -> MortarDef [raises NotImplementedError]",
   "summary": "Not implemented — always raises NotImplementedError (the prior implementation was a collocation tie with a unit-dependent hardcoded tolerance mislabelled MORTAR); use tied_contact instead. Defence-in-depth twin in ConstraintResolver.resolve_mortar.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1138",
   "flow": [
    {
     "node": "constraints",
     "action": "raise NotImplementedError immediately (no def stored, no resolution)",
     "passes": "NotImplementedError (no data crosses)",
     "to": "constraints"
    }
   ],
   "inputs": "master_label, slave_label, master_entities, slave_entities, dofs, integration_order, name (accepted but unused)",
   "outputs": "none — raises NotImplementedError",
   "reads": [],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.node_to_surface",
   "label": "node_to_surface",
   "composite": "constraints",
   "signature": "node_to_surface(master, slave, *, dofs=None, tolerance=1e-6, name=None) -> NodeToSurfaceDef",
   "summary": "Declares a 6-DOF node to 3-DOF surface coupling via phantom nodes (bare-tag args, not part labels); resolver creates phantoms, rigid-links master->phantom and equalDOFs phantom->slave, emitting a compound NodeToSurfaceRecord onto fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:955",
   "flow": [
    {
     "node": "constraints",
     "action": "resolve_to_tags(master,dim=0)/(slave,dim=2), validate single master, join slave tags as CSV string, _add_def (label validation skipped) append NodeToSurfaceDef (pre-mesh)",
     "passes": "NodeToSurfaceDef(master_label=str(point_tag),slave_label='t1,t2,...',dofs,tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "NodeToSurfaceDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_node_to_surface: gmsh getNodes for master point entity + union surface nodes, ConstraintResolver.resolve_node_to_surface generates phantom tags, rigid_beam + equal_dof sub-records",
     "passes": "master_node int + slave set[int] -> NodeToSurfaceRecord(kind=node_to_surface,master_node,slave_nodes,phantom_nodes,phantom_coords,rigid_link_records,equal_dof_records,dofs)",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve()",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints; consumed via phantom_nodes()/rigid_link_groups()/equal_dofs()/node_to_surfaces()",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master (int|str|(dim,tag) -> one dim-0 point), slave (int|str|(dim,tag) -> one+ dim-2 surfaces); dofs (None=[1,2,3]); tolerance (ignored); name",
   "outputs": "NodeToSurfaceDef; resolves to compound NodeToSurfaceRecord on fem.nodes.constraints (rigid_beam + equal_dof + phantom nodes)",
   "reads": [
    "apeGmsh.core._helpers.resolve_to_tags",
    "self._parent (session)"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.node_to_surface_spring",
   "label": "node_to_surface_spring",
   "composite": "constraints",
   "signature": "node_to_surface_spring(master, slave, *, dofs=None, tolerance=1e-6, name=None) -> NodeToSurfaceSpringDef",
   "summary": "Stiff-beam variant of node_to_surface for free-rotation masters; identical topology but master->phantom links are tagged rigid_beam_stiff for downstream emission as stiff elasticBeamColumn elements via stiff_beam_groups().",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1009",
   "flow": [
    {
     "node": "constraints",
     "action": "resolve_to_tags(master,dim=0)/(slave,dim=2), validate single master, join slave tags CSV, _add_def append NodeToSurfaceSpringDef (pre-mesh)",
     "passes": "NodeToSurfaceSpringDef(master_label=str(point_tag),slave_label='t1,t2,...',dofs,tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "NodeToSurfaceSpringDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_node_to_surface (shared) -> ConstraintResolver.resolve_node_to_surface_spring: phantom tags, stiff records kind=rigid_beam_stiff + equal_dof records",
     "passes": "master_node int + slave set[int] -> NodeToSurfaceRecord(kind=node_to_surface_spring,rigid_link_records[kind=rigid_beam_stiff],equal_dof_records,phantom_nodes,phantom_coords)",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve()",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints; stiff links consumed via stiff_beam_groups() (skipped by rigid_link_groups), equalDOFs via equal_dofs()",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master (one dim-0 point), slave (one+ dim-2 surfaces); dofs (None=[1,2,3]); tolerance (ignored); name",
   "outputs": "NodeToSurfaceSpringDef; resolves to NodeToSurfaceRecord(kind=node_to_surface_spring) with rigid_beam_stiff links on fem.nodes.constraints",
   "reads": [
    "apeGmsh.core._helpers.resolve_to_tags",
    "self._parent (session)"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.penalty",
   "label": "penalty",
   "composite": "constraints",
   "signature": "penalty(master_label, slave_label, *, stiffness=1e10, dofs=None, tolerance=1e-6, name=None) -> PenaltyDef",
   "summary": "Declares a soft-spring (penalty) coupling between co-located node pairs; resolver matches pairs within tolerance and emits NodePairRecord(kind=penalty, penalty_stiffness=...) onto fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:523",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates labels, append PenaltyDef to constraint_defs (pre-mesh)",
     "passes": "PenaltyDef(master_label,slave_label,stiffness,dofs,tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "PenaltyDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_node_pair -> ConstraintResolver.resolve_penalty: _match_node_pairs within tolerance (fail-loud on many-to-one), kind=penalty",
     "passes": "list[NodePairRecord(kind=penalty,master_node,slave_node,dofs,penalty_stiffness)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve()",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label; stiffness float; dofs 1-based list (None=all); tolerance; name",
   "outputs": "PenaltyDef; resolves to NodePairRecord(kind=penalty) on fem.nodes.constraints",
   "reads": [
    "self._parent.parts._instances"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.resolve",
   "label": "resolve",
   "composite": "constraint.resolver",
   "signature": "resolve(node_tags, node_coords, elem_tags=None, connectivity=None, *, node_map=None, face_map=None) -> NodeConstraintSet",
   "summary": "Stage-2 driver auto-called by get_fem_data(): instantiates ConstraintResolver, walks constraint_defs through the _DISPATCH table resolving names->mesh nodes/faces, and returns a NodeConstraintSet of resolved ConstraintRecords.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1186",
   "flow": [
    {
     "node": "constraint.defs",
     "action": "iterate self.constraint_defs; raise if any face constraint and face_map is None",
     "passes": "list[ConstraintDef]",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "construct ConstraintResolver(node_tags,node_coords,elem_tags,connectivity); per def look up _DISPATCH method, _resolve_nodes/_resolve_faces gather mesh sets, call ConstraintResolver.resolve_* (_RESOLVER_METHOD)",
     "passes": "node/face mesh sets + Def -> ConstraintRecord (NodePairRecord/NodeGroupRecord/InterpolationRecord/SurfaceCouplingRecord/NodeToSurfaceRecord)",
     "to": "records"
    },
    {
     "node": "records",
     "action": "extend self.constraint_records, wrap in NodeConstraintSet(records)",
     "passes": "list[ConstraintRecord] -> NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "node-pair/group/node_to_surface records -> fem.nodes.constraints; interpolation/surface-coupling -> fem.elements.constraints",
     "passes": "NodeConstraintSet split across fem.nodes.constraints / fem.elements.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "node_tags, node_coords, elem_tags, connectivity; node_map={part->set[int]}, face_map={part->ndarray}",
   "outputs": "NodeConstraintSet (also stored on self.constraint_records); records distributed to fem.nodes.constraints and fem.elements.constraints",
   "reads": [
    "self.constraint_defs",
    "self._parent.parts._collect_surface_faces",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.constraint_records",
    "fem.nodes.constraints",
    "fem.elements.constraints"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.resolve_bcs",
   "label": "resolve_bcs",
   "composite": "constraint.resolver",
   "signature": "resolve_bcs(node_tags, *, node_map=None) -> list[SPRecord]",
   "summary": "Stage-2 resolver for bc() defs: maps each BCDef target through the loads composite's dimension-agnostic node resolution and emits one homogeneous SPRecord per restrained DOF per node, failing loud on zero nodes.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:335",
   "flow": [
    {
     "node": "constraint.resolver",
     "action": "for each BCDef call loads._target_nodes(target,node_map,all_nodes,source,expected_dim=None)",
     "passes": "BCDef.target + node_map",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_target_nodes resolves name->DimTags via core._resolution.resolve_target then gmsh.model.mesh.getNodes (or mesh-selection sentinel / part node_map fast-path)",
     "passes": "set[int] mesh node ids",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "fail-loud if zero nodes; emit SPRecord(value=0.0,is_homogeneous=True) per masked DOF per sorted node",
     "passes": "list[SPRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "SPRecord list returned to FEMData assembly, collected into SPSet on fem.nodes.sp",
     "passes": "list[SPRecord] -> SPSet",
     "to": "fem.nodes"
    }
   ],
   "inputs": "node_tags (mesh node ids), node_map={part_label->set[int]}",
   "outputs": "list[SPRecord] (homogeneous fixes) -> fem.nodes.sp",
   "reads": [
    "self._bc_defs",
    "self._parent.loads._target_nodes",
    "apeGmsh.mesh.records._loads.SPRecord"
   ],
   "writes": [
    "return value consumed into fem.nodes.sp"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.rigid_body",
   "label": "rigid_body",
   "composite": "constraints",
   "signature": "rigid_body(master_label, slave_label, *, master_point=(0.,0.,0.), name=None) -> RigidBodyDef",
   "summary": "Declares a fully rigid cluster (all 6 DOFs of every slave follow the master); resolver emits one NodeGroupRecord (dofs=[1..6], offsets) onto fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:655",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates labels, append RigidBodyDef to constraint_defs (pre-mesh)",
     "passes": "RigidBodyDef(master_label,slave_label,master_point,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "RigidBodyDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_kinematic -> ConstraintResolver.resolve_kinematic_coupling: closest master in set, slaves=set-{master}, dofs=[1..6] for RigidBodyDef, offsets",
     "passes": "NodeGroupRecord(kind=rigid_body,master_node,slave_nodes,dofs=[1..6],offsets)",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve()",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints; consumed via rigid_link_groups() (rigid_body included)",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label; master_point (x,y,z); name",
   "outputs": "RigidBodyDef; resolves to NodeGroupRecord(kind=rigid_body) on fem.nodes.constraints",
   "reads": [
    "self._parent.parts._instances"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.rigid_diaphragm",
   "label": "rigid_diaphragm",
   "composite": "constraints",
   "signature": "rigid_diaphragm(master_label, slave_label, *, master_point=(0.,0.,0.), plane_normal=(0.,0.,1.), constrained_dofs=None, plane_tolerance=1.0, name=None) -> RigidDiaphragmDef",
   "summary": "Declares an in-plane rigid floor; resolver collects slave nodes within plane_tolerance of the diaphragm plane and emits one NodeGroupRecord (master + many slaves, plane_normal) onto fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:577",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates labels, constrained_dofs default [1,2,6], append RigidDiaphragmDef to constraint_defs (pre-mesh)",
     "passes": "RigidDiaphragmDef(master_label,slave_label,master_point,plane_normal,constrained_dofs,plane_tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "RigidDiaphragmDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_diaphragm: union master|slave node sets -> ConstraintResolver.resolve_rigid_diaphragm collects plane nodes, picks master, offsets",
     "passes": "(m|s) set[int] -> NodeGroupRecord(kind=rigid_diaphragm,master_node,slave_nodes,dofs,offsets,plane_normal)",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve()",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints; consumed via rigid_diaphragms() (perpDirn from plane_normal)",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label; master_point (x,y,z); plane_normal (nx,ny,nz); constrained_dofs list (default [1,2,6]); plane_tolerance; name",
   "outputs": "RigidDiaphragmDef; resolves to NodeGroupRecord(kind=rigid_diaphragm) on fem.nodes.constraints",
   "reads": [
    "self._parent.parts._instances"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.rigid_link",
   "label": "rigid_link",
   "composite": "constraints",
   "signature": "rigid_link(master_label, slave_label, *, link_type=\"beam\", master_point=None, slave_entities=None, tolerance=1e-6, name=None) -> RigidLinkDef",
   "summary": "Declares a rigid bar (beam=6-DOF, rod=translations-only) from a master node to slave nodes; resolver picks the master by proximity/centroid and emits one offset-bearing NodePairRecord per slave onto fem.nodes.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:454",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates labels, append RigidLinkDef to constraint_defs (pre-mesh)",
     "passes": "RigidLinkDef(master_label,slave_label,link_type,master_point,slave_entities,tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve",
     "passes": "RigidLinkDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_node_pair -> ConstraintResolver.resolve_rigid_link: closest master node to master_point/centroid, offset r=x_slave-x_master per slave, kind=rigid_beam|rigid_rod",
     "passes": "list[NodePairRecord(kind=rigid_beam/rigid_rod,master_node,slave_node,dofs,offset)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve()",
     "passes": "NodeConstraintSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.constraints; consumed via rigid_link_groups()/pairs()",
     "passes": "NodeConstraintSet on fem.nodes.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label; link_type 'beam'|'rod'; master_point (x,y,z)|None; slave_entities [(dim,tag)]; tolerance; name",
   "outputs": "RigidLinkDef; resolves to NodePairRecord(kind=rigid_beam|rigid_rod, offset=...) on fem.nodes.constraints",
   "reads": [
    "self._parent.parts._instances"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.nodes.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.summary",
   "label": "summary",
   "composite": "constraints",
   "signature": "summary() -> pandas.DataFrame",
   "summary": "Returns a DataFrame of declared constraint intent (one row per def: kind, name, master, slave, params) for pre-mesh review.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1536",
   "flow": [
    {
     "node": "constraints",
     "action": "introspect dataclass fields of each ConstraintDef, build pandas rows",
     "passes": "DataFrame[kind,name,master,slave,params]",
     "to": "constraints"
    }
   ],
   "inputs": "none",
   "outputs": "pandas.DataFrame of constraint defs",
   "reads": [
    "self.constraint_defs"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.tie",
   "label": "tie",
   "composite": "constraints",
   "signature": "tie(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1.0, name=None) -> TieDef",
   "summary": "Declares a non-matching-mesh tie via shape-function interpolation; resolver projects each slave node onto the closest master face and emits one InterpolationRecord (weights) onto fem.elements.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:749",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates labels, append TieDef to constraint_defs (pre-mesh)",
     "passes": "TieDef(master_label,slave_label,master_entities,slave_entities,dofs,tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve (requires face_map; raises if None)",
     "passes": "TieDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_face_slave: _resolve_faces(master)->surface connectivity, _resolve_nodes(slave)->node set, ConstraintResolver.resolve_tie projects via _project_point_to_face + SHAPE_FUNCTIONS",
     "passes": "master_face_conn ndarray + slave set[int] -> list[InterpolationRecord(kind=tie,slave_node,master_nodes,weights,dofs,projected_point,parametric_coords)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve(); InterpolationRecords route to the element-side SurfaceConstraintSet",
     "passes": "InterpolationRecord list",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "lands on fem.elements.constraints; consumed via interpolations()",
     "passes": "SurfaceConstraintSet on fem.elements.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label; master_entities/slave_entities [(dim,tag)]; dofs (None=all translational); tolerance projection distance; name",
   "outputs": "TieDef; resolves to InterpolationRecord(kind=tie) on fem.elements.constraints",
   "reads": [
    "self._parent.parts._instances",
    "self._parent.parts._collect_surface_faces"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.elements.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.tied_contact",
   "label": "tied_contact",
   "composite": "constraints",
   "signature": "tied_contact(master_label, slave_label, *, master_entities=None, slave_entities=None, dofs=None, tolerance=1.0, name=None) -> TiedContactDef",
   "summary": "Declares a one-directional surface-to-surface tie (slave conforms to master); resolver projects slave nodes onto master faces and emits a SurfaceCouplingRecord wrapping per-slave InterpolationRecords onto fem.elements.constraints.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1085",
   "flow": [
    {
     "node": "constraints",
     "action": "_add_def validates labels, append TiedContactDef to constraint_defs (pre-mesh)",
     "passes": "TiedContactDef(master_label,slave_label,master_entities,slave_entities,dofs,tolerance,name)",
     "to": "constraint.defs"
    },
    {
     "node": "constraint.defs",
     "action": "held until get_fem_data() -> ConstraintsComposite.resolve (face constraint; requires face_map)",
     "passes": "TiedContactDef",
     "to": "constraint.resolver"
    },
    {
     "node": "constraint.resolver",
     "action": "_resolve_face_both: master/slave faces + master/slave nodes, ConstraintResolver.resolve_tied_contact builds fwd TieDef and resolve_tie (one-directional, slave_face_conn unused)",
     "passes": "master/slave face_conn + node sets -> SurfaceCouplingRecord(kind=tied_contact,slave_records=[InterpolationRecord],master_nodes,slave_nodes,dofs)",
     "to": "records"
    },
    {
     "node": "records",
     "action": "collected into NodeConstraintSet by resolve(); SurfaceCouplingRecord routes to element-side set",
     "passes": "SurfaceCouplingRecord",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "lands on fem.elements.constraints; consumed via couplings()/interpolations()",
     "passes": "SurfaceConstraintSet on fem.elements.constraints",
     "to": "femdata"
    }
   ],
   "inputs": "master_label, slave_label; master_entities/slave_entities [(dim,tag)]; dofs (None=all translational); tolerance; name",
   "outputs": "TiedContactDef; resolves to SurfaceCouplingRecord(kind=tied_contact) on fem.elements.constraints",
   "reads": [
    "self._parent.parts._instances",
    "self._parent.parts._collect_surface_faces"
   ],
   "writes": [
    "self.constraint_defs",
    "fem.elements.constraints (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.constraints.validate_pre_mesh",
   "label": "validate_pre_mesh",
   "composite": "constraints",
   "signature": "validate_pre_mesh() -> None",
   "summary": "No-op for constraints (targets are validated eagerly at _add_def); present so Mesh.generate can call validate_pre_mesh uniformly across the three definition composites.",
   "file": "src/apeGmsh/core/ConstraintsComposite.py:1175",
   "flow": [
    {
     "node": "constraints",
     "action": "return None (validation already done at _add_def time)",
     "passes": "None",
     "to": "constraints"
    }
   ],
   "inputs": "none",
   "outputs": "None",
   "reads": [],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.labels.add",
   "label": "add",
   "composite": "labels",
   "signature": "g.labels.add(dim: int, tags: list[int], name: str) -> int",
   "summary": "Create (or merge into) an internal '_label:'-prefixed physical group naming geometry entities; warns on same-dim merge and cross-dim shadowing.",
   "file": "src/apeGmsh/core/Labels.py:420",
   "flow": [
    {
     "node": "labels",
     "action": "prefix name, build (dim,name)->pg_tag index in one pass",
     "passes": "prefixed='_label:'+name",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "if exists at dim: merge tags, remove+re-add PG (warn on new tags)",
     "passes": "merged tags: list[int]",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "else addPhysicalGroup + setPhysicalName (warn cross-dim)",
     "passes": "dim, tags, prefixed",
     "to": "gmsh"
    }
   ],
   "inputs": "dim (0-3), tags list[int], name (bare label)",
   "outputs": "int (Gmsh physical-group tag backing the label)",
   "reads": [
    "gmsh.model.getPhysicalGroups(-1) / getPhysicalName / getEntitiesForPhysicalGroup"
   ],
   "writes": [
    "gmsh.model.addPhysicalGroup / setPhysicalName / removePhysicalGroups (label PG)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.entities",
   "label": "entities",
   "composite": "labels",
   "signature": "g.labels.entities(name: str, *, dim: int | None = None) -> list[int]",
   "summary": "Return entity tags for a label; KeyError if absent, ValueError if dim=None and the label spans multiple dimensions.",
   "file": "src/apeGmsh/core/Labels.py:509",
   "flow": [
    {
     "node": "labels",
     "action": "prefix name; if dim given: direct PG scan at that dim",
     "passes": "prefixed, dim",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "dim=None: scan all dims, require single unambiguous match",
     "passes": "matches: list[(dim,pg_tag)]",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "return getEntitiesForPhysicalGroup of the match",
     "passes": "entity tags: list[int]",
     "to": "gmsh"
    }
   ],
   "inputs": "name (bare label); optional dim=int",
   "outputs": "list[int] entity tags",
   "reads": [
    "gmsh.model.getPhysicalGroups / getPhysicalName / getEntitiesForPhysicalGroup"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.get_all",
   "label": "get_all",
   "composite": "labels",
   "signature": "g.labels.get_all(*, dim: int = -1) -> list[str]",
   "summary": "Return all label names (prefix stripped, sorted, deduped), optionally filtered by dimension.",
   "file": "src/apeGmsh/core/Labels.py:584",
   "flow": [
    {
     "node": "labels",
     "action": "walk getPhysicalGroups(dim), keep _label: PGs, strip prefix",
     "passes": "dim filter",
     "to": "gmsh"
    }
   ],
   "inputs": "optional dim=int (-1 = all)",
   "outputs": "list[str] sorted unique label names",
   "reads": [
    "gmsh.model.getPhysicalGroups / getPhysicalName"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.has",
   "label": "has",
   "composite": "labels",
   "signature": "g.labels.has(name: str, *, dim: int | None = None) -> bool",
   "summary": "Return True if a label with this name (optionally at a given dim) exists, by attempting entities() and catching KeyError.",
   "file": "src/apeGmsh/core/Labels.py:634",
   "flow": [
    {
     "node": "labels",
     "action": "try entities(name, dim=); True unless KeyError",
     "passes": "name, dim",
     "to": "labels"
    }
   ],
   "inputs": "name; optional dim=int",
   "outputs": "bool",
   "reads": [
    "self.entities (-> gmsh PGs)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.labels_for_entity",
   "label": "labels_for_entity",
   "composite": "labels",
   "signature": "g.labels.labels_for_entity(dim: int, tag: int) -> list[str]",
   "summary": "Return all label names whose backing PG (at the given dim) contains the given entity tag.",
   "file": "src/apeGmsh/core/Labels.py:798",
   "flow": [
    {
     "node": "labels",
     "action": "scan label PGs at dim, collect names containing tag",
     "passes": "dim, tag",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "list[str] label names containing the entity",
   "reads": [
    "gmsh.model.getPhysicalGroups / getPhysicalName / getEntitiesForPhysicalGroup"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.promote_to_physical",
   "label": "promote_to_physical",
   "composite": "labels",
   "signature": "g.labels.promote_to_physical(label_name: str, *, pg_name=None, dim=None) -> int",
   "summary": "Copy (not move) a label's entities into a new solver-facing physical group visible to g.physical / fem.physical / the OpenSees exporter.",
   "file": "src/apeGmsh/core/Labels.py:723",
   "flow": [
    {
     "node": "labels",
     "action": "resolve tags via entities(label, dim=)",
     "passes": "label_name, dim",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve dim from the label's PG if not given",
     "passes": "resolved_dim: int",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "addPhysicalGroup + setPhysicalName with unprefixed name",
     "passes": "out_name (solver-facing PG)",
     "to": "physical"
    }
   ],
   "inputs": "label_name; optional pg_name (default label name), dim=int",
   "outputs": "int (physical-group tag of the new solver PG); label left intact",
   "reads": [
    "self.entities",
    "gmsh.model.getPhysicalGroups / getPhysicalName"
   ],
   "writes": [
    "gmsh.model.addPhysicalGroup / setPhysicalName (solver-facing PG, no _label: prefix)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.remove",
   "label": "remove",
   "composite": "labels",
   "signature": "g.labels.remove(name: str, *, dim: int | None = None) -> None",
   "summary": "Delete a label and its backing physical group at one or all dimensions; KeyError if not found.",
   "file": "src/apeGmsh/core/Labels.py:646",
   "flow": [
    {
     "node": "labels",
     "action": "prefix name, scan target dims for matching PG",
     "passes": "prefixed, dims: list[int]",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "removePhysicalGroups for each match; KeyError if none",
     "passes": "(dim,pg_tag) pairs",
     "to": "gmsh"
    }
   ],
   "inputs": "name; optional dim=int (None = all dims)",
   "outputs": "None (label PG removed)",
   "reads": [
    "gmsh.model.getPhysicalGroups / getPhysicalName"
   ],
   "writes": [
    "gmsh.model.removePhysicalGroups"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.rename",
   "label": "rename",
   "composite": "labels",
   "signature": "g.labels.rename(old_name: str, new_name: str, *, dim: int | None = None) -> None",
   "summary": "Rename a label in place (remove + re-add PG preserving entity membership) at one or all dimensions; KeyError if old not found.",
   "file": "src/apeGmsh/core/Labels.py:678",
   "flow": [
    {
     "node": "labels",
     "action": "prefix both names, scan dims for old PG",
     "passes": "old_prefixed, new_prefixed",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "read entities, removePhysicalGroups, re-add under new name",
     "passes": "entity tags: list[int]",
     "to": "gmsh"
    }
   ],
   "inputs": "old_name, new_name; optional dim=int",
   "outputs": "None (label PG renamed, entities preserved)",
   "reads": [
    "gmsh.model.getPhysicalGroups / getPhysicalName / getEntitiesForPhysicalGroup"
   ],
   "writes": [
    "gmsh.model.removePhysicalGroups / addPhysicalGroup / setPhysicalName"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.reverse_map",
   "label": "reverse_map",
   "composite": "labels",
   "signature": "g.labels.reverse_map(*, dim: int = -1) -> dict[(int,int), str]",
   "summary": "Build a (dim,tag)->label_name reverse lookup over all label PGs for bulk entity->label resolution.",
   "file": "src/apeGmsh/core/Labels.py:777",
   "flow": [
    {
     "node": "labels",
     "action": "walk label PGs, map each entity (dim,tag) to stripped name",
     "passes": "dim filter",
     "to": "gmsh"
    }
   ],
   "inputs": "optional dim=int (-1 = all)",
   "outputs": "dict {(dim,tag): label_name}",
   "reads": [
    "gmsh.model.getPhysicalGroups / getPhysicalName / getEntitiesForPhysicalGroup"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.labels.summary",
   "label": "summary",
   "composite": "labels",
   "signature": "g.labels.summary() -> pd.DataFrame",
   "summary": "Return a DataFrame (indexed by dim,pg_tag) of every label PG with name, entity count, and entity tags.",
   "file": "src/apeGmsh/core/Labels.py:599",
   "flow": [
    {
     "node": "labels",
     "action": "walk all PGs, filter _label:, build rows",
     "passes": "rows: list[dict]",
     "to": "gmsh"
    },
    {
     "node": "labels",
     "action": "build DataFrame indexed by (dim,pg_tag)",
     "passes": "pandas DataFrame",
     "to": "labels"
    }
   ],
   "inputs": "none",
   "outputs": "pd.DataFrame (dim,pg_tag index; columns name/n_entities/entity_tags)",
   "reads": [
    "gmsh.model.getPhysicalGroups / getPhysicalName / getEntitiesForPhysicalGroup"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.loader.from_msh",
   "label": "from_msh",
   "composite": "loader",
   "signature": "from_msh(path, *, dim=2) -> FEMData",
   "summary": "Merge a .msh file into the active Gmsh session and extract a FEMData (composites stay usable).",
   "file": "src/apeGmsh/mesh/MshLoader.py:154",
   "flow": [
    {
     "node": "loader",
     "action": "validate path and require an active session",
     "passes": "_validate_path(path) -> Path; self._parent.is_active check",
     "to": "loader"
    },
    {
     "node": "loader",
     "action": "merge the .msh into the live gmsh model",
     "passes": "gmsh.merge(str(p))",
     "to": "gmsh"
    },
    {
     "node": "loader",
     "action": "extract FEMData from the merged live mesh via the FEMData/_fem_extract engine",
     "passes": "FEMData.from_gmsh(dim:int)",
     "to": "fem.extract"
    },
    {
     "node": "fem.extract",
     "action": "build the composite broker (nodes/elements/info + physical snapshot) from the merged mesh",
     "passes": "FEMData(info.n_nodes, info.n_elems, info.bandwidth)",
     "to": "femdata"
    }
   ],
   "inputs": "path str|Path, dim",
   "outputs": "FEMData object",
   "reads": [
    ".msh file on disk",
    "live gmsh mesh after merge"
   ],
   "writes": [
    "live gmsh model (merged mesh)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.loads.body",
   "label": "body",
   "composite": "loads",
   "signature": "body(target=None, *, pg=None, label=None, tag=None, force_per_volume=(0.,0.,0.), reduction=\"tributary\", target_form=\"nodal\", name=None) -> BodyLoadDef",
   "summary": "Declares a generic per-volume body force (force per unit volume) over volume(s); resolver distributes bf·V per element to nodes or emits per-element ElementLoadRecords(bodyForce, params{bf}).",
   "file": "src/apeGmsh/core/LoadsComposite.py:634",
   "flow": [
    {
     "node": "loads",
     "action": "_coalesce_target, _add_def validates (reduction,target_form), append BodyLoadDef (pre-mesh)",
     "passes": "BodyLoadDef(target,target_source,pattern,name,force_per_volume,reduction,target_form)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "BodyLoadDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_body_tributary/_element: _target_elements gathers conns/eids, LoadResolver.resolve_body_tributary (consistent aliases tributary) / resolve_body_element",
     "passes": "conns/eids -> list[NodalLoadRecord] or list[ElementLoadRecord(load_type=bodyForce,params{bf})]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "nodal -> NodalLoadSet; element -> ElementLoadSet",
     "passes": "NodalLoadSet / ElementLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.loads / fem.elements.loads",
     "passes": "records on fem.nodes.loads / fem.elements.loads",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; force_per_volume (bx,by,bz); reduction; target_form; name",
   "outputs": "BodyLoadDef; resolves to NodalLoadRecord (fem.nodes.loads) or ElementLoadRecord bodyForce (fem.elements.loads)",
   "reads": [
    "self._active_pattern",
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.load_defs",
    "fem.nodes.loads / fem.elements.loads (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.by_pattern",
   "label": "by_pattern",
   "composite": "loads",
   "signature": "by_pattern(name: str) -> list[LoadDef]",
   "summary": "Returns all stored load defs whose pattern matches the given name (pre-mesh def-level filter).",
   "file": "src/apeGmsh/core/LoadsComposite.py:1803",
   "flow": [
    {
     "node": "loads",
     "action": "filter self.load_defs by defn.pattern == name",
     "passes": "list[LoadDef]",
     "to": "loads"
    }
   ],
   "inputs": "name (pattern label)",
   "outputs": "list[LoadDef]",
   "reads": [
    "self.load_defs"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.face_load",
   "label": "face_load",
   "composite": "loads",
   "signature": "face_load(target=None, *, pg=None, label=None, tag=None, force_xyz=None, moment_xyz=None, magnitude=0.0, normal=False, direction=None, name=None) -> FaceLoadDef",
   "summary": "Declares a centroidal force/moment on a face distributed directly to its nodes (force split equally, moment via least-norm distribution, magnitude+normal/direction derived); resolver emits NodalLoadRecords onto fem.nodes.loads (no reference-node coupling).",
   "file": "src/apeGmsh/core/LoadsComposite.py:699",
   "flow": [
    {
     "node": "loads",
     "action": "validate force_xyz/moment_xyz/magnitude/normal/direction combos, _coalesce_target, _add_def append FaceLoadDef (pre-mesh)",
     "passes": "FaceLoadDef(target,target_source,pattern,name,force_xyz,moment_xyz,magnitude,normal,direction)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "FaceLoadDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_face_load: _target_nodes(expected_dim=2) + (for normal-magnitude) _target_faces + _face_outward_normals, LoadResolver.resolve_face_load (equal split + _moment_to_nodal_forces least-norm)",
     "passes": "sorted nodes + faces/outwards -> list[NodalLoadRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "extend records; wrapped in NodalLoadSet",
     "passes": "NodalLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.loads",
     "passes": "NodalLoadSet on fem.nodes.loads",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; force_xyz; moment_xyz; magnitude (Newtons) + normal/direction; name",
   "outputs": "FaceLoadDef; resolves to NodalLoadRecord on fem.nodes.loads",
   "reads": [
    "self._active_pattern",
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.load_defs",
    "fem.nodes.loads (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.face_sp",
   "label": "face_sp",
   "composite": "loads",
   "signature": "face_sp(target=None, *, pg=None, label=None, tag=None, dofs=None, disp_xyz=None, rot_xyz=None, magnitude=0.0, normal=False, direction=None, name=None) -> FaceSPDef",
   "summary": "Declares a prescribed displacement/rotation at a face centroid mapped to face nodes (u_i = disp + rot×r_i; all-zero => homogeneous fix); resolver emits SPRecords onto fem.nodes.sp.",
   "file": "src/apeGmsh/core/LoadsComposite.py:781",
   "flow": [
    {
     "node": "loads",
     "action": "validate disp_xyz/magnitude/normal/direction combos, _coalesce_target, _add_def append FaceSPDef (dofs default [1,1,1]) (pre-mesh)",
     "passes": "FaceSPDef(target,target_source,pattern,name,dofs,disp_xyz,rot_xyz,magnitude,normal,direction)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "FaceSPDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_face_sp: _target_nodes(expected_dim=2) + (normal-magnitude) faces/outwards, LoadResolver.resolve_face_sp maps centroidal rigid-body motion to per-node SPRecords",
     "passes": "sorted nodes + faces/outwards -> list[SPRecord(pattern,name,node_id,dof,value,is_homogeneous)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "SPRecord list collected into SPSet",
     "passes": "SPSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.sp (same channel as g.constraints.bc)",
     "passes": "SPSet on fem.nodes.sp",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; dofs restraint mask (default [1,1,1]); disp_xyz; rot_xyz; magnitude + normal/direction; name",
   "outputs": "FaceSPDef; resolves to SPRecord on fem.nodes.sp",
   "reads": [
    "self._active_pattern",
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.load_defs",
    "fem.nodes.sp (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.gravity",
   "label": "gravity",
   "composite": "loads",
   "signature": "gravity(target=None, *, pg=None, label=None, tag=None, g=(0.,0.,-9.81), density=None, reduction=\"tributary\", target_form=\"nodal\", name=None) -> GravityLoadDef",
   "summary": "Declares body weight (ρ·g) over volume(s); resolver distributes ρ·V·g per element to nodes (tributary/consistent, density required for nodal) or emits per-element ElementLoadRecords(bodyForce) onto fem.elements.loads.",
   "file": "src/apeGmsh/core/LoadsComposite.py:545",
   "flow": [
    {
     "node": "loads",
     "action": "_coalesce_target, _add_def validates (reduction,target_form), append GravityLoadDef (pre-mesh)",
     "passes": "GravityLoadDef(target,target_source,pattern,name,g,density,reduction,target_form)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "GravityLoadDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_gravity_tributary/_consistent/_element: _target_elements gathers volume connectivity/eids, LoadResolver.resolve_gravity_tributary (raises if density None) / consistent / element",
     "passes": "conns/eids -> list[NodalLoadRecord] or list[ElementLoadRecord(load_type=bodyForce,params{g,density})]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "nodal -> NodalLoadSet; element -> ElementLoadSet",
     "passes": "NodalLoadSet / ElementLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.loads / fem.elements.loads",
     "passes": "records on fem.nodes.loads / fem.elements.loads",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; g vector; density (required for nodal); reduction; target_form; name",
   "outputs": "GravityLoadDef; resolves to NodalLoadRecord (fem.nodes.loads) or ElementLoadRecord bodyForce (fem.elements.loads)",
   "reads": [
    "self._active_pattern",
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.load_defs",
    "fem.nodes.loads / fem.elements.loads (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.line",
   "label": "line",
   "composite": "loads",
   "signature": "line(target=None, *, pg=None, label=None, tag=None, magnitude=None, direction=(0.,0.,-1.), q_xyz=None, normal=False, away_from=None, reduction=\"tributary\", target_form=\"nodal\", name=None) -> LineLoadDef",
   "summary": "Declares a distributed force-per-length along curve(s) (magnitude+direction, q_xyz, or normal-pressure; magnitude may be callable); resolver reduces by tributary/consistent to NodalLoadRecords or per-element ElementLoadRecords(beamUniform).",
   "file": "src/apeGmsh/core/LoadsComposite.py:280",
   "flow": [
    {
     "node": "loads",
     "action": "validate magnitude/q_xyz/normal combos, _coalesce_target, _add_def validates (reduction,target_form) in _DISPATCH, append LineLoadDef (pre-mesh)",
     "passes": "LineLoadDef(target,target_source,pattern,name,magnitude,direction,q_xyz,normal,away_from,reduction,target_form)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "LineLoadDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_line_tributary/_consistent/_element: _target_edges(_full)/_collect_line_normal_items/_collect_line_vector_items/_iter_curve_edges build edge data, LoadResolver.resolve_line_* (tributary, consistent, per_edge, varying, element, element_varying)",
     "passes": "edges/items -> list[NodalLoadRecord] or list[ElementLoadRecord(load_type=beamUniform,params)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "nodal -> NodalLoadSet (fem.nodes.loads); element -> ElementLoadSet (fem.elements.loads)",
     "passes": "NodalLoadSet / ElementLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "nodal records land on fem.nodes.loads; element records on fem.elements.loads",
     "passes": "records on fem.nodes.loads / fem.elements.loads",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; magnitude (float|callable) or q_xyz; direction (vec|'x'/'y'/'z'); normal+away_from; reduction tributary|consistent; target_form nodal|element; name",
   "outputs": "LineLoadDef; resolves to NodalLoadRecord (fem.nodes.loads) or ElementLoadRecord beamUniform (fem.elements.loads)",
   "reads": [
    "self._active_pattern",
    "core._resolution.resolve_target",
    "gmsh.model.mesh",
    "_load_resolver._direction_vec"
   ],
   "writes": [
    "self.load_defs",
    "fem.nodes.loads / fem.elements.loads (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.pattern",
   "label": "pattern",
   "composite": "loads",
   "signature": "pattern(name: str) -> contextmanager (Iterator[None])",
   "summary": "Context manager that groups every load def created inside the `with` block under the named pattern; restores the previous active pattern on exit.",
   "file": "src/apeGmsh/core/LoadsComposite.py:145",
   "flow": [
    {
     "node": "loads",
     "action": "set self._active_pattern=name on enter, restore prev on exit; subsequent factory calls stamp LoadDef.pattern",
     "passes": "active pattern name (str) stamped onto every LoadDef built in scope",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "patterned LoadDefs held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "list[LoadDef] (pattern=name)",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "resolve() carries defn.pattern through to NodalLoadRecord/ElementLoadRecord/SPRecord.pattern",
     "passes": "pattern str on each record",
     "to": "records"
    },
    {
     "node": "records",
     "action": "records grouped by pattern, exposed via NodalLoadSet.by_pattern/patterns and ElementLoadSet.by_pattern/patterns",
     "passes": "pattern-tagged record sets",
     "to": "fem.nodes"
    }
   ],
   "inputs": "name (pattern label, str)",
   "outputs": "context manager; mutates self._active_pattern for the block",
   "reads": [
    "self._active_pattern"
   ],
   "writes": [
    "self._active_pattern"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.patterns",
   "label": "patterns",
   "composite": "loads",
   "signature": "patterns() -> list[str]",
   "summary": "Returns the unique load-pattern names across all stored defs in insertion order.",
   "file": "src/apeGmsh/core/LoadsComposite.py:1806",
   "flow": [
    {
     "node": "loads",
     "action": "collect distinct defn.pattern in order",
     "passes": "list[str]",
     "to": "loads"
    }
   ],
   "inputs": "none",
   "outputs": "list[str] of pattern names",
   "reads": [
    "self.load_defs"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.point",
   "label": "point",
   "composite": "loads",
   "signature": "point(target=None, *, pg=None, label=None, tag=None, force_xyz=None, moment_xyz=None, name=None) -> PointLoadDef",
   "summary": "Declares a concentrated force/moment applied at every node of a named target; resolver applies the same force6 to all targeted nodes and emits NodalLoadRecords onto fem.nodes.loads.",
   "file": "src/apeGmsh/core/LoadsComposite.py:171",
   "flow": [
    {
     "node": "loads",
     "action": "_coalesce_target, _add_def validates _DISPATCH (reduction,target_form), append PointLoadDef (pattern stamped) (pre-mesh)",
     "passes": "PointLoadDef(target,target_source,pattern,name,force_xyz,moment_xyz)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "PointLoadDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_point: _target_nodes resolves target->mesh node set, LoadResolver.resolve_point applies _to_force6 to each node",
     "passes": "set[int] -> list[NodalLoadRecord(pattern,name,node_id,force_xyz,moment_xyz)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "extend records; LoadsComposite.resolve wraps in NodalLoadSet",
     "passes": "NodalLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.loads",
     "passes": "NodalLoadSet on fem.nodes.loads",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; force_xyz (Fx,Fy,Fz); moment_xyz (Mx,My,Mz) or (Mz,); name",
   "outputs": "PointLoadDef; resolves to NodalLoadRecord on fem.nodes.loads",
   "reads": [
    "self._active_pattern",
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.load_defs",
    "fem.nodes.loads (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.point_closest",
   "label": "point_closest",
   "composite": "loads",
   "signature": "point_closest(xyz, *, within=None, pg=None, label=None, tag=None, force_xyz=None, moment_xyz=None, tol=None, name=None) -> PointClosestLoadDef",
   "summary": "Declares a concentrated load at the mesh node(s) closest to a world coordinate (optionally restricted to a `within` pool); resolver snaps at resolve time, writes back snap_distance, and emits NodalLoadRecords onto fem.nodes.loads.",
   "file": "src/apeGmsh/core/LoadsComposite.py:245",
   "flow": [
    {
     "node": "loads",
     "action": "require force_xyz or moment_xyz, coalesce within scope, _add_def append PointClosestLoadDef (pattern stamped) (pre-mesh)",
     "passes": "PointClosestLoadDef(target=xyz,target_source='closest_xyz',pattern,name,force_xyz,moment_xyz,xyz_request,within,within_source,tol)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "PointClosestLoadDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_point_closest: _snap_node_xyz finds nearest node(s) within optional within/tol, sets defn.snap_distance (warns if snapped), LoadResolver.resolve_point",
     "passes": "snapped node ids -> list[NodalLoadRecord]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "extend records; wrapped in NodalLoadSet",
     "passes": "NodalLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.loads",
     "passes": "NodalLoadSet on fem.nodes.loads",
     "to": "femdata"
    }
   ],
   "inputs": "xyz (x,y,z); within (PG/label/part/DimTag) or pg=/label=/tag=; force_xyz/moment_xyz (one required); tol (radius, None=single nearest); name",
   "outputs": "PointClosestLoadDef (snap_distance written back post-resolve); resolves to NodalLoadRecord on fem.nodes.loads",
   "reads": [
    "self._active_pattern",
    "resolver.node_tags/node_coords",
    "core._resolution.resolve_target"
   ],
   "writes": [
    "self.load_defs",
    "defn.snap_distance (post-resolve)",
    "fem.nodes.loads (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.resolve",
   "label": "resolve",
   "composite": "load.resolver",
   "signature": "resolve(node_tags, node_coords, elem_tags=None, connectivity=None, *, node_map=None, face_map=None) -> NodalLoadSet",
   "summary": "Stage-2 driver auto-called by get_fem_data(): instantiates LoadResolver, walks load_defs via the (reduction,target_form) _DISPATCH table resolving names->mesh nodes/edges/faces/elements, and returns a NodalLoadSet of resolved records.",
   "file": "src/apeGmsh/core/LoadsComposite.py:1261",
   "flow": [
    {
     "node": "load.defs",
     "action": "iterate self.load_defs; look up _DISPATCH[(type)][(reduction,target_form)]",
     "passes": "list[LoadDef]",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "construct LoadResolver(node_tags,node_coords,elem_tags,connectivity); per def gather mesh data (_target_nodes/edges/faces/elements) and call LoadResolver.resolve_*",
     "passes": "mesh data + LoadDef -> NodalLoadRecord / ElementLoadRecord / SPRecord",
     "to": "records"
    },
    {
     "node": "records",
     "action": "extend self.load_records, wrap in NodalLoadSet(records)",
     "passes": "list[LoadRecord] -> NodalLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "NodalLoadRecord -> fem.nodes.loads; ElementLoadRecord -> fem.elements.loads; SPRecord -> fem.nodes.sp",
     "passes": "records distributed across fem.nodes.loads / fem.elements.loads / fem.nodes.sp",
     "to": "femdata"
    }
   ],
   "inputs": "node_tags, node_coords, elem_tags, connectivity; node_map, face_map",
   "outputs": "NodalLoadSet (also stored on self.load_records); records distributed to fem.nodes.loads / fem.elements.loads / fem.nodes.sp",
   "reads": [
    "self.load_defs",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.load_records",
    "fem.nodes.loads",
    "fem.elements.loads",
    "fem.nodes.sp"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.summary",
   "label": "summary",
   "composite": "loads",
   "signature": "summary() -> pandas.DataFrame",
   "summary": "Returns a DataFrame of declared load intent (kind, name, pattern, target, source, reduction, target_form, params), one row per def.",
   "file": "src/apeGmsh/core/LoadsComposite.py:1813",
   "flow": [
    {
     "node": "loads",
     "action": "introspect dataclass fields of each LoadDef, build pandas rows",
     "passes": "DataFrame[kind,name,pattern,target,source,reduction,target_form,params]",
     "to": "loads"
    }
   ],
   "inputs": "none",
   "outputs": "pandas.DataFrame of load defs",
   "reads": [
    "self.load_defs"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.surface",
   "label": "surface",
   "composite": "loads",
   "signature": "surface(target=None, *, pg=None, label=None, tag=None, magnitude=0.0, normal=True, direction=(0.,0.,-1.), reduction=\"tributary\", target_form=\"nodal\", name=None) -> SurfaceLoadDef",
   "summary": "Declares pressure (normal=True) or vector traction (normal=False) on surface(s); resolver reduces by tributary/consistent (per-face physical outward normals) to NodalLoadRecords or per-face ElementLoadRecords(surfacePressure).",
   "file": "src/apeGmsh/core/LoadsComposite.py:445",
   "flow": [
    {
     "node": "loads",
     "action": "_coalesce_target, _add_def validates (reduction,target_form), append SurfaceLoadDef (pre-mesh)",
     "passes": "SurfaceLoadDef(target,target_source,pattern,name,magnitude,normal,direction,reduction,target_form)",
     "to": "load.defs"
    },
    {
     "node": "load.defs",
     "action": "held until get_fem_data() -> LoadsComposite.resolve",
     "passes": "SurfaceLoadDef",
     "to": "load.resolver"
    },
    {
     "node": "load.resolver",
     "action": "_resolve_surface_tributary/_consistent/_element: _target_faces(_full) + _face_outward_normals, LoadResolver.resolve_surface_tributary/consistent/element",
     "passes": "faces+outwards -> list[NodalLoadRecord] or list[ElementLoadRecord(load_type=surfacePressure,params)]",
     "to": "records"
    },
    {
     "node": "records",
     "action": "nodal -> NodalLoadSet; element -> ElementLoadSet",
     "passes": "NodalLoadSet / ElementLoadSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.loads / fem.elements.loads",
     "passes": "records on fem.nodes.loads / fem.elements.loads",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; magnitude (pressure or traction); normal bool; direction vec; reduction; target_form; name",
   "outputs": "SurfaceLoadDef; resolves to NodalLoadRecord (fem.nodes.loads) or ElementLoadRecord surfacePressure (fem.elements.loads)",
   "reads": [
    "self._active_pattern",
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.load_defs",
    "fem.nodes.loads / fem.elements.loads (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.loads.validate_pre_mesh",
   "label": "validate_pre_mesh",
   "composite": "loads",
   "signature": "validate_pre_mesh() -> None",
   "summary": "Eagerly resolves every string load target via _resolve_target so typos fail fast before meshing; raw (dim,tag) lists are skipped.",
   "file": "src/apeGmsh/core/LoadsComposite.py:891",
   "flow": [
    {
     "node": "loads",
     "action": "for each LoadDef with str target, call _resolve_target -> core._resolution.resolve_target (raises on miss)",
     "passes": "LoadDef.target str -> resolved DimTags (validation only)",
     "to": "load.defs"
    }
   ],
   "inputs": "none (iterates self.load_defs)",
   "outputs": "None (raises KeyError on unresolvable target)",
   "reads": [
    "self.load_defs",
    "core._resolution.resolve_target"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.masses.line",
   "label": "line",
   "composite": "masses",
   "signature": "line(target=None, *, pg=None, label=None, tag=None, linear_density: float, rotational=None, dofs=None, reduction=\"lumped\", name=None) -> LineMassDef",
   "summary": "Declares a distributed line mass (mass per unit length) on curve(s); resolver distributes by tributary length (lumped) or consistent line2 matrix and accumulates into the MassSet on fem.nodes.masses.",
   "file": "src/apeGmsh/core/MassesComposite.py:312",
   "flow": [
    {
     "node": "masses",
     "action": "_coalesce_target, validate dofs/rotational, _add_def append LineMassDef (pre-mesh)",
     "passes": "LineMassDef(target,target_source,name,reduction,dofs,linear_density,rotational)",
     "to": "mass.defs"
    },
    {
     "node": "mass.defs",
     "action": "held until get_fem_data() -> MassesComposite.resolve",
     "passes": "LineMassDef",
     "to": "mass.resolver"
    },
    {
     "node": "mass.resolver",
     "action": "_resolve_line_lumped/_consistent: _target_edges resolves curve edges, MassResolver.resolve_line_lumped (½ρ_lL each) or resolve_line_consistent; resolve() accumulates per node",
     "passes": "list[(n1,n2)] -> list[MassRecord] then accumulated",
     "to": "records"
    },
    {
     "node": "records",
     "action": "per-node accumulation flattened, wrapped in MassSet",
     "passes": "MassSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.masses",
     "passes": "MassSet on fem.nodes.masses",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; linear_density (required); rotational; dofs subset {1,2,3}; reduction lumped|consistent; name",
   "outputs": "LineMassDef; resolves to accumulated MassRecord on fem.nodes.masses",
   "reads": [
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.mass_defs",
    "fem.nodes.masses (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.masses.point",
   "label": "point",
   "composite": "masses",
   "signature": "point(target=None, *, pg=None, label=None, tag=None, mass: float, rotational=None, dofs=None, reduction=\"lumped\", name=None) -> PointMassDef",
   "summary": "Declares a concentrated mass (+ optional rotational inertia) at every node of a target; resolver applies the same length-6 mass vector to each node and accumulates into the MassSet on fem.nodes.masses.",
   "file": "src/apeGmsh/core/MassesComposite.py:236",
   "flow": [
    {
     "node": "masses",
     "action": "_coalesce_target, validate dofs/rotational, _add_def checks reduction in _DISPATCH, append PointMassDef (pre-mesh)",
     "passes": "PointMassDef(target,target_source,name,reduction,dofs,mass,rotational)",
     "to": "mass.defs"
    },
    {
     "node": "mass.defs",
     "action": "held until get_fem_data() -> MassesComposite.resolve",
     "passes": "PointMassDef",
     "to": "mass.resolver"
    },
    {
     "node": "mass.resolver",
     "action": "_resolve_point: _target_nodes resolves target->node set, MassResolver.resolve_point_lumped builds _build_vec6 per node; resolve() accumulates per-node across all defs",
     "passes": "set[int] -> list[MassRecord(node_id,mass=(mx,my,mz,Ixx,Iyy,Izz))] then accumulated",
     "to": "records"
    },
    {
     "node": "records",
     "action": "per-node accumulation flattened to one MassRecord/node, wrapped in MassSet",
     "passes": "MassSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.masses",
     "passes": "MassSet on fem.nodes.masses",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; mass (float, required); rotational (Ixx,Iyy,Izz); dofs translational mask subset {1,2,3}; reduction lumped|consistent; name",
   "outputs": "PointMassDef; resolves to accumulated MassRecord on fem.nodes.masses",
   "reads": [
    "core._resolution.resolve_target",
    "gmsh.model.mesh",
    "self._parent.parts._instances"
   ],
   "writes": [
    "self.mass_defs",
    "fem.nodes.masses (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.masses.resolve",
   "label": "resolve",
   "composite": "mass.resolver",
   "signature": "resolve(node_tags, node_coords, elem_tags=None, connectivity=None, *, node_map=None, face_map=None, ndf=6) -> MassSet",
   "summary": "Stage-2 driver auto-called by get_fem_data(): instantiates MassResolver, walks mass_defs via the (type,reduction) _DISPATCH table, accumulates per-node length-6 mass vectors across overlapping targets, and returns a MassSet of one MassRecord per node.",
   "file": "src/apeGmsh/core/MassesComposite.py:741",
   "flow": [
    {
     "node": "mass.defs",
     "action": "iterate self.mass_defs; look up _DISPATCH[type][reduction]",
     "passes": "list[MassDef]",
     "to": "mass.resolver"
    },
    {
     "node": "mass.resolver",
     "action": "construct MassResolver(node_tags,node_coords,elem_tags,connectivity,ndf); per def gather mesh data (_target_nodes/edges/faces/elements) and call MassResolver.resolve_*; sum vectors into per-node accumulator",
     "passes": "mesh data + MassDef -> raw MassRecord list summed by node_id",
     "to": "records"
    },
    {
     "node": "records",
     "action": "build one MassRecord per node from accumulator, store self.mass_records, wrap in MassSet(records)",
     "passes": "list[MassRecord] -> MassSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.masses",
     "passes": "MassSet on fem.nodes.masses",
     "to": "femdata"
    }
   ],
   "inputs": "node_tags, node_coords, elem_tags, connectivity; node_map, face_map; ndf (default 6)",
   "outputs": "MassSet (also stored on self.mass_records); lands on fem.nodes.masses",
   "reads": [
    "self.mass_defs",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.mass_records",
    "fem.nodes.masses"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.masses.surface",
   "label": "surface",
   "composite": "masses",
   "signature": "surface(target=None, *, pg=None, label=None, tag=None, areal_density: float, rotational=None, derive_rotational=False, dofs=None, reduction=\"lumped\", name=None) -> SurfaceMassDef",
   "summary": "Declares a distributed surface mass (mass per unit area) on face(s); resolver distributes ρ_a·A per face by tributary/HRZ (optionally shape-function-derived rotational inertia) and accumulates into the MassSet on fem.nodes.masses.",
   "file": "src/apeGmsh/core/MassesComposite.py:386",
   "flow": [
    {
     "node": "masses",
     "action": "_coalesce_target, validate dofs/rotational/derive_rotational (consistent-only, mutually exclusive with rotational), _add_def append SurfaceMassDef (pre-mesh)",
     "passes": "SurfaceMassDef(target,target_source,name,reduction,dofs,areal_density,rotational,derive_rotational)",
     "to": "mass.defs"
    },
    {
     "node": "mass.defs",
     "action": "held until get_fem_data() -> MassesComposite.resolve",
     "passes": "SurfaceMassDef",
     "to": "mass.resolver"
    },
    {
     "node": "mass.resolver",
     "action": "_resolve_surface_lumped/_consistent: _target_faces resolves face conn, MassResolver.resolve_surface_lumped (ρ_a·A/n) or resolve_surface_consistent (HRZ / _hrz_distribute_derived); resolve() accumulates",
     "passes": "list[list[int]] faces -> list[MassRecord] then accumulated",
     "to": "records"
    },
    {
     "node": "records",
     "action": "per-node accumulation flattened, wrapped in MassSet",
     "passes": "MassSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.masses",
     "passes": "MassSet on fem.nodes.masses",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; areal_density (required); rotational; derive_rotational (consistent-only); dofs subset {1,2,3}; reduction; name",
   "outputs": "SurfaceMassDef; resolves to accumulated MassRecord on fem.nodes.masses",
   "reads": [
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.mass_defs",
    "fem.nodes.masses (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.masses.validate_pre_mesh",
   "label": "validate_pre_mesh",
   "composite": "masses",
   "signature": "validate_pre_mesh() -> None",
   "summary": "Eagerly resolves every string mass target via _resolve_target so typos fail fast before meshing; raw (dim,tag) lists are skipped.",
   "file": "src/apeGmsh/core/MassesComposite.py:590",
   "flow": [
    {
     "node": "masses",
     "action": "for each MassDef with str target, call _resolve_target -> core._resolution.resolve_target (raises on miss)",
     "passes": "MassDef.target str -> resolved DimTags (validation only)",
     "to": "mass.defs"
    }
   ],
   "inputs": "none (iterates self.mass_defs)",
   "outputs": "None (raises on unresolvable target)",
   "reads": [
    "self.mass_defs",
    "core._resolution.resolve_target"
   ],
   "writes": [],
   "_cluster": "D_clm"
  },
  {
   "id": "g.masses.volume",
   "label": "volume",
   "composite": "masses",
   "signature": "volume(target=None, *, pg=None, label=None, tag=None, density: float, rotational=None, derive_rotational=False, dofs=None, reduction=\"lumped\", name=None) -> VolumeMassDef",
   "summary": "Declares distributed mass from material density over volume(s); resolver distributes ρ·V per element by tributary/HRZ (optionally shape-function-derived parallel-axis rotational inertia) and accumulates into the MassSet on fem.nodes.masses.",
   "file": "src/apeGmsh/core/MassesComposite.py:464",
   "flow": [
    {
     "node": "masses",
     "action": "_coalesce_target, validate dofs/rotational/derive_rotational, _add_def append VolumeMassDef (pre-mesh)",
     "passes": "VolumeMassDef(target,target_source,name,reduction,dofs,density,rotational,derive_rotational)",
     "to": "mass.defs"
    },
    {
     "node": "mass.defs",
     "action": "held until get_fem_data() -> MassesComposite.resolve",
     "passes": "VolumeMassDef",
     "to": "mass.resolver"
    },
    {
     "node": "mass.resolver",
     "action": "_resolve_volume_lumped/_consistent: _target_elements resolves volume conn, MassResolver.resolve_volume_lumped (ρ·V/n) or resolve_volume_consistent (HRZ via element_volume isoparametric); resolve() accumulates",
     "passes": "list[ndarray] conns -> list[MassRecord] then accumulated",
     "to": "records"
    },
    {
     "node": "records",
     "action": "per-node accumulation flattened, wrapped in MassSet",
     "passes": "MassSet",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "lands on fem.nodes.masses",
     "passes": "MassSet on fem.nodes.masses",
     "to": "femdata"
    }
   ],
   "inputs": "target or pg=/label=/tag=; density (required); rotational; derive_rotational (consistent-only); dofs subset {1,2,3}; reduction; name",
   "outputs": "VolumeMassDef; resolves to accumulated MassRecord on fem.nodes.masses",
   "reads": [
    "core._resolution.resolve_target",
    "gmsh.model.mesh"
   ],
   "writes": [
    "self.mass_defs",
    "fem.nodes.masses (deferred)"
   ],
   "_cluster": "D_clm"
  },
  {
   "id": "g.mesh.editing.affine_transform",
   "label": "affine_transform",
   "composite": "mesh.editing",
   "signature": "affine_transform(matrix, dim_tags=None) -> _Editing",
   "summary": "Apply a 4x3 affine transform to mesh nodes of the given or all entities.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:563",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "resolve dim_tags via resolve_to_dimtags (None=all)",
     "passes": "resolve_to_dimtags(dim_tags, default_dim=3, session) -> list[(dim,tag)]",
     "to": "mesh.editing"
    },
    {
     "node": "mesh.editing",
     "action": "apply affine transform to mesh nodes",
     "passes": "gmsh.model.mesh.affineTransform(matrix:list[float×12], dimTags:list[(int,int)])",
     "to": "gmsh"
    }
   ],
   "inputs": "matrix (12 coeffs), dim_tags (None|int|label|PG|dimtag|list)",
   "outputs": "self (_Editing)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh mesh node positions"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.classify_surfaces",
   "label": "classify_surfaces",
   "composite": "mesh.editing",
   "signature": "classify_surfaces(angle, *, boundary=True, for_reparametrization=False, curve_angle=pi, export_discrete=True) -> _Editing",
   "summary": "Partition a discrete STL mesh into surface patches by dihedral angle.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:126",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "classify discrete surfaces by angle",
     "passes": "gmsh.model.mesh.classifySurfaces(angle:float, boundary:bool, forReparametrization:bool, curveAngle:float, exportDiscrete:bool)",
     "to": "gmsh"
    }
   ],
   "inputs": "angle, boundary, for_reparametrization, curve_angle, export_discrete",
   "outputs": "self (_Editing)",
   "reads": [
    "gmsh discrete mesh"
   ],
   "writes": [
    "gmsh classified surface patches"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.clear",
   "label": "clear",
   "composite": "mesh.editing",
   "signature": "clear(dim_tags=None) -> _Editing",
   "summary": "Clear mesh data (nodes + elements) for the given or all entities.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:168",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "resolve dim_tags via resolve_to_dimtags (None=all)",
     "passes": "resolve_to_dimtags(dim_tags, default_dim=3, session) -> list[(dim,tag)]",
     "to": "mesh.editing"
    },
    {
     "node": "mesh.editing",
     "action": "clear mesh",
     "passes": "gmsh.model.mesh.clear(dimTags:list[(int,int)])",
     "to": "gmsh"
    }
   ],
   "inputs": "dim_tags (None|int|label|PG|dimtag|list)",
   "outputs": "self (_Editing)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh mesh (cleared)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.crack",
   "label": "crack",
   "composite": "mesh.editing",
   "signature": "crack(physical_group, *, dim=1, open_boundary=None, normal=None, side_labels=True) -> _Editing",
   "summary": "Duplicate mesh nodes along a physical group via the Gmsh Crack plugin, optionally auto-labeling the two crack sides.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:297",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "resolve crack PG (and optional open-boundary PG one dim lower) to tags via g.physical.get_tag",
     "passes": "physical.get_tag(dim, physical_group); physical.get_tag(dim-1, open_boundary)",
     "to": "physical"
    },
    {
     "node": "mesh.editing",
     "action": "for dim=2 side-labels snapshot pre-entities + source surface analytic normal/center via gmsh.model.getNormal/occ.getCenterOfMass",
     "passes": "ref_origin/ref_normal:np.ndarray",
     "to": "gmsh"
    },
    {
     "node": "mesh.editing",
     "action": "configure and run the Crack plugin",
     "passes": "gmsh.plugin.setNumber('Crack','Dimension'|'PhysicalGroup'|'OpenBoundaryPhysicalGroup'|'Normal*'); gmsh.plugin.run('Crack')",
     "to": "gmsh"
    },
    {
     "node": "mesh.editing",
     "action": "classify the new vs original face side and register two side-label physical groups",
     "passes": "physical.add_surface([normal_tag], name=f'{pg}_normal'); add_surface([inverted_tag], name=f'{pg}_inverted')",
     "to": "physical"
    }
   ],
   "inputs": "physical_group name, dim, open_boundary name, normal hint, side_labels (bool|tuple)",
   "outputs": "self (_Editing)",
   "reads": [
    "g.physical PG registry",
    "gmsh mesh + analytic surface normal"
   ],
   "writes": [
    "gmsh mesh (duplicated crack nodes)",
    "g.physical side-label groups",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.create_geometry",
   "label": "create_geometry",
   "composite": "mesh.editing",
   "signature": "create_geometry(dim_tags=None) -> _Editing",
   "summary": "Create a CAD-like geometry from classified discrete surfaces (after classify_surfaces).",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:152",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "build geometry from classified discrete surfaces",
     "passes": "gmsh.model.mesh.createGeometry(dimTags:list[(int,int)])",
     "to": "gmsh"
    }
   ],
   "inputs": "dim_tags list[(dim,tag)]|None",
   "outputs": "self (_Editing)",
   "reads": [
    "gmsh classified surfaces"
   ],
   "writes": [
    "gmsh CAD-like geometry"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.embed",
   "label": "embed",
   "composite": "mesh.editing",
   "signature": "embed(tags, in_tag, *, dim=0, in_dim=3) -> _Editing",
   "summary": "Embed lower-dim entities inside a higher-dim entity so the mesh conforms along them.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:33",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "resolve tags and in_tag via resolve_to_tags",
     "passes": "resolve_to_tags(tags, dim, session); resolve_to_tags(in_tag, in_dim, session)",
     "to": "mesh.editing"
    },
    {
     "node": "mesh.editing",
     "action": "embed into the host entity",
     "passes": "gmsh.model.mesh.embed(dim:int, tag_list:list[int], in_dim:int, in_tag_resolved:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "tags, in_tag (int|label|PG|list), dim, in_dim",
   "outputs": "self (_Editing)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh embedding constraint"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.import_stl",
   "label": "import_stl",
   "composite": "mesh.editing",
   "signature": "import_stl() -> _Editing",
   "summary": "Classify an STL mesh previously merged into the model as a discrete surface mesh.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:117",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "classify merged STL as discrete surface mesh",
     "passes": "gmsh.model.mesh.importStl()",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "self (_Editing)",
   "reads": [
    "gmsh merged STL data"
   ],
   "writes": [
    "gmsh discrete surface mesh"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.relocate_nodes",
   "label": "relocate_nodes",
   "composite": "mesh.editing",
   "signature": "relocate_nodes(*, dim=-1, tag=-1) -> _Editing",
   "summary": "Project mesh nodes back onto their underlying geometry, iterating per resolved entity.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:220",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "if tag=-1 relocate all; else resolve via resolve_to_dimtags and call gmsh once per (dim,tag)",
     "passes": "resolve_to_dimtags(tag, default_dim, session) -> list[(d,t)]; gmsh.model.mesh.relocateNodes(dim=d, tag=t)",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag (-1|int|label|PG|dimtag|list)",
   "outputs": "self (_Editing)",
   "reads": [
    "session label/PG registry",
    "gmsh geometry"
   ],
   "writes": [
    "gmsh mesh node positions"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.remove_duplicate_elements",
   "label": "remove_duplicate_elements",
   "composite": "mesh.editing",
   "signature": "remove_duplicate_elements(verbose=True) -> _Editing",
   "summary": "Remove elements with identical node connectivity and report the count.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:277",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "count elements before, remove duplicates, count after, print delta",
     "passes": "gmsh.model.mesh.getElements() before/after; gmsh.model.mesh.removeDuplicateElements()",
     "to": "gmsh"
    }
   ],
   "inputs": "verbose bool",
   "outputs": "self (_Editing)",
   "reads": [
    "gmsh mesh elements"
   ],
   "writes": [
    "gmsh mesh (removed duplicate elements)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.remove_duplicate_nodes",
   "label": "remove_duplicate_nodes",
   "composite": "mesh.editing",
   "signature": "remove_duplicate_nodes(verbose=True) -> _Editing",
   "summary": "Merge mesh nodes sharing a position within tolerance and report the count.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:255",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "count nodes before, merge duplicates, count after, print delta",
     "passes": "gmsh.model.mesh.getNodes() before/after; gmsh.model.mesh.removeDuplicateNodes()",
     "to": "gmsh"
    }
   ],
   "inputs": "verbose bool",
   "outputs": "self (_Editing)",
   "reads": [
    "gmsh mesh nodes"
   ],
   "writes": [
    "gmsh mesh (merged duplicate nodes)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.reverse",
   "label": "reverse",
   "composite": "mesh.editing",
   "signature": "reverse(dim_tags=None) -> _Editing",
   "summary": "Reverse the orientation of mesh elements in the given or all entities.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:195",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "resolve dim_tags via resolve_to_dimtags (None=all)",
     "passes": "resolve_to_dimtags(dim_tags, default_dim=3, session) -> list[(dim,tag)]",
     "to": "mesh.editing"
    },
    {
     "node": "mesh.editing",
     "action": "reverse element orientation",
     "passes": "gmsh.model.mesh.reverse(dimTags:list[(int,int)])",
     "to": "gmsh"
    }
   ],
   "inputs": "dim_tags (None|int|label|PG|dimtag|list)",
   "outputs": "self (_Editing)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh mesh (reversed orientation)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.editing.set_periodic",
   "label": "set_periodic",
   "composite": "mesh.editing",
   "signature": "set_periodic(tags, master_tags, transform, *, dim=2) -> _Editing",
   "summary": "Declare periodic mesh correspondence between slave and master entities under a 4x4 transform.",
   "file": "src/apeGmsh/mesh/_mesh_editing.py:69",
   "flow": [
    {
     "node": "mesh.editing",
     "action": "resolve slave/master refs via resolve_to_tags and check count parity",
     "passes": "resolve_to_tags(tags|master_tags, dim, session) -> list[int]",
     "to": "mesh.editing"
    },
    {
     "node": "mesh.editing",
     "action": "set periodic correspondence",
     "passes": "gmsh.model.mesh.setPeriodic(dim:int, slave:list[int], master:list[int], transform:list[float×16])",
     "to": "gmsh"
    }
   ],
   "inputs": "tags, master_tags, transform (16-elem), dim",
   "outputs": "self (_Editing)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh periodic mesh constraint"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.add",
   "label": "add",
   "composite": "mesh.field",
   "signature": "add(field_type) -> int",
   "summary": "Create a new Gmsh mesh-size field of the given type and return its tag.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:114",
   "flow": [
    {
     "node": "mesh.field",
     "action": "create the field",
     "passes": "gmsh.model.mesh.field.add(field_type:str) -> int",
     "to": "gmsh"
    },
    {
     "node": "mesh.field",
     "action": "record field_add directive",
     "passes": "{kind:'field_add', field_type, field_tag}:dict",
     "to": "mesh.field"
    }
   ],
   "inputs": "field_type str",
   "outputs": "int field tag",
   "reads": [],
   "writes": [
    "gmsh mesh field",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.boundary_layer",
   "label": "boundary_layer",
   "composite": "mesh.field",
   "signature": "boundary_layer(*, curves=None, points=None, size_near, ratio=1.2, n_layers=5, thickness=None, fan_points=None) -> int",
   "summary": "Create a BoundaryLayer field for wall-resolved meshes from resolved curves/points/fan-points.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:262",
   "flow": [
    {
     "node": "mesh.field",
     "action": "resolve curves(dim1)/points(dim0)/fan_points(dim0) via _resolve_at_dim",
     "passes": "_resolve_at_dim(...) -> list[int]",
     "to": "mesh.field"
    },
    {
     "node": "mesh.field",
     "action": "add BoundaryLayer field + set CurvesList/PointsList/FanPointsList/Size/Ratio/NbLayers(+Thickness)",
     "passes": "gmsh.model.mesh.field.add('BoundaryLayer'); setNumbers/setNumber(...)",
     "to": "gmsh"
    }
   ],
   "inputs": "curves/points/fan_points refs, size_near, ratio, n_layers, thickness",
   "outputs": "int field tag",
   "reads": [
    "session label/PG registry via resolve_to_dimtags"
   ],
   "writes": [
    "gmsh BoundaryLayer field"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.box",
   "label": "box",
   "composite": "mesh.field",
   "signature": "box(*, x_min, y_min, z_min, x_max, y_max, z_max, size_in, size_out, thickness=0.0) -> int",
   "summary": "Create a Box field: size_in inside the box, size_out outside, with optional transition thickness.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:223",
   "flow": [
    {
     "node": "mesh.field",
     "action": "add Box field and set VIn/VOut/X..Z Min/Max (+ optional Thickness)",
     "passes": "gmsh.model.mesh.field.add('Box'); setNumber(tag,'VIn'|'VOut'|bounds:float)",
     "to": "gmsh"
    }
   ],
   "inputs": "x/y/z min/max, size_in, size_out, thickness",
   "outputs": "int field tag",
   "reads": [],
   "writes": [
    "gmsh Box field"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.distance",
   "label": "distance",
   "composite": "mesh.field",
   "signature": "distance(*, curves=None, surfaces=None, points=None, sampling=100) -> int",
   "summary": "Create a Distance field measuring shortest distance to resolved curves/surfaces/points.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:157",
   "flow": [
    {
     "node": "mesh.field",
     "action": "resolve each ref to tags at its expected dim (int trusted; str/dimtag validated via resolve_to_dimtags)",
     "passes": "_resolve_at_dim(curves,1)/surfaces,2/points,0 -> list[int]",
     "to": "mesh.field"
    },
    {
     "node": "mesh.field",
     "action": "add Distance field + set CurvesList/SurfacesList/PointsList + Sampling",
     "passes": "gmsh.model.mesh.field.add('Distance'); setNumbers(tag,'CurvesList',...); setNumber(tag,'Sampling',sampling:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "curves/surfaces/points refs, sampling int",
   "outputs": "int field tag",
   "reads": [
    "session label/PG registry via resolve_to_dimtags"
   ],
   "writes": [
    "gmsh Distance field"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.math_eval",
   "label": "math_eval",
   "composite": "mesh.field",
   "signature": "math_eval(expression) -> int",
   "summary": "Create a MathEval field driven by an expression in x, y, z.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:216",
   "flow": [
    {
     "node": "mesh.field",
     "action": "add MathEval field and set its F expression",
     "passes": "gmsh.model.mesh.field.add('MathEval'); setString(tag,'F',expression:str)",
     "to": "gmsh"
    }
   ],
   "inputs": "expression str",
   "outputs": "int field tag",
   "reads": [],
   "writes": [
    "gmsh MathEval field"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.minimum",
   "label": "minimum",
   "composite": "mesh.field",
   "signature": "minimum(field_tags) -> int",
   "summary": "Create a Min field combining several fields by element-wise minimum.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:255",
   "flow": [
    {
     "node": "mesh.field",
     "action": "add Min field and set FieldsList",
     "passes": "gmsh.model.mesh.field.add('Min'); setNumbers(tag,'FieldsList',field_tags:list[int])",
     "to": "gmsh"
    }
   ],
   "inputs": "field_tags list[int]",
   "outputs": "int field tag",
   "reads": [],
   "writes": [
    "gmsh Min field"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.set_background",
   "label": "set_background",
   "composite": "mesh.field",
   "signature": "set_background(tag) -> FieldHelper",
   "summary": "Register a field as the global background mesh-size field.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:138",
   "flow": [
    {
     "node": "mesh.field",
     "action": "register field as background mesh",
     "passes": "gmsh.model.mesh.field.setAsBackgroundMesh(tag:int)",
     "to": "gmsh"
    },
    {
     "node": "mesh.field",
     "action": "record field_background directive",
     "passes": "{kind:'field_background', field_tag}:dict",
     "to": "mesh.field"
    }
   ],
   "inputs": "tag int",
   "outputs": "self (FieldHelper)",
   "reads": [],
   "writes": [
    "gmsh background mesh field",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.set_boundary_layer_field",
   "label": "set_boundary_layer_field",
   "composite": "mesh.field",
   "signature": "set_boundary_layer_field(tag) -> FieldHelper",
   "summary": "Register a BoundaryLayer field to be applied during meshing.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:147",
   "flow": [
    {
     "node": "mesh.field",
     "action": "register field as boundary layer",
     "passes": "gmsh.model.mesh.field.setAsBoundaryLayer(tag:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "tag int",
   "outputs": "self (FieldHelper)",
   "reads": [],
   "writes": [
    "gmsh boundary-layer field"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.set_number",
   "label": "set_number",
   "composite": "mesh.field",
   "signature": "set_number(tag, name, value) -> FieldHelper",
   "summary": "Set a scalar parameter on a Gmsh mesh field.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:123",
   "flow": [
    {
     "node": "mesh.field",
     "action": "set scalar field parameter",
     "passes": "gmsh.model.mesh.field.setNumber(tag:int, name:str, value:float)",
     "to": "gmsh"
    }
   ],
   "inputs": "tag int, name str, value float",
   "outputs": "self (FieldHelper)",
   "reads": [],
   "writes": [
    "gmsh field scalar parameter"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.set_numbers",
   "label": "set_numbers",
   "composite": "mesh.field",
   "signature": "set_numbers(tag, name, values) -> FieldHelper",
   "summary": "Set a list parameter on a Gmsh mesh field.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:128",
   "flow": [
    {
     "node": "mesh.field",
     "action": "set list field parameter",
     "passes": "gmsh.model.mesh.field.setNumbers(tag:int, name:str, values:list[float])",
     "to": "gmsh"
    }
   ],
   "inputs": "tag int, name str, values list[float]",
   "outputs": "self (FieldHelper)",
   "reads": [],
   "writes": [
    "gmsh field list parameter"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.set_string",
   "label": "set_string",
   "composite": "mesh.field",
   "signature": "set_string(tag, name, value) -> FieldHelper",
   "summary": "Set a string parameter on a Gmsh mesh field.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:133",
   "flow": [
    {
     "node": "mesh.field",
     "action": "set string field parameter",
     "passes": "gmsh.model.mesh.field.setString(tag:int, name:str, value:str)",
     "to": "gmsh"
    }
   ],
   "inputs": "tag int, name str, value str",
   "outputs": "self (FieldHelper)",
   "reads": [],
   "writes": [
    "gmsh field string parameter"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.field.threshold",
   "label": "threshold",
   "composite": "mesh.field",
   "signature": "threshold(distance_field, *, size_min, size_max, dist_min, dist_max, sigmoid=False, stop_at_dist_max=False) -> int",
   "summary": "Create a Threshold field ramping element size between size_min and size_max over a distance band.",
   "file": "src/apeGmsh/mesh/_mesh_field.py:189",
   "flow": [
    {
     "node": "mesh.field",
     "action": "add Threshold field and set InField/SizeMin/SizeMax/DistMin/DistMax/Sigmoid/StopAtDistMax",
     "passes": "gmsh.model.mesh.field.add('Threshold'); setNumber(tag,'InField',distance_field:int); setNumber(...,size/dist:float)",
     "to": "gmsh"
    }
   ],
   "inputs": "distance_field int, size_min/max, dist_min/max, sigmoid, stop_at_dist_max",
   "outputs": "int field tag",
   "reads": [],
   "writes": [
    "gmsh Threshold field"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.generation.generate",
   "label": "generate",
   "composite": "mesh.generation",
   "signature": "generate(dim=3) -> _Generation",
   "summary": "Validate pending loads/constraints/masses then run the Gmsh mesher up to the given dimension.",
   "file": "src/apeGmsh/mesh/_mesh_generation.py:31",
   "flow": [
    {
     "node": "mesh.generation",
     "action": "call validate_pre_mesh() on session.loads/constraints/masses to catch typo'd targets pre-mesh",
     "passes": "session attrs loads/constraints/masses",
     "to": "loads"
    },
    {
     "node": "mesh.generation",
     "action": "drive the mesher",
     "passes": "gmsh.model.mesh.generate(dim:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "dim (1=edges, 2=surface, 3=volume)",
   "outputs": "self (_Generation) for chaining",
   "reads": [
    "session.loads/constraints/masses validators",
    "gmsh geometry + applied sizing/field directives"
   ],
   "writes": [
    "gmsh in-memory mesh (nodes + elements)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.generation.optimize",
   "label": "optimize",
   "composite": "mesh.generation",
   "signature": "optimize(method='', *, force=False, niter=1, dim_tags=None) -> _Generation",
   "summary": "Run a Gmsh mesh-quality optimisation pass (Netgen, HighOrder, etc.).",
   "file": "src/apeGmsh/mesh/_mesh_generation.py:79",
   "flow": [
    {
     "node": "mesh.generation",
     "action": "invoke mesh optimiser",
     "passes": "gmsh.model.mesh.optimize(method:str, force:bool, niter:int, dimTags:list[(int,int)])",
     "to": "gmsh"
    }
   ],
   "inputs": "method (OptimizeMethod.*), force, niter, dim_tags",
   "outputs": "self (_Generation)",
   "reads": [
    "gmsh mesh"
   ],
   "writes": [
    "gmsh mesh (optimised node positions)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.generation.refine",
   "label": "refine",
   "composite": "mesh.generation",
   "signature": "refine() -> _Generation",
   "summary": "Uniformly refine the mesh by splitting every element once.",
   "file": "src/apeGmsh/mesh/_mesh_generation.py:73",
   "flow": [
    {
     "node": "mesh.generation",
     "action": "split every element once",
     "passes": "gmsh.model.mesh.refine()",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "self (_Generation)",
   "reads": [
    "gmsh mesh"
   ],
   "writes": [
    "gmsh mesh (refined)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.generation.set_algorithm",
   "label": "set_algorithm",
   "composite": "mesh.generation",
   "signature": "set_algorithm(tag, algorithm, *, dim=2) -> _Generation",
   "summary": "Set the 2-D per-surface or global 3-D meshing algorithm, resolving tag via label/PG.",
   "file": "src/apeGmsh/mesh/_mesh_generation.py:112",
   "flow": [
    {
     "node": "mesh.generation",
     "action": "normalize algorithm name/int via _normalize_algorithm (ALGORITHM_2D/3D tables)",
     "passes": "algorithm:str|int|IntEnum, dim:int",
     "to": "mesh.generation"
    },
    {
     "node": "mesh.generation",
     "action": "for dim=2 resolve tag to surface tags via resolve_to_tags then setAlgorithm per surface; for dim=3 set global option",
     "passes": "resolve_to_tags(tag, dim=2, session); gmsh.model.mesh.setAlgorithm(2,t,alg_int) | gmsh.option.setNumber('Mesh.Algorithm3D', alg_int)",
     "to": "gmsh"
    },
    {
     "node": "mesh.generation",
     "action": "record directive for Inspect.print_summary",
     "passes": "{kind:'algorithm', dim, tag, algorithm, requested}:dict",
     "to": "mesh.generation"
    }
   ],
   "inputs": "tag (int|label|PG name), algorithm, dim (2 or 3)",
   "outputs": "self (_Generation)",
   "reads": [
    "session label/PG registry via resolve_to_tags"
   ],
   "writes": [
    "gmsh surface algorithm or Mesh.Algorithm3D option",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.generation.set_algorithm_by_physical",
   "label": "set_algorithm_by_physical",
   "composite": "mesh.generation",
   "signature": "set_algorithm_by_physical(name, algorithm, *, dim=2) -> _Generation",
   "summary": "Deprecated alias forwarding a PG name to set_algorithm (tag=0 for dim=3).",
   "file": "src/apeGmsh/mesh/_mesh_generation.py:162",
   "flow": [
    {
     "node": "mesh.generation",
     "action": "emit DeprecationWarning then delegate to set_algorithm with the PG name as tag",
     "passes": "name:str, algorithm, dim:int",
     "to": "mesh.generation"
    }
   ],
   "inputs": "name (PG name), algorithm, dim",
   "outputs": "self (_Generation)",
   "reads": [
    "session label/PG registry (via set_algorithm)"
   ],
   "writes": [
    "gmsh algorithm option (via set_algorithm)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.generation.set_order",
   "label": "set_order",
   "composite": "mesh.generation",
   "signature": "set_order(order, *, bubble=True) -> _Generation",
   "summary": "Elevate mesh elements to high order, optionally with interior bubble nodes.",
   "file": "src/apeGmsh/mesh/_mesh_generation.py:55",
   "flow": [
    {
     "node": "mesh.generation",
     "action": "set Mesh.SecondOrderIncomplete option for order>=2 then elevate",
     "passes": "gmsh.option.setNumber('Mesh.SecondOrderIncomplete', 0|1); gmsh.model.mesh.setOrder(order:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "order int, bubble bool",
   "outputs": "self (_Generation)",
   "reads": [
    "gmsh mesh"
   ],
   "writes": [
    "gmsh mesh element order",
    "Mesh.SecondOrderIncomplete option"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.get_algorithm_2d",
   "label": "get_algorithm_2d",
   "composite": "mesh.queries",
   "signature": "get_algorithm_2d() -> str | int",
   "summary": "Return the current Mesh.Algorithm as a string enum or raw int.",
   "file": "src/apeGmsh/mesh/_mesh_options.py:271",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "read Mesh.Algorithm and map to enum string/int",
     "passes": "gmsh.option.getNumber('Mesh.Algorithm') -> str|int",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "str or int",
   "reads": [
    "Mesh.Algorithm option"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.get_algorithm_3d",
   "label": "get_algorithm_3d",
   "composite": "mesh.queries",
   "signature": "get_algorithm_3d() -> str | int",
   "summary": "Return the current Mesh.Algorithm3D as a string enum or raw int.",
   "file": "src/apeGmsh/mesh/_mesh_options.py:305",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "read Mesh.Algorithm3D and map to enum string/int",
     "passes": "gmsh.option.getNumber('Mesh.Algorithm3D') -> str|int",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "str or int",
   "reads": [
    "Mesh.Algorithm3D option"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.get_element_order",
   "label": "get_element_order",
   "composite": "mesh.queries",
   "signature": "get_element_order() -> int",
   "summary": "Return the current Mesh.ElementOrder value.",
   "file": "src/apeGmsh/mesh/_mesh_options.py:238",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "read the element-order option",
     "passes": "gmsh.option.getNumber('Mesh.ElementOrder') -> int",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "int",
   "reads": [
    "Mesh.ElementOrder option"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.get_recombination_algorithm",
   "label": "get_recombination_algorithm",
   "composite": "mesh.queries",
   "signature": "get_recombination_algorithm() -> str | int",
   "summary": "Return the current Mesh.RecombinationAlgorithm as a string enum or raw int.",
   "file": "src/apeGmsh/mesh/_mesh_options.py:338",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "read Mesh.RecombinationAlgorithm and map to enum string/int",
     "passes": "gmsh.option.getNumber('Mesh.RecombinationAlgorithm') -> str|int",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "str or int",
   "reads": [
    "Mesh.RecombinationAlgorithm option"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.get_smoothing",
   "label": "get_smoothing",
   "composite": "mesh.queries",
   "signature": "get_smoothing() -> int",
   "summary": "Return the current Mesh.Smoothing value.",
   "file": "src/apeGmsh/mesh/_mesh_options.py:207",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "read the global smoothing option",
     "passes": "gmsh.option.getNumber('Mesh.Smoothing') -> int",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "int",
   "reads": [
    "Mesh.Smoothing option"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.get_subdivision_algorithm",
   "label": "get_subdivision_algorithm",
   "composite": "mesh.queries",
   "signature": "get_subdivision_algorithm() -> str | int",
   "summary": "Return the current Mesh.SubdivisionAlgorithm as a string enum or raw int.",
   "file": "src/apeGmsh/mesh/_mesh_options.py:173",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "read option and map to enum string/int",
     "passes": "gmsh.option.getNumber('Mesh.SubdivisionAlgorithm') -> str|int",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "str or int",
   "reads": [
    "Mesh.SubdivisionAlgorithm option"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.set_algorithm_2d",
   "label": "set_algorithm_2d",
   "composite": "mesh.queries",
   "signature": "set_algorithm_2d(algorithm) -> _Options",
   "summary": "Set the global 2-D meshing algorithm (Mesh.Algorithm).",
   "file": "src/apeGmsh/mesh/_mesh_options.py:246",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "resolve enum then set Mesh.Algorithm",
     "passes": "_resolve_enum(algorithm,_ALGORITHM_2D) -> gmsh.option.setNumber('Mesh.Algorithm', code:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "algorithm str|int",
   "outputs": "self (_Options)",
   "reads": [],
   "writes": [
    "Mesh.Algorithm option",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.set_algorithm_3d",
   "label": "set_algorithm_3d",
   "composite": "mesh.queries",
   "signature": "set_algorithm_3d(algorithm) -> _Options",
   "summary": "Set the global 3-D meshing algorithm (Mesh.Algorithm3D).",
   "file": "src/apeGmsh/mesh/_mesh_options.py:282",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "resolve enum then set Mesh.Algorithm3D",
     "passes": "_resolve_enum(algorithm,_ALGORITHM_3D) -> gmsh.option.setNumber('Mesh.Algorithm3D', code:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "algorithm str|int",
   "outputs": "self (_Options)",
   "reads": [],
   "writes": [
    "Mesh.Algorithm3D option",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.set_element_order",
   "label": "set_element_order",
   "composite": "mesh.queries",
   "signature": "set_element_order(order) -> _Options",
   "summary": "Set Mesh.ElementOrder (element interpolation order).",
   "file": "src/apeGmsh/mesh/_mesh_options.py:215",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "set the element-order option",
     "passes": "gmsh.option.setNumber('Mesh.ElementOrder', int(order))",
     "to": "gmsh"
    }
   ],
   "inputs": "order int",
   "outputs": "self (_Options)",
   "reads": [],
   "writes": [
    "Mesh.ElementOrder option",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.set_recombination_algorithm",
   "label": "set_recombination_algorithm",
   "composite": "mesh.queries",
   "signature": "set_recombination_algorithm(algorithm) -> _Options",
   "summary": "Set the triangle-to-quad recombination strategy (Mesh.RecombinationAlgorithm).",
   "file": "src/apeGmsh/mesh/_mesh_options.py:316",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "resolve enum then set Mesh.RecombinationAlgorithm",
     "passes": "_resolve_enum(algorithm,_RECOMBINATION_ALGORITHM) -> gmsh.option.setNumber('Mesh.RecombinationAlgorithm', code:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "algorithm str|int",
   "outputs": "self (_Options)",
   "reads": [],
   "writes": [
    "Mesh.RecombinationAlgorithm option",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.set_smoothing",
   "label": "set_smoothing",
   "composite": "mesh.queries",
   "signature": "set_smoothing(iterations) -> _Options",
   "summary": "Set Mesh.Smoothing (global Laplacian passes after generation) — distinct from structured.set_smoothing.",
   "file": "src/apeGmsh/mesh/_mesh_options.py:184",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "set the global smoothing option",
     "passes": "gmsh.option.setNumber('Mesh.Smoothing', int(iterations))",
     "to": "gmsh"
    }
   ],
   "inputs": "iterations int",
   "outputs": "self (_Options)",
   "reads": [],
   "writes": [
    "Mesh.Smoothing option",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.options.set_subdivision_algorithm",
   "label": "set_subdivision_algorithm",
   "composite": "mesh.queries",
   "signature": "set_subdivision_algorithm(algorithm) -> _Options",
   "summary": "Set Mesh.SubdivisionAlgorithm to post-process tets/prisms into hexes (g.mesh.options).",
   "file": "src/apeGmsh/mesh/_mesh_options.py:141",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "resolve string/int enum then set option",
     "passes": "_resolve_enum(algorithm,_SUBDIVISION_ALGORITHM) -> gmsh.option.setNumber('Mesh.SubdivisionAlgorithm', code:int)",
     "to": "gmsh"
    }
   ],
   "inputs": "algorithm str|int ('none'|'all_quad'|'all_hex')",
   "outputs": "self (_Options)",
   "reads": [],
   "writes": [
    "Mesh.SubdivisionAlgorithm option",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.entity_table",
   "label": "entity_table",
   "composite": "mesh.partitioning",
   "signature": "entity_table(dim=-1) -> pd.DataFrame",
   "summary": "DataFrame of all model entities with their partition membership and parent.",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:351",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "walk gmsh entities collecting partitions + parent into rows",
     "passes": "gmsh.model.getEntities / getPartitions / getParent -> pd.DataFrame indexed by (dim,tag)",
     "to": "gmsh"
    }
   ],
   "inputs": "dim (-1=all)",
   "outputs": "pd.DataFrame (dim,tag,partitions,parent_dim,parent_tag)",
   "reads": [
    "gmsh entities + partition membership"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.n_partitions",
   "label": "n_partitions",
   "composite": "mesh.partitioning",
   "signature": "n_partitions() -> int",
   "summary": "Return the current number of partitions (0 if not partitioned).",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:322",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "query partition count",
     "passes": "gmsh.model.getNumberOfPartitions() -> int",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "int partition count",
   "reads": [
    "gmsh partition state"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.partition",
   "label": "partition",
   "composite": "mesh.partitioning",
   "signature": "partition(n_parts) -> PartitionInfo",
   "summary": "Partition the mesh into n_parts sub-domains via Gmsh/METIS and gather partition info.",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:238",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "invoke METIS partitioner",
     "passes": "gmsh.model.mesh.partition(n_parts:int)",
     "to": "gmsh"
    },
    {
     "node": "mesh.partitioning",
     "action": "walk entities to count elements per partition (_gather_partition_info)",
     "passes": "gmsh.model.getPartitions / getElements -> PartitionInfo(n_parts, elements_per_partition)",
     "to": "mesh.partitioning"
    }
   ],
   "inputs": "n_parts int (>=1)",
   "outputs": "PartitionInfo",
   "reads": [
    "gmsh mesh + partition membership"
   ],
   "writes": [
    "gmsh mesh partition structure"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.partition_explicit",
   "label": "partition_explicit",
   "composite": "mesh.partitioning",
   "signature": "partition_explicit(n_parts, elem_tags, parts) -> PartitionInfo",
   "summary": "Partition with an explicit per-element partition assignment.",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:259",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "invoke partitioner with explicit element->partition map",
     "passes": "gmsh.model.mesh.partition(n_parts:int, elementTags:list[int], partitions:list[int])",
     "to": "gmsh"
    },
    {
     "node": "mesh.partitioning",
     "action": "gather partition info",
     "passes": "_gather_partition_info() -> PartitionInfo",
     "to": "mesh.partitioning"
    }
   ],
   "inputs": "n_parts, elem_tags list[int], parts list[int] (parallel)",
   "outputs": "PartitionInfo",
   "reads": [
    "gmsh mesh + partition membership"
   ],
   "writes": [
    "gmsh mesh partition structure"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.renumber",
   "label": "renumber",
   "composite": "mesh.partitioning",
   "signature": "renumber(dim=2, *, method='rcm', base=1) -> RenumberResult",
   "summary": "Renumber Gmsh nodes/elements to contiguous or bandwidth-optimised IDs and report before/after bandwidth.",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:134",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "extract raw mesh + build element groups + compute bandwidth BEFORE",
     "passes": "_fem_extract.extract_raw(dim) ; _fem_factory._build_element_groups(groups) ; FEMData._compute_bandwidth(groups)",
     "to": "fem.extract"
    },
    {
     "node": "mesh.partitioning",
     "action": "node renumber: 'simple' = sorted contiguous via _renumber_nodes_simple; 'rcm'/'hilbert'/'metis' = gmsh.model.mesh.computeRenumbering(_METHOD_MAP) then renumberNodes (RCM via Gmsh RCMK, not _numberer)",
     "passes": "gmsh.model.mesh.computeRenumbering(method='RCMK'|'Hilbert'|'Metis') -> (old,new); gmsh.model.mesh.renumberNodes(oldTags,newTags)",
     "to": "gmsh"
    },
    {
     "node": "mesh.partitioning",
     "action": "element renumber always simple contiguous via _renumber_elements_simple",
     "passes": "gmsh.model.mesh.renumberElements(oldTags:list[int], newTags:list[int])",
     "to": "gmsh"
    },
    {
     "node": "mesh.partitioning",
     "action": "re-extract + recompute bandwidth AFTER and build result",
     "passes": "RenumberResult(method, n_nodes, n_elements, bw_before, bw_after)",
     "to": "mesh.partitioning"
    }
   ],
   "inputs": "dim, method ('simple'|'rcm'|'hilbert'|'metis'), base",
   "outputs": "RenumberResult",
   "reads": [
    "gmsh mesh via _fem_extract.extract_raw"
   ],
   "writes": [
    "gmsh mesh node + element tags (renumbered in place)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.save",
   "label": "save",
   "composite": "mesh.partitioning",
   "signature": "save(path, *, one_file_per_partition=False, create_topology=False, create_physicals=True) -> _Partitioning",
   "summary": "Write the partitioned mesh to file(s) with partition topology/physicals options.",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:399",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "set PartitionCreateTopology/CreatePhysicals/SplitMeshFiles options then write",
     "passes": "gmsh.option.setNumber('Mesh.Partition*', int); gmsh.write(str(path))",
     "to": "gmsh"
    }
   ],
   "inputs": "path, one_file_per_partition, create_topology, create_physicals",
   "outputs": "self (_Partitioning)",
   "reads": [
    "gmsh partitioned mesh"
   ],
   "writes": [
    "mesh file(s) on disk",
    "Mesh.Partition* options"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.summary",
   "label": "summary",
   "composite": "mesh.partitioning",
   "signature": "summary() -> str",
   "summary": "Concise text summary of the partition state grouped by entity dimension.",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:326",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "read partition count + entity_table() and format counts per dim",
     "passes": "n_partitions(); entity_table() -> str",
     "to": "mesh.partitioning"
    }
   ],
   "inputs": "none",
   "outputs": "str summary",
   "reads": [
    "gmsh partition state + entities"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.partitioning.unpartition",
   "label": "unpartition",
   "composite": "mesh.partitioning",
   "signature": "unpartition() -> _Partitioning",
   "summary": "Remove the partition structure and restore a monolithic mesh.",
   "file": "src/apeGmsh/mesh/_mesh_partitioning.py:292",
   "flow": [
    {
     "node": "mesh.partitioning",
     "action": "drop partition structure",
     "passes": "gmsh.model.mesh.unpartition()",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "self (_Partitioning)",
   "reads": [
    "gmsh mesh"
   ],
   "writes": [
    "gmsh mesh (partition removed)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.preview",
   "label": "preview",
   "composite": "mesh.queries",
   "signature": "preview(*, dims=None, show_nodes=True, browser=False, return_fig=False)",
   "summary": "Render a zero-Qt WebGL plotly preview of the mesh inline or in a browser tab.",
   "file": "src/apeGmsh/mesh/Mesh.py:210",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "delegate to viz.NotebookPreview.preview_mesh",
     "passes": "session, dims:list[int]|None, show_nodes:bool, browser:bool, return_fig:bool",
     "to": "external",
     "to_raw": "viz.NotebookPreview.preview_mesh (plotly preview; out-of-cluster)"
    }
   ],
   "inputs": "dims, show_nodes, browser, return_fig",
   "outputs": "plotly Figure or None (display side effect)",
   "reads": [
    "live gmsh mesh via preview_mesh"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.queries.get_element_properties",
   "label": "get_element_properties",
   "composite": "mesh.queries",
   "signature": "get_element_properties(element_type) -> dict",
   "summary": "Return metadata (name, dim, order, node counts, local coords) for a Gmsh element type code.",
   "file": "src/apeGmsh/mesh/_mesh_queries.py:96",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "query element-type metadata from gmsh",
     "passes": "gmsh.model.mesh.getElementProperties(element_type:int) -> {'name','dim','order','n_nodes','n_primary_nodes','local_coords'}",
     "to": "gmsh"
    }
   ],
   "inputs": "element_type int",
   "outputs": "dict of element-type metadata",
   "reads": [
    "gmsh element-type tables"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.queries.get_element_qualities",
   "label": "get_element_qualities",
   "composite": "mesh.queries",
   "signature": "get_element_qualities(element_tags, quality_name='minSICN') -> ndarray",
   "summary": "Compute a quality metric (minSICN/minSIGE/gamma/minSJ) for the given elements.",
   "file": "src/apeGmsh/mesh/_mesh_queries.py:166",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "compute element qualities via gmsh",
     "passes": "gmsh.model.mesh.getElementQualities(tags:list[int], qualityName:str) -> ndarray",
     "to": "gmsh"
    }
   ],
   "inputs": "element_tags list/ndarray, quality_name str",
   "outputs": "ndarray of quality values",
   "reads": [
    "gmsh mesh elements"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.queries.get_elements",
   "label": "get_elements",
   "composite": "mesh.queries",
   "signature": "get_elements(*, dim=-1, tag=-1) -> dict",
   "summary": "Query mesh element type codes, per-type tags, and per-type connectivity.",
   "file": "src/apeGmsh/mesh/_mesh_queries.py:65",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "fetch elements from gmsh and pack per-type ndarrays",
     "passes": "gmsh.model.mesh.getElements(dim, tag) -> {'types':list[int], 'tags':list[ndarray], 'node_tags':list[ndarray]}",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "dict with 'types', 'tags', 'node_tags'",
   "reads": [
    "gmsh mesh elements"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.queries.get_fem_data",
   "label": "get_fem_data",
   "composite": "mesh.queries",
   "signature": "get_fem_data(dim=None, *, remove_orphans=False) -> FEMData",
   "summary": "Extract solver-ready FEMData from the live mesh by delegating to the FEMData/_fem_extract engine.",
   "file": "src/apeGmsh/mesh/_mesh_queries.py:122",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "delegate extraction to FEMData.from_gmsh with the session",
     "passes": "FEMData.from_gmsh(dim:int|None, session, remove_orphans:bool)",
     "to": "fem.extract"
    },
    {
     "node": "fem.extract",
     "action": "pull raw nodes/elements/groups from live gmsh (extract_raw) and build typed element groups (_fem_factory._build_element_groups), snapshot physical + mesh_selection stores onto the broker",
     "passes": "raw{node_tags,node_coords,groups,elem_tags} + PhysicalGroupSet/MeshSelectionStore snapshots",
     "to": "femdata"
    },
    {
     "node": "femdata",
     "action": "return composite broker with .nodes/.elements/.info/.physical/.mesh_selection",
     "passes": "FEMData(info.n_nodes, info.n_elems, info.bandwidth, ...)",
     "to": "mesh.queries"
    }
   ],
   "inputs": "dim (None=all dims), remove_orphans",
   "outputs": "FEMData object",
   "reads": [
    "gmsh mesh (nodes/elements/groups) via _fem_extract",
    "g.physical + g.mesh_selection snapshots",
    "session constraints/loads/masses node refs (orphan retention)"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.queries.get_nodes",
   "label": "get_nodes",
   "composite": "mesh.queries",
   "signature": "get_nodes(*, dim=-1, tag=-1, include_boundary=False, return_parametric=False) -> dict",
   "summary": "Query mesh node tags and XYZ coordinates (optionally parametric) from the live Gmsh mesh.",
   "file": "src/apeGmsh/mesh/_mesh_queries.py:33",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "fetch nodes from gmsh and pack into ndarrays",
     "passes": "gmsh.model.mesh.getNodes(dim, tag, includeBoundary, returnParametricCoord) -> {'tags':ndarray(N), 'coords':ndarray(N,3)}",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag, include_boundary, return_parametric",
   "outputs": "dict with 'tags', 'coords' (+ 'parametric_coords')",
   "reads": [
    "gmsh mesh nodes"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.queries.quality_report",
   "label": "quality_report",
   "composite": "mesh.queries",
   "signature": "quality_report(*, dim=-1, metrics=None) -> pd.DataFrame",
   "summary": "Compute a per-element-type, per-metric quality summary DataFrame (count/min/max/mean/std/pct-below).",
   "file": "src/apeGmsh/mesh/_mesh_queries.py:184",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "get elements then loop types×metrics computing gmsh qualities and aggregating stats",
     "passes": "self.get_elements(dim); gmsh.model.mesh.getElementQualities(...) -> rows -> pd.DataFrame",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, metrics list[str]|None",
   "outputs": "pd.DataFrame indexed by (element_type, metric)",
   "reads": [
    "gmsh mesh elements + qualities"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.results_viewer",
   "label": "results_viewer",
   "composite": "mesh.queries",
   "signature": "results_viewer(results=None, *, point_data=None, cell_data=None, blocking=False) -> None",
   "summary": "Open apeGmshViewer on a results file or export the bare mesh to a temp .vtu and show it.",
   "file": "src/apeGmsh/mesh/Mesh.py:252",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "if results path: call apeGmshViewer.show; elif point/cell_data: raise NotImplementedError; else VTKExport.write temp .vtu then show",
     "passes": "results:str|None, point_data:dict|None, cell_data:dict|None, blocking:bool",
     "to": "external",
     "to_raw": "apeGmshViewer.show / viz.VTKExport (results viewer; out-of-cluster)"
    }
   ],
   "inputs": "results path or point_data/cell_data dicts, blocking",
   "outputs": "None (viewer side effect)",
   "reads": [
    "live gmsh mesh via VTKExport"
   ],
   "writes": [
    "temp .vtu file when no results path given"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.sizing.set_global_size",
   "label": "set_global_size",
   "composite": "mesh.sizing",
   "signature": "set_global_size(max_size, min_size=0.0) -> _Sizing",
   "summary": "Set the global element-size band via Mesh.MeshSizeMax / Mesh.MeshSizeMin.",
   "file": "src/apeGmsh/mesh/_mesh_sizing.py:30",
   "flow": [
    {
     "node": "mesh.sizing",
     "action": "push min/max size ceiling to gmsh options",
     "passes": "gmsh.option.setNumber('Mesh.MeshSizeMax', max_size:float); gmsh.option.setNumber('Mesh.MeshSizeMin', min_size:float)",
     "to": "gmsh"
    }
   ],
   "inputs": "max_size, min_size",
   "outputs": "self (_Sizing)",
   "reads": [],
   "writes": [
    "Mesh.MeshSizeMax / Mesh.MeshSizeMin options"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.sizing.set_size",
   "label": "set_size",
   "composite": "mesh.sizing",
   "signature": "set_size(tags, size, *, dim=None) -> _Sizing",
   "summary": "Assign a target element size by walking any entity reference down to its BRep points.",
   "file": "src/apeGmsh/mesh/_mesh_sizing.py:126",
   "flow": [
    {
     "node": "mesh.sizing",
     "action": "resolve refs to dimtags at native dim (label/PG aware)",
     "passes": "resolve_to_dimtags(tags, default_dim, session) -> list[(dim,tag)]",
     "to": "mesh.sizing"
    },
    {
     "node": "mesh.sizing",
     "action": "walk each non-point entity to boundary points via gmsh.model.getBoundary(recursive=True)",
     "passes": "[(d,t)] -> list[(0,pt)]",
     "to": "gmsh"
    },
    {
     "node": "mesh.sizing",
     "action": "apply characteristic length on the resolved points",
     "passes": "gmsh.model.mesh.setSize(point_dimtags:list[(0,int)], size:float)",
     "to": "gmsh"
    },
    {
     "node": "mesh.sizing",
     "action": "record set_size directive",
     "passes": "{kind:'set_size', dim:0, tags, size, source_ref}:dict",
     "to": "mesh.sizing"
    }
   ],
   "inputs": "tags (int|str|dimtag|list), size float, dim hint",
   "outputs": "self (_Sizing)",
   "reads": [
    "session label/PG registry",
    "gmsh boundary topology"
   ],
   "writes": [
    "gmsh per-point characteristic length",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.sizing.set_size_all_points",
   "label": "set_size_all_points",
   "composite": "mesh.sizing",
   "signature": "set_size_all_points(size) -> _Sizing",
   "summary": "Assign one characteristic length to every BRep point (normalises CAD-imported lc).",
   "file": "src/apeGmsh/mesh/_mesh_sizing.py:215",
   "flow": [
    {
     "node": "mesh.sizing",
     "action": "get all dim-0 entities and setSize on them",
     "passes": "gmsh.model.getEntities(dim=0) -> gmsh.model.mesh.setSize(pts, size:float)",
     "to": "gmsh"
    },
    {
     "node": "mesh.sizing",
     "action": "record directive",
     "passes": "{kind:'set_size_all_points', size, n_points}:dict",
     "to": "mesh.sizing"
    }
   ],
   "inputs": "size float",
   "outputs": "self (_Sizing)",
   "reads": [
    "gmsh dim-0 entities"
   ],
   "writes": [
    "gmsh per-point characteristic length",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.sizing.set_size_by_physical",
   "label": "set_size_by_physical",
   "composite": "mesh.sizing",
   "signature": "set_size_by_physical(name, size, *, dim=None) -> _Sizing",
   "summary": "Resolve a physical group then delegate to set_size on its entities.",
   "file": "src/apeGmsh/mesh/_mesh_sizing.py:259",
   "flow": [
    {
     "node": "mesh.sizing",
     "action": "if dim given resolve PG via Mesh._resolve_physical (physical.entities) then call set_size on dimtags; else pass name string to set_size",
     "passes": "name:str, dim:int|None -> _resolve_physical(name,dim) -> physical.entities",
     "to": "physical"
    },
    {
     "node": "mesh.sizing",
     "action": "delegate to set_size",
     "passes": "dimtags|name, size:float",
     "to": "mesh.sizing"
    }
   ],
   "inputs": "name (PG name), size, dim filter",
   "outputs": "self (_Sizing)",
   "reads": [
    "g.physical PG registry"
   ],
   "writes": [
    "gmsh per-point characteristic length (via set_size)",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.sizing.set_size_callback",
   "label": "set_size_callback",
   "composite": "mesh.sizing",
   "signature": "set_size_callback(func) -> _Sizing",
   "summary": "Register a Python callback returning the desired element size at any point.",
   "file": "src/apeGmsh/mesh/_mesh_sizing.py:241",
   "flow": [
    {
     "node": "mesh.sizing",
     "action": "register the size callback with gmsh",
     "passes": "gmsh.model.mesh.setSizeCallback(func:Callable[[dim,tag,x,y,z,lc],float])",
     "to": "gmsh"
    },
    {
     "node": "mesh.sizing",
     "action": "record directive",
     "passes": "{kind:'set_size_callback', func_name}:dict",
     "to": "mesh.sizing"
    }
   ],
   "inputs": "func callable",
   "outputs": "self (_Sizing)",
   "reads": [],
   "writes": [
    "gmsh size callback",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.sizing.set_size_global",
   "label": "set_size_global",
   "composite": "mesh.sizing",
   "signature": "set_size_global(*, min_size=None, max_size=None) -> _Sizing",
   "summary": "Set the global mesh-size min and/or max bounds independently.",
   "file": "src/apeGmsh/mesh/_mesh_sizing.py:99",
   "flow": [
    {
     "node": "mesh.sizing",
     "action": "set Mesh.MeshSizeMin/Max for whichever bound is provided",
     "passes": "gmsh.option.setNumber('Mesh.MeshSizeMin'|'Mesh.MeshSizeMax', value:float)",
     "to": "gmsh"
    }
   ],
   "inputs": "min_size, max_size (each float|None)",
   "outputs": "self (_Sizing)",
   "reads": [],
   "writes": [
    "Mesh.MeshSizeMin / Mesh.MeshSizeMax options"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.sizing.set_size_sources",
   "label": "set_size_sources",
   "composite": "mesh.sizing",
   "signature": "set_size_sources(*, from_points=None, from_curvature=None, extend_from_boundary=None) -> _Sizing",
   "summary": "Toggle which Gmsh size sources (points, curvature, boundary extension) are consulted.",
   "file": "src/apeGmsh/mesh/_mesh_sizing.py:59",
   "flow": [
    {
     "node": "mesh.sizing",
     "action": "set MeshSizeFromPoints / MeshSizeFromCurvature / MeshSizeExtendFromBoundary options where not None",
     "passes": "gmsh.option.setNumber('Mesh.MeshSizeFromPoints'|'...FromCurvature'|'...ExtendFromBoundary', int(bool))",
     "to": "gmsh"
    }
   ],
   "inputs": "from_points, from_curvature, extend_from_boundary (each bool|None)",
   "outputs": "self (_Sizing)",
   "reads": [],
   "writes": [
    "Mesh.MeshSizeFromPoints / FromCurvature / ExtendFromBoundary options"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.recombine",
   "label": "recombine",
   "composite": "mesh.structured",
   "signature": "recombine() -> _Structured",
   "summary": "Globally recombine all triangular elements into quads.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:871",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "global recombine",
     "passes": "gmsh.model.mesh.recombine()",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "self (_Structured)",
   "reads": [
    "gmsh mesh"
   ],
   "writes": [
    "gmsh mesh (recombined to quads)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.remove_constraints",
   "label": "remove_constraints",
   "composite": "mesh.structured",
   "signature": "remove_constraints(dim_tags=None) -> _Structured",
   "summary": "Remove all meshing constraints from the given (or all) entities.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:940",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve dim_tags via resolve_to_dimtags (None=all)",
     "passes": "resolve_to_dimtags(dim_tags, default_dim=3, session) -> list[(dim,tag)]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "remove constraints",
     "passes": "gmsh.model.mesh.removeConstraints(dimTags:list[(int,int)])",
     "to": "gmsh"
    }
   ],
   "inputs": "dim_tags (None|int|label|PG|dimtag|list)",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh (cleared meshing constraints)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_compound",
   "label": "set_compound",
   "composite": "mesh.structured",
   "signature": "set_compound(dim, tags) -> _Structured",
   "summary": "Merge entities so they are meshed together as a single compound.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:929",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve tags via resolve_to_tags(dim)",
     "passes": "resolve_to_tags(tags, dim, session) -> list[int]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "declare compound",
     "passes": "gmsh.model.mesh.setCompound(dim:int, resolved:list[int])",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tags (int|label|PG|dimtag|list)",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh compound meshing constraint"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_recombine",
   "label": "set_recombine",
   "composite": "mesh.structured",
   "signature": "set_recombine(tag, *, dim=2, angle=45.0) -> _Structured",
   "summary": "Request quad recombination on a resolved entity.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:855",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve tag(s) via resolve_to_tags(dim)",
     "passes": "resolve_to_tags(tag, dim, session) -> list[int]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "set recombine per entity",
     "passes": "gmsh.model.mesh.setRecombine(dim:int, t:int, angle:float)",
     "to": "gmsh"
    },
    {
     "node": "mesh.structured",
     "action": "record recombine directive",
     "passes": "{kind:'recombine', dim, tag, angle}:dict",
     "to": "mesh.structured"
    }
   ],
   "inputs": "tag, dim, angle",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh recombine constraint",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_recombine_by_physical",
   "label": "set_recombine_by_physical",
   "composite": "mesh.structured",
   "signature": "set_recombine_by_physical(name, *, dim=2, angle=45.0) -> _Structured",
   "summary": "Deprecated alias forwarding a PG name to set_recombine.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:877",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "emit DeprecationWarning then delegate to set_recombine with the PG name",
     "passes": "name:str, dim:int, angle:float",
     "to": "mesh.structured"
    }
   ],
   "inputs": "name (PG name), dim, angle",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry (via set_recombine)"
   ],
   "writes": [
    "gmsh recombine constraint (via set_recombine)",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_smoothing",
   "label": "set_smoothing",
   "composite": "mesh.structured",
   "signature": "set_smoothing(tag, val, *, dim=2) -> _Structured",
   "summary": "Set per-entity smoothing passes on a resolved entity (distinct from the global option).",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:898",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve tag(s) via resolve_to_tags(dim)",
     "passes": "resolve_to_tags(tag, dim, session) -> list[int]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "set smoothing per entity",
     "passes": "gmsh.model.mesh.setSmoothing(dim:int, t:int, val:int)",
     "to": "gmsh"
    },
    {
     "node": "mesh.structured",
     "action": "record smoothing directive",
     "passes": "{kind:'smoothing', dim, tag, val}:dict",
     "to": "mesh.structured"
    }
   ],
   "inputs": "tag, val int, dim",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh per-entity smoothing constraint",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_smoothing_by_physical",
   "label": "set_smoothing_by_physical",
   "composite": "mesh.structured",
   "signature": "set_smoothing_by_physical(name, val, *, dim=2) -> _Structured",
   "summary": "Deprecated alias forwarding a PG name to set_smoothing.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:908",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "emit DeprecationWarning then delegate to set_smoothing with the PG name",
     "passes": "name:str, val:int, dim:int",
     "to": "mesh.structured"
    }
   ],
   "inputs": "name (PG name), val, dim",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry (via set_smoothing)"
   ],
   "writes": [
    "gmsh per-entity smoothing (via set_smoothing)",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_transfinite",
   "label": "set_transfinite",
   "composite": "mesh.structured",
   "signature": "set_transfinite(target=None, *, n=None, size=None, recombine=True, dim=None, angle_tol_deg=5.0) -> _Structured",
   "summary": "High-level dispatcher applying the transfinite cascade to one entity, many, or the whole model with scalar/dict/tuple sizing.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:468",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve target to dimtags (None=all volumes) via resolve_to_dimtags; group by dim, volumes first",
     "passes": "resolve_to_dimtags(target, default_dim, session) -> list[(dim,tag)]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "cluster bounding-edge directions via core._selection._cluster_edge_directions / _order_clusters_by_global_axis; resolve scalar/dict/tuple sizing spec",
     "passes": "edges + spec -> per-cluster node counts",
     "to": "external",
     "to_raw": "core._selection (edge-direction clustering helpers)"
    },
    {
     "node": "mesh.structured",
     "action": "cascade per volume/surface/curve via set_transfinite_curve/_surface/_volume + set_recombine; warn+skip non-decomposable",
     "passes": "node counts -> transfinite constraints",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "query bounding curves/faces for the cascade",
     "passes": "model.queries.boundary_curves / boundary",
     "to": "model.queries"
    }
   ],
   "inputs": "target (None|name|Selection|dimtag|int), exactly one of n|size (scalar/dict/tuple), recombine, dim, angle_tol_deg",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry",
    "g.model.queries geometry",
    "core._selection clustering"
   ],
   "writes": [
    "gmsh transfinite + recombine constraints",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_transfinite_automatic",
   "label": "set_transfinite_automatic",
   "composite": "mesh.structured",
   "signature": "set_transfinite_automatic(dim_tags=None, *, corner_angle=2.35, recombine=True) -> _Structured",
   "summary": "Auto-detect transfinite-compatible faces/volumes and constrain them, skipping split faces.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:285",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "delegate detection+constraint to gmsh",
     "passes": "gmsh.model.mesh.setTransfiniteAutomatic(dimTags:list[(int,int)], cornerAngle:float, recombine:bool)",
     "to": "gmsh"
    },
    {
     "node": "mesh.structured",
     "action": "record transfinite_automatic directive",
     "passes": "{kind:'transfinite_automatic', dim_tags, corner_angle, recombine}:dict",
     "to": "mesh.structured"
    }
   ],
   "inputs": "dim_tags list[(dim,tag)]|None, corner_angle radians, recombine bool",
   "outputs": "self (_Structured)",
   "reads": [
    "gmsh model entities"
   ],
   "writes": [
    "gmsh transfinite + recombine constraints",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_transfinite_box",
   "label": "set_transfinite_box",
   "composite": "mesh.structured",
   "signature": "set_transfinite_box(vol, *, size=None, n=None, recombine=True) -> _Structured",
   "summary": "Apply the full transfinite+recombine hex cascade to a clean hex-decomposable volume.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:358",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve vol to dim-3 tags via resolve_to_tags",
     "passes": "resolve_to_tags(vol, dim=3, session) -> list[int]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "query bounding curves/faces and per-edge bounding box for length-based n",
     "passes": "model.queries.boundary_curves(vtag) / boundary(vtag) / bounding_box(ctag,dim=1)",
     "to": "model.queries"
    },
    {
     "node": "mesh.structured",
     "action": "cascade: set_transfinite_curve per edge, set_transfinite_surface(+set_recombine) per face, set_transfinite_volume",
     "passes": "n_edge:int per curve, then surface/volume constraints",
     "to": "mesh.structured"
    }
   ],
   "inputs": "vol ref, exactly one of size float or n int, recombine",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry",
    "g.model.queries geometry (boundary/bbox)"
   ],
   "writes": [
    "gmsh transfinite curve/surface/volume + recombine constraints",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_transfinite_by_physical",
   "label": "set_transfinite_by_physical",
   "composite": "mesh.structured",
   "signature": "set_transfinite_by_physical(name, *, dim, **kwargs) -> _Structured",
   "summary": "Deprecated alias dispatching a PG name to set_transfinite_curve/surface/volume by dim.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:810",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "emit DeprecationWarning then delegate by dim to set_transfinite_curve/surface/volume with the PG name",
     "passes": "name:str, dim:int, **kwargs",
     "to": "mesh.structured"
    }
   ],
   "inputs": "name (PG name), dim (1/2/3), kwargs",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry (via delegate)"
   ],
   "writes": [
    "gmsh transfinite constraint (via delegate)",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_transfinite_curve",
   "label": "set_transfinite_curve",
   "composite": "mesh.structured",
   "signature": "set_transfinite_curve(tag, n_nodes, *, mesh_type='Progression', coef=1.0) -> _Structured",
   "summary": "Force a curve to be meshed with a deterministic node count and distribution.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:42",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve tag(s) to curve tags via resolve_to_tags(dim=1)",
     "passes": "resolve_to_tags(tag, dim=1, session) -> list[int]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "apply transfinite constraint per curve",
     "passes": "gmsh.model.mesh.setTransfiniteCurve(t:int, n_nodes:int, meshType:str, coef:float)",
     "to": "gmsh"
    },
    {
     "node": "mesh.structured",
     "action": "record transfinite_curve directive",
     "passes": "{kind:'transfinite_curve', tag, n_nodes, mesh_type, coef}:dict",
     "to": "mesh.structured"
    }
   ],
   "inputs": "tag (int|label|PG|dimtag|list), n_nodes int, mesh_type, coef",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh transfinite curve constraint",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_transfinite_surface",
   "label": "set_transfinite_surface",
   "composite": "mesh.structured",
   "signature": "set_transfinite_surface(tag, *, arrangement='Left', corners=None) -> _Structured",
   "summary": "Force a surface to be meshed as a structured grid by transfinite interpolation.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:143",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve tag(s) to surface tags via resolve_to_tags(dim=2)",
     "passes": "resolve_to_tags(tag, dim=2, session) -> list[int]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "apply transfinite surface constraint per surface",
     "passes": "gmsh.model.mesh.setTransfiniteSurface(t:int, arrangement:str, cornerTags:list[int])",
     "to": "gmsh"
    },
    {
     "node": "mesh.structured",
     "action": "record transfinite_surface directive",
     "passes": "{kind:'transfinite_surface', tag, arrangement, corners}:dict",
     "to": "mesh.structured"
    }
   ],
   "inputs": "tag, arrangement, corners list[int]|None",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh transfinite surface constraint",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.structured.set_transfinite_volume",
   "label": "set_transfinite_volume",
   "composite": "mesh.structured",
   "signature": "set_transfinite_volume(tag, *, corners=None) -> _Structured",
   "summary": "Force a volume to be meshed as a structured grid by transfinite interpolation.",
   "file": "src/apeGmsh/mesh/_mesh_structured.py:229",
   "flow": [
    {
     "node": "mesh.structured",
     "action": "resolve tag(s) to volume tags via resolve_to_tags(dim=3)",
     "passes": "resolve_to_tags(tag, dim=3, session) -> list[int]",
     "to": "mesh.structured"
    },
    {
     "node": "mesh.structured",
     "action": "apply transfinite volume constraint per volume",
     "passes": "gmsh.model.mesh.setTransfiniteVolume(t:int, cornerTags:list[int])",
     "to": "gmsh"
    },
    {
     "node": "mesh.structured",
     "action": "record transfinite_volume directive",
     "passes": "{kind:'transfinite_volume', tag, corners}:dict",
     "to": "mesh.structured"
    }
   ],
   "inputs": "tag, corners list[int]|None",
   "outputs": "self (_Structured)",
   "reads": [
    "session label/PG registry"
   ],
   "writes": [
    "gmsh transfinite volume constraint",
    "Mesh._directives log"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh.viewer",
   "label": "viewer",
   "composite": "mesh.queries",
   "signature": "viewer(**kwargs) -> MeshViewer",
   "summary": "Open the interactive Qt mesh viewer with picking and load/constraint/mass overlays.",
   "file": "src/apeGmsh/mesh/Mesh.py:198",
   "flow": [
    {
     "node": "mesh.queries",
     "action": "construct MeshViewer over the session and show it",
     "passes": "session:_ApeGmshSession, **kwargs",
     "to": "external",
     "to_raw": "viewers.mesh_viewer.MeshViewer (Qt mesh viewer; out-of-cluster)"
    }
   ],
   "inputs": "kwargs forwarded to MeshViewer",
   "outputs": "MeshViewer.show() return",
   "reads": [
    "live gmsh mesh via MeshViewer"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.add",
   "label": "add",
   "composite": "mesh_selection",
   "signature": "add(dim, tags, *, name='', tag=-1) -> int",
   "summary": "Add a mesh selection set from explicit node IDs (dim=0) or element IDs (dim>=1) against the live mesh.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:147",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "fetch live mesh nodes/elements and mask to the explicit IDs, gather connected nodes for element sets",
     "passes": "gmsh.model.mesh.getNodes/getElements -> isin(tags) masks",
     "to": "gmsh"
    },
    {
     "node": "mesh_selection",
     "action": "store node/element set under (dim,tag) in _sets",
     "passes": "_store_node_set / _store_element_set(node_ids, coords[, elem_ids, conn])",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tags list[int], name, tag",
   "outputs": "int allocated set tag",
   "reads": [
    "live gmsh mesh nodes/elements"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.add_elements",
   "label": "add_elements",
   "composite": "mesh_selection",
   "signature": "add_elements(dim=2, *, name='', tag=-1, in_box=None, on_plane=None, predicate=None, inclusive=False) -> int",
   "summary": "Create an element set by filtering live-mesh elements on centroid box / all-nodes-on-plane / predicate.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:259",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "fetch live nodes+elements, compute centroids, apply _mesh_filters element masks",
     "passes": "gmsh.model.mesh.getElements -> _flt.element_centroids/elements_in_box/elements_on_plane",
     "to": "gmsh"
    },
    {
     "node": "mesh_selection",
     "action": "gather connected nodes and store the element set",
     "passes": "_store_element_set(dim, tag, name, sel_ids, sel_conn, node_ids, coords)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, name, tag, in_box, on_plane, predicate, inclusive",
   "outputs": "int set tag",
   "reads": [
    "live gmsh mesh nodes/elements"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.add_nodes",
   "label": "add_nodes",
   "composite": "mesh_selection",
   "signature": "add_nodes(*, name='', tag=-1, on_plane=None, in_box=None, in_sphere=None, closest_to=None, count=1, predicate=None, inclusive=False) -> int",
   "summary": "Create a node set (dim=0) by AND-combining spatial queries over live-mesh node coordinates.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:199",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "fetch live node coords then apply _mesh_filters node masks (on_plane/in_box/in_sphere/nearest/predicate)",
     "passes": "gmsh.model.mesh.getNodes -> _flt.nodes_on_plane/nodes_in_box/nodes_in_sphere/nodes_nearest",
     "to": "gmsh"
    },
    {
     "node": "mesh_selection",
     "action": "store the filtered node set",
     "passes": "_store_node_set(tag, name, ids[mask], coords[mask])",
     "to": "mesh_selection"
    }
   ],
   "inputs": "name, tag, on_plane, in_box, in_sphere, closest_to, count, predicate, inclusive",
   "outputs": "int set tag",
   "reads": [
    "live gmsh mesh nodes"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.difference",
   "label": "difference",
   "composite": "mesh_selection",
   "signature": "difference(dim, tag_a, tag_b, *, name='', tag=-1) -> int",
   "summary": "Create a new set as the set difference A \\ B of two same-dim sets.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:457",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "np.setdiff1d the two sets' ids then re-add via add()",
     "passes": "np.setdiff1d(a,b ids) -> self.add(dim, ids, name, tag)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag_a, tag_b, name, tag",
   "outputs": "int new set tag",
   "reads": [
    "MeshSelectionSet._sets",
    "live gmsh mesh (via add)"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.filter_set",
   "label": "filter_set",
   "composite": "mesh_selection",
   "signature": "filter_set(dim, tag, *, name='', new_tag=-1, on_plane=None, in_box=None, in_sphere=None, closest_to=None, count=1, predicate=None, inclusive=False) -> int",
   "summary": "Refine an existing set with AND-combined spatial filters into a new set.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:585",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "load source set; for element sets recompute centroids from live mesh via _mesh_filters",
     "passes": "_sets[(dim,tag)]; _flt.element_centroids(conn,id_to_idx,node_coords)",
     "to": "mesh_selection"
    },
    {
     "node": "mesh_selection",
     "action": "apply _mesh_filters masks then store the filtered set",
     "passes": "_flt.nodes_on_plane/in_box/in_sphere/nearest + predicate -> _store_node_set / _sets[(dim,t)]",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag (source), name, new_tag, on_plane, in_box, in_sphere, closest_to, count, predicate, inclusive",
   "outputs": "int new (filtered) set tag",
   "reads": [
    "MeshSelectionSet._sets",
    "live gmsh mesh nodes (element centroids)"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.from_geometric",
   "label": "from_geometric",
   "composite": "mesh_selection",
   "signature": "from_geometric(selection, *, kind='nodes', name='', tag=-1) -> int",
   "summary": "Seed a mesh selection from a pre-mesh geometric Selection's mesh nodes or elements.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:516",
   "flow": [
    {
     "node": "selection",
     "action": "call Selection.to_mesh_nodes() or to_mesh_elements() to materialise mesh entities of the selected geometry",
     "passes": "selection.to_mesh_nodes()/to_mesh_elements() -> {'tags'|'element_ids','coords'|'connectivity'}",
     "to": "mesh_selection"
    },
    {
     "node": "mesh_selection",
     "action": "store as node set or element set under the selection's dim",
     "passes": "_store_node_set / _sets[(sel_dim,t)] = {...}",
     "to": "mesh_selection"
    }
   ],
   "inputs": "selection (geometric Selection), kind ('nodes'|'elements'), name, tag",
   "outputs": "int mesh selection tag",
   "reads": [
    "g.model.selection geometric selection (to_mesh_nodes/elements)"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.from_physical",
   "label": "from_physical",
   "composite": "mesh_selection",
   "signature": "from_physical(dim, name_or_tag, *, ms_name='', ms_tag=-1) -> int",
   "summary": "Import a Gmsh physical group's mesh nodes as a node selection set.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:476",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "resolve PG name->tag then fetch its mesh nodes from gmsh",
     "passes": "gmsh.model.getPhysicalGroups/getPhysicalName; gmsh.model.mesh.getNodesForPhysicalGroup(dim, pg_tag)",
     "to": "gmsh"
    },
    {
     "node": "mesh_selection",
     "action": "store as a node set",
     "passes": "_store_node_set(t, label, nids, ncoords)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, name_or_tag (PG name|tag), ms_name, ms_tag",
   "outputs": "int mesh selection tag",
   "reads": [
    "gmsh physical groups + mesh nodes"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.get_all",
   "label": "get_all",
   "composite": "mesh_selection",
   "signature": "get_all(dim=-1) -> list[DimTag]",
   "summary": "Return all mesh selection (dim,tag) keys, optionally filtered by dim.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:369",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "return sorted _sets keys filtered by dim",
     "passes": "sorted(_sets.keys()) -> list[(dim,tag)]",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim (-1=all)",
   "outputs": "list[(dim,tag)]",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.get_elements",
   "label": "get_elements",
   "composite": "mesh_selection",
   "signature": "get_elements(dim, tag) -> dict",
   "summary": "Return {'element_ids','connectivity'} for a stored element set (raises for node-only sets).",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:405",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "look up set and return its stored element_ids/connectivity",
     "passes": "_sets[(dim,tag)] -> {'element_ids':ndarray(object), 'connectivity':ndarray(object)}",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "dict {'element_ids','connectivity'}",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.get_entities",
   "label": "get_entities",
   "composite": "mesh_selection",
   "signature": "get_entities(dim, tag) -> list[int]",
   "summary": "Return node IDs (dim=0) or element IDs (dim>=1) of a set.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:374",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "look up set and return its node_ids or element_ids list",
     "passes": "_sets[(dim,tag)] -> list[int]",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "list[int]",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.get_name",
   "label": "get_name",
   "composite": "mesh_selection",
   "signature": "get_name(dim, tag) -> str",
   "summary": "Return the name of a mesh selection set (raises if absent).",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:383",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "look up set and return its name",
     "passes": "_sets[(dim,tag)]['name'] -> str",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "str name",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.get_nodes",
   "label": "get_nodes",
   "composite": "mesh_selection",
   "signature": "get_nodes(dim, tag) -> dict",
   "summary": "Return {'tags','coords'} for a stored mesh selection set.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:395",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "look up set and return its stored node_ids/node_coords",
     "passes": "_sets[(dim,tag)] -> {'tags':ndarray(object), 'coords':ndarray(N,3)}",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "dict {'tags','coords'}",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.get_tag",
   "label": "get_tag",
   "composite": "mesh_selection",
   "signature": "get_tag(dim, name) -> int | None",
   "summary": "Look up the tag of a named mesh selection set at a dim (None if absent).",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:389",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "scan _sets for matching (dim,name)",
     "passes": "_sets items -> tag:int|None",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, name",
   "outputs": "int tag or None",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.intersection",
   "label": "intersection",
   "composite": "mesh_selection",
   "signature": "intersection(dim, tag_a, tag_b, *, name='', tag=-1) -> int",
   "summary": "Create a new set as the intersection of two same-dim sets.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:442",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "np.intersect1d the two sets' ids then re-add via add()",
     "passes": "np.intersect1d(a,b ids) -> self.add(dim, ids, name, tag)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag_a, tag_b, name, tag",
   "outputs": "int new set tag",
   "reads": [
    "MeshSelectionSet._sets",
    "live gmsh mesh (via add)"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.remove",
   "label": "remove",
   "composite": "mesh_selection",
   "signature": "remove(dim_tags) -> MeshSelectionSet",
   "summary": "Remove the specified mesh selection sets by (dim,tag).",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:355",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "pop each (dim,tag) from _sets",
     "passes": "_sets.pop(dt) for dt in dim_tags:list[(int,int)]",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim_tags list[(dim,tag)]",
   "outputs": "self (MeshSelectionSet)",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.remove_all",
   "label": "remove_all",
   "composite": "mesh_selection",
   "signature": "remove_all() -> MeshSelectionSet",
   "summary": "Clear every mesh selection set and reset tag counters.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:360",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "clear _sets and reset _next_tag",
     "passes": "_sets.clear(); _next_tag={0:1,1:1,2:1,3:1}",
     "to": "mesh_selection"
    }
   ],
   "inputs": "none",
   "outputs": "self (MeshSelectionSet)",
   "reads": [],
   "writes": [
    "MeshSelectionSet._sets + _next_tag"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.remove_name",
   "label": "remove_name",
   "composite": "mesh_selection",
   "signature": "remove_name(name) -> MeshSelectionSet",
   "summary": "Clear the name on every set currently carrying it.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:345",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "scan _sets and blank any matching name",
     "passes": "for info in _sets: info['name']='' where name matches",
     "to": "mesh_selection"
    }
   ],
   "inputs": "name",
   "outputs": "self (MeshSelectionSet)",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [
    "MeshSelectionSet._sets (name)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.select",
   "label": "select",
   "composite": "mesh_selection",
   "signature": "select(*, level='node', dim=2, ids=None, name=None) -> MeshSelectionChain",
   "summary": "Start an additive daisy-chainable live-mesh selection (node or element level) without registering a set.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:715",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "build/fetch the per-(level,dim) _LiveMeshEngine adapter (engine_for)",
     "passes": "engine_for(self, level:str, dim:int) -> _LiveMeshEngine",
     "to": "mesh_selection"
    },
    {
     "node": "mesh_selection",
     "action": "seed atoms: explicit ids, or existing set name via _seed_ids_by_name (get_tag/get_nodes/get_elements), or full live-mesh universe via _get_mesh_nodes/_get_mesh_elements",
     "passes": "atoms:list[int]",
     "to": "gmsh"
    },
    {
     "node": "mesh_selection",
     "action": "construct the chain over the atoms bound to the engine",
     "passes": "MeshSelectionChain(atoms, _engine=eng)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "level ('node'|'element'), dim, ids list|None, name str|None (mutually exclusive with ids)",
   "outputs": "MeshSelectionChain",
   "reads": [
    "live gmsh mesh nodes/elements",
    "MeshSelectionSet._sets (name seed only)"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.set_name",
   "label": "set_name",
   "composite": "mesh_selection",
   "signature": "set_name(dim, tag, name) -> MeshSelectionSet",
   "summary": "Rename an existing mesh selection set.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:338",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "look up (dim,tag) in _sets and overwrite its name",
     "passes": "_sets[(dim,tag)]['name'] = name:str",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag, name",
   "outputs": "self (MeshSelectionSet)",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [
    "MeshSelectionSet._sets (name)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.sort_set",
   "label": "sort_set",
   "composite": "mesh_selection",
   "signature": "sort_set(dim, tag, *, by='x', descending=False) -> None",
   "summary": "Sort a set's entries in place by a coordinate axis (node coords or element centroids).",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:836",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "argsort node coords (dim=0) or live-mesh element centroids by axis and reorder in place",
     "passes": "_sets[(dim,tag)]; _flt.element_centroids(...); np.argsort(axis)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag, by ('x'|'y'|'z'), descending",
   "outputs": "None (in-place)",
   "reads": [
    "MeshSelectionSet._sets",
    "live gmsh mesh nodes (element centroids)"
   ],
   "writes": [
    "MeshSelectionSet._sets (reordered)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.summary",
   "label": "summary",
   "composite": "mesh_selection",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame describing all mesh selection sets (dim, tag, name, n_nodes, n_elems).",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:881",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "iterate _sets into a DataFrame indexed by (dim,tag)",
     "passes": "_sets -> pd.DataFrame",
     "to": "mesh_selection"
    }
   ],
   "inputs": "none",
   "outputs": "pd.DataFrame",
   "reads": [
    "MeshSelectionSet._sets"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.to_dataframe",
   "label": "to_dataframe",
   "composite": "mesh_selection",
   "signature": "to_dataframe(dim, tag) -> pd.DataFrame",
   "summary": "DataFrame of a single set's entries (node_id+xyz, or element_id+centroid+n_nodes).",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:897",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "for dim=0 emit node_id/x/y/z; for dim>0 recompute centroids from live mesh and emit element_id/cx/cy/cz/n_nodes",
     "passes": "_sets[(dim,tag)]; _flt.element_centroids(...) -> pd.DataFrame",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "pd.DataFrame",
   "reads": [
    "MeshSelectionSet._sets",
    "live gmsh mesh nodes (element centroids)"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.mesh_selection.union",
   "label": "union",
   "composite": "mesh_selection",
   "signature": "union(dim, tag_a, tag_b, *, name='', tag=-1) -> int",
   "summary": "Create a new set as the union of two same-dim sets.",
   "file": "src/apeGmsh/mesh/MeshSelectionSet.py:427",
   "flow": [
    {
     "node": "mesh_selection",
     "action": "np.union1d the two sets' ids then re-add via add()",
     "passes": "np.union1d(a,b ids) -> self.add(dim, ids, name, tag)",
     "to": "mesh_selection"
    }
   ],
   "inputs": "dim, tag_a, tag_b, name, tag",
   "outputs": "int new set tag",
   "reads": [
    "MeshSelectionSet._sets",
    "live gmsh mesh (via add)"
   ],
   "writes": [
    "MeshSelectionSet._sets"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.model.boolean.cut",
   "label": "cut",
   "composite": "model.boolean",
   "signature": "cut(objects, tools, *, dim=3, remove_object=True, remove_tool=True, sync=True, label=None) -> list[int]",
   "summary": "Boolean difference (A−B); returns surviving volume tags.",
   "file": "src/apeGmsh/core/_model_boolean.py:153",
   "flow": [
    {
     "node": "model.boolean",
     "action": "_bool_op: resolve_to_dimtags(objects/tools) tier walk",
     "passes": "obj_dt,tool_dt:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved(): occ.cut(...); if sync synchronize(); pg.set_result(absorbed=False)",
     "passes": "result,result_map",
     "to": "physical"
    },
    {
     "node": "parts",
     "action": "if parts: parts._remap_from_result",
     "passes": "remapped instances",
     "to": "model.boolean"
    },
    {
     "node": "model.boolean",
     "action": "metadata pop consumed; _register(...,'cut'); apply label override",
     "passes": "tags:list[int]",
     "to": "labels"
    }
   ],
   "inputs": "objects,tools: EntityRefs; dim default for bare ints; remove flags; label replaces object's label on result",
   "outputs": "list[int] surviving volume tags; PGs/parts remapped; metadata kind='cut'",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.boolean.fragment",
   "label": "fragment",
   "composite": "model.boolean",
   "signature": "fragment(objects, tools, *, dim=3, remove_object=True, remove_tool=True, cleanup_free=True, sync=True) -> list[int]",
   "summary": "Boolean fragment — split all shapes at intersections, keep all sub-volumes (conformal meshing).",
   "file": "src/apeGmsh/core/_model_boolean.py:200",
   "flow": [
    {
     "node": "model.boolean",
     "action": "_bool_op('fragment',...) tier-resolve, pg_preserved fragment + remap (no label override)",
     "passes": "result tags:list[int]",
     "to": "physical"
    },
    {
     "node": "model.boolean",
     "action": "if cleanup_free & 3-D exist: find dim-2 surfaces with no upward adjacency & centroid outside every vol bbox",
     "passes": "free [(2,t)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.remove(free,recursive=True); if sync synchronize(); metadata pop; cleanup_label_pgs(free)",
     "passes": "result:list[int]",
     "to": "labels"
    }
   ],
   "inputs": "objects,tools: EntityRefs (tool dims auto-resolved); dim default for bare ints; cleanup_free drops stray exterior cutting-plane surfaces; remove flags",
   "outputs": "list[int] surviving tags at target dim; metadata kind='fragment'; free surfaces cleaned; PGs/parts remapped",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.boolean.fuse",
   "label": "fuse",
   "composite": "model.boolean",
   "signature": "fuse(objects, tools, *, dim=3, remove_object=True, remove_tool=True, sync=True, label=None) -> list[int]",
   "summary": "Boolean union (A∪B); returns surviving volume tags.",
   "file": "src/apeGmsh/core/_model_boolean.py:125",
   "flow": [
    {
     "node": "model.boolean",
     "action": "_bool_op: resolve_to_dimtags(objects/tools, default_dim) tier walk; snapshot input label names",
     "passes": "obj_dt,tool_dt:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved(): occ.fuse(obj,tool,remove flags); if sync synchronize(); pg.set_result(absorbed=True)",
     "passes": "result,result_map",
     "to": "physical"
    },
    {
     "node": "parts",
     "action": "if parts: parts._remap_from_result(absorbed_into_result=True)",
     "passes": "remapped instance entities",
     "to": "model.boolean"
    },
    {
     "node": "model.boolean",
     "action": "metadata pop consumed; _register(d,t,None,'fuse'); strip input labels per-dim, labels.add new label",
     "passes": "tags:list[int]",
     "to": "labels"
    }
   ],
   "inputs": "objects,tools: EntityRefs (int|(dim,tag)|label|PG|part|list mix); dim default for bare ints; remove_object/remove_tool; label replaces input labels on result",
   "outputs": "list[int] surviving volume tags; PGs preserved+remapped; parts remapped; metadata kind='fuse'",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.boolean.intersect",
   "label": "intersect",
   "composite": "model.boolean",
   "signature": "intersect(objects, tools, *, dim=3, remove_object=True, remove_tool=True, sync=True, label=None) -> list[int]",
   "summary": "Boolean intersection (A∩B); returns surviving volume tags.",
   "file": "src/apeGmsh/core/_model_boolean.py:179",
   "flow": [
    {
     "node": "model.boolean",
     "action": "_bool_op: resolve_to_dimtags(objects/tools) tier walk",
     "passes": "obj_dt,tool_dt:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved(): occ.intersect(...); if sync synchronize(); pg.set_result(absorbed=True)",
     "passes": "result,result_map",
     "to": "physical"
    },
    {
     "node": "parts",
     "action": "if parts: parts._remap_from_result(absorbed_into_result=True)",
     "passes": "remapped instances",
     "to": "model.boolean"
    },
    {
     "node": "model.boolean",
     "action": "metadata pop consumed; _register(...,'intersect'); apply label override",
     "passes": "tags:list[int]",
     "to": "labels"
    }
   ],
   "inputs": "objects,tools: EntityRefs; dim default for bare ints; remove flags; label replaces input labels on result",
   "outputs": "list[int] surviving volume tags; PGs/parts remapped; metadata kind='intersect'",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_arc",
   "label": "add_arc",
   "composite": "model.geometry",
   "signature": "add_arc(start, center, end, *, through_point=False, label=None, sync=True) -> int",
   "summary": "Add a circular arc from three point references (center = circle centre, or a point on the arc).",
   "file": "src/apeGmsh/core/_model_geometry.py:743",
   "flow": [
    {
     "node": "model.geometry",
     "action": "_resolve_entity_tag start/center/end (dim=0)",
     "passes": "3 point tags:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string refs to single dim-0 tags",
     "passes": "(0,tag) x3",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addCircleArc(start,center,end,center=not through_point); if sync synchronize()",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,tag,label,'arc')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "start/center/end point refs; through_point switches center meaning to a point on the arc; label optional",
   "outputs": "int arc tag; metadata kind='arc'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_axis_cutting_plane",
   "label": "add_axis_cutting_plane",
   "composite": "model.geometry",
   "signature": "add_axis_cutting_plane(axis, offset=0.0, *, origin=None, rotation=0.0, rotation_about=None, label=None, sync=True) -> int",
   "summary": "Add an axis-aligned cutting plane, optionally tilted, delegating to add_cutting_plane.",
   "file": "src/apeGmsh/core/_model_geometry.py:1415",
   "flow": [
    {
     "node": "model.geometry",
     "action": "validate axis; reject rotation without rotation_about; Rodrigues-rotate base normal; point=origin+offset*normal",
     "passes": "point, base_normal: ndarray",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "return self.add_cutting_plane(point, base_normal, label, sync)",
     "passes": "point+normal+label+sync",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "(via add_cutting_plane) build BRep square + register",
     "passes": "surface tag:int",
     "to": "labels"
    }
   ],
   "inputs": "axis 'x'|'y'|'z' (plane normal to it); offset signed dist along base normal; origin anchor (default 0); rotation deg + rotation_about to tilt; label optional",
   "outputs": "int surface tag; same registration/stash as add_cutting_plane",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_bezier",
   "label": "add_bezier",
   "composite": "model.geometry",
   "signature": "add_bezier(point_tags, *, label=None, sync=True) -> int",
   "summary": "Add a Bezier curve through control points (endpoints interpolated, interior are handles).",
   "file": "src/apeGmsh/core/_model_geometry.py:944",
   "flow": [
    {
     "node": "model.geometry",
     "action": "require >=2 pts; _resolve_entity_tag each (dim=0)",
     "passes": "list[ctrl pt tag:int]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string refs to single dim-0 tags",
     "passes": "[(0,tag)...]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addBezier(pts); if sync synchronize()",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,tag,label,'bezier')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "point_tags control points (≥2); label optional",
   "outputs": "int curve tag; metadata kind='bezier'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_box",
   "label": "add_box",
   "composite": "model.geometry",
   "signature": "add_box(x, y, z, dx, dy, dz, *, label=None, sync=True) -> int",
   "summary": "Add an axis-aligned box solid.",
   "file": "src/apeGmsh/core/_model_geometry.py:2157",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addBox(x,y,z,dx,dy,dz)",
     "passes": "origin,size: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_add_solid: if sync synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(3,tag,label,'box')",
     "passes": "(3,tag)+label",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "if label & _auto_pg_from_label: labels.add(3,[tag],name)",
     "passes": "name+(3,tag)",
     "to": "physical"
    }
   ],
   "inputs": "x,y,z origin corner; dx,dy,dz extents; label optional",
   "outputs": "int volume tag; metadata kind='box'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_bspline",
   "label": "add_bspline",
   "composite": "model.geometry",
   "signature": "add_bspline(point_tags, *, degree=3, weights=None, knots=None, multiplicities=None, label=None, sync=True) -> int",
   "summary": "Add a B-spline curve attracted to (not interpolating) explicit control points.",
   "file": "src/apeGmsh/core/_model_geometry.py:900",
   "flow": [
    {
     "node": "model.geometry",
     "action": "require >=2 pts; _resolve_entity_tag each (dim=0)",
     "passes": "list[ctrl pt tag:int]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string refs to single dim-0 tags",
     "passes": "[(0,tag)...]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addBSpline(pts,degree,weights,knots,multiplicities); if sync synchronize()",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,tag,label,'bspline')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "point_tags control points (≥2); degree (default 3); optional weights/knots/multiplicities; label optional",
   "outputs": "int curve tag; metadata kind='bspline'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_circle",
   "label": "add_circle",
   "composite": "model.geometry",
   "signature": "add_circle(cx, cy, cz, radius, *, angle1=0.0, angle2=2*pi, label=None, sync=True) -> int",
   "summary": "Add a full circle (or arc sector) as a single curve from centre+radius.",
   "file": "src/apeGmsh/core/_model_geometry.py:797",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addCircle(cx,cy,cz,radius,angle1,angle2)",
     "passes": "centre,radius,angles: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,tag,label,'circle')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "cx,cy,cz centre; radius; angle1/angle2 radians (default full circle); label optional",
   "outputs": "int curve tag; metadata kind='circle'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_cone",
   "label": "add_cone",
   "composite": "model.geometry",
   "signature": "add_cone(x, y, z, dx, dy, dz, r1, r2, *, angle=2*pi, label=None, sync=True) -> int",
   "summary": "Add a cone / truncated cone solid.",
   "file": "src/apeGmsh/core/_model_geometry.py:2221",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addCone(x,y,z,dx,dy,dz,r1,r2,angle)",
     "passes": "base,axis,r1,r2,angle: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_add_solid: if sync synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(3,tag,label,'cone')",
     "passes": "(3,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "x,y,z base centre; dx,dy,dz axis vector; r1 base radius; r2 top radius (0=sharp); angle radians; label optional",
   "outputs": "int volume tag; metadata kind='cone'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_curve_loop",
   "label": "add_curve_loop",
   "composite": "model.geometry",
   "signature": "add_curve_loop(curve_tags, *, label=None, sync=True) -> int",
   "summary": "Assemble curves into a closed curve loop for surface creation.",
   "file": "src/apeGmsh/core/_model_geometry.py:1066",
   "flow": [
    {
     "node": "model.geometry",
     "action": "_resolve_entity_tag each (dim=1, allow_sign=True)",
     "passes": "signed curve tags:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string refs to single dim-1 tags (sign re-applied)",
     "passes": "[curve tag...]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addCurveLoop(curve_tags); if sync synchronize()",
     "passes": "loop tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,tag,label,'curve_loop')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "curve_tags: ordered closed-loop curve refs (sign or '-'name reverses orientation); label optional",
   "outputs": "int curve-loop tag; metadata kind='curve_loop'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_cutting_plane",
   "label": "add_cutting_plane",
   "composite": "model.geometry",
   "signature": "add_cutting_plane(point, normal_vector, *, size=None, label=None, sync=True) -> int",
   "summary": "Create a square BRep plane through a point with a given normal (real geometry, for cut/section).",
   "file": "src/apeGmsh/core/_model_geometry.py:1292",
   "flow": [
    {
     "node": "model.geometry",
     "action": "normalise normal; if size None: synchronize()+getBoundingBox -> 2*diag; build orthonormal (u,v) + 4 corners",
     "passes": "4 corner xyz: float",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "self.add_point x4 -> add_line x4 -> add_curve_loop -> add_plane_surface (all sync=False)",
     "passes": "surface tag:int",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize()",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "if sync _register(2,tag,label,'cutting_plane') else metadata kind only; stash entry['point']/['normal']",
     "passes": "(2,tag)+label+normal/point",
     "to": "labels"
    }
   ],
   "inputs": "point: a point on the plane (square centred here); normal_vector (auto-normalised); size edge length (None=2*model-bbox-diag); label optional",
   "outputs": "int surface tag; metadata kind='cutting_plane' with stashed point+normal (consumed by cut_by_plane); optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_cylinder",
   "label": "add_cylinder",
   "composite": "model.geometry",
   "signature": "add_cylinder(x, y, z, dx, dy, dz, radius, *, angle=2*pi, label=None, sync=True) -> int",
   "summary": "Add a cylinder solid from base centre, axis vector, and radius.",
   "file": "src/apeGmsh/core/_model_geometry.py:2194",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addCylinder(x,y,z,dx,dy,dz,radius,angle)",
     "passes": "base,axis,radius,angle: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_add_solid: if sync synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(3,tag,label,'cylinder')",
     "passes": "(3,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "x,y,z base-circle centre; dx,dy,dz axis vector (length=height); radius; angle radians sweep (default full); label optional",
   "outputs": "int volume tag; metadata kind='cylinder'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_ellipse",
   "label": "add_ellipse",
   "composite": "model.geometry",
   "signature": "add_ellipse(cx, cy, cz, r_major, r_minor, *, angle1=0.0, angle2=2*pi, label=None, sync=True) -> int",
   "summary": "Add a full ellipse (or elliptic arc) as a single curve.",
   "file": "src/apeGmsh/core/_model_geometry.py:830",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addEllipse(cx,cy,cz,r_major,r_minor,angle1,angle2)",
     "passes": "centre,radii,angles: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,tag,label,'ellipse')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "cx,cy,cz centre; r_major (along X pre-rotation), r_minor; angle1/angle2 radians; label optional",
   "outputs": "int curve tag; metadata kind='ellipse'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_imperfect_line",
   "label": "add_imperfect_line",
   "composite": "model.geometry",
   "signature": "add_imperfect_line(start, end, *, magnitude=0.0, direction, shape='kink', n_segments=8, modes=None, label=None, sync=True) -> list[int]",
   "summary": "Add a line with a baked-in geometric imperfection (kink/sine/multi_mode polyline).",
   "file": "src/apeGmsh/core/_model_geometry.py:142",
   "flow": [
    {
     "node": "model.geometry",
     "action": "_resolve_entity_tag both endpoints (dim=0)",
     "passes": "start,end tags:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string endpoint refs to single dim-0 tag",
     "passes": "(0,tag)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getValue endpoint coords; build offset envelope; occ.addPoint/addLine per segment; if sync synchronize()",
     "passes": "list[line tag]",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,ln,None,'imperfect_line') per segment",
     "passes": "(1,tag) x N",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "if label & _auto_pg_from_label: single batched labels.add(1, all segs, name)",
     "passes": "name+[tags]",
     "to": "physical"
    }
   ],
   "inputs": "start/end point refs; magnitude peak offset; direction (dx,dy,dz) hint (projected ⟂ to axis); shape kink|sine|multi_mode; n_segments; modes [(k,a)] for multi_mode; label applied to all segments",
   "outputs": "list[int] segment tags in start→end order; metadata kind='imperfect_line'; one batched Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_line",
   "label": "add_line",
   "composite": "model.geometry",
   "signature": "add_line(start, end, *, label=None, sync=True) -> int",
   "summary": "Add a straight line between two existing points.",
   "file": "src/apeGmsh/core/_model_geometry.py:117",
   "flow": [
    {
     "node": "model.geometry",
     "action": "_resolve_entity_tag(start/end, dim=0) — int passthrough else resolve_to_single_dimtag tier walk",
     "passes": "start,end point tags:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "string refs -> label(T1)/PG(T2)/part(T3) single dim=0 hit",
     "passes": "(0,tag)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addLine(start,end); if sync occ.synchronize()",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1, tag, label, 'line')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "start/end: point ref (int|label|PG|part|(dim,tag)) each resolving to exactly one point; label optional",
   "outputs": "int line tag; metadata kind='line'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_plane_surface",
   "label": "add_plane_surface",
   "composite": "model.geometry",
   "signature": "add_plane_surface(wire_tags, *, label=None, sync=True) -> int",
   "summary": "Create a planar surface bounded by one (outer) or more (holes) curve loops.",
   "file": "src/apeGmsh/core/_model_geometry.py:1107",
   "flow": [
    {
     "node": "model.geometry",
     "action": "wrap scalar to list; _resolve_entity_tag each (dim=1)",
     "passes": "loop tags:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string refs to single dim-1 curve-loop tags",
     "passes": "[loop tag...]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addPlaneSurface(wire_tags); if sync synchronize()",
     "passes": "surface tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(2,tag,label,'plane_surface')",
     "passes": "(2,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "wire_tags: curve-loop ref or list (first = outer boundary, rest = holes); label optional",
   "outputs": "int surface tag; metadata kind='plane_surface'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_point",
   "label": "add_point",
   "composite": "model.geometry",
   "signature": "add_point(x, y, z, *, mesh_size=0.0, lc=None, label=None, sync=True) -> int",
   "summary": "Add a single point with an optional target mesh size.",
   "file": "src/apeGmsh/core/_model_geometry.py:83",
   "flow": [
    {
     "node": "model.geometry",
     "action": "lc aliases mesh_size; gmsh.model.occ.addPoint",
     "passes": "x,y,z,meshSize: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync: occ.synchronize(); return point tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_model._register(0, tag, label, 'point')",
     "passes": "(0,tag)+label",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "if label & _auto_pg_from_label: labels.add(0,[tag],name) -> Tier-1 _label: PG",
     "passes": "name+(dim,tag)",
     "to": "physical"
    }
   ],
   "inputs": "x,y,z floats (coords); mesh_size/lc float (characteristic length, 0=global); label optional name",
   "outputs": "int point tag; registers metadata kind='point'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_rectangle",
   "label": "add_rectangle",
   "composite": "model.geometry",
   "signature": "add_rectangle(x, y, z, dx, dy, *, angles_deg=None, angles_rad=None, pivot=(0,0,0), rounded_radius=0.0, label=None, sync=True) -> int",
   "summary": "Add a rectangular planar surface in XY, optionally rotated in place and corner-rounded.",
   "file": "src/apeGmsh/core/_model_geometry.py:1169",
   "flow": [
    {
     "node": "model.geometry",
     "action": "reject both angles_*; convert deg->rad; occ.addRectangle(x,y,z,dx,dy,roundedRadius)",
     "passes": "corner,size,radius: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if angles: occ.rotate about centre+pivot per X/Y/Z; if sync synchronize()",
     "passes": "surface tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(2,tag,label,'rectangle')",
     "passes": "(2,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "x,y,z corner; dx,dy extents; angles_deg|angles_rad (rx,ry,rz, exactly one); pivot offset from centre; rounded_radius; label optional",
   "outputs": "int surface tag; metadata kind='rectangle'; optional Tier-1 label PG (commonly used as a fragment cutting tool)",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_sphere",
   "label": "add_sphere",
   "composite": "model.geometry",
   "signature": "add_sphere(cx, cy, cz, radius, *, label=None, sync=True) -> int",
   "summary": "Add a sphere solid centred at (cx,cy,cz).",
   "file": "src/apeGmsh/core/_model_geometry.py:2179",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addSphere(cx,cy,cz,radius)",
     "passes": "centre,radius: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_add_solid: if sync synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(3,tag,label,'sphere')",
     "passes": "(3,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "cx,cy,cz centre; radius; label optional",
   "outputs": "int volume tag; metadata kind='sphere'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_spline",
   "label": "add_spline",
   "composite": "model.geometry",
   "signature": "add_spline(point_tags, *, label=None, sync=True) -> int",
   "summary": "Add a C2 interpolating spline that passes through the given points.",
   "file": "src/apeGmsh/core/_model_geometry.py:860",
   "flow": [
    {
     "node": "model.geometry",
     "action": "require >=2 pts; _resolve_entity_tag each (dim=0)",
     "passes": "list[point tag:int]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string refs to single dim-0 tags",
     "passes": "[(0,tag)...]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addSpline(point_tags); if sync synchronize()",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(1,tag,label,'spline')",
     "passes": "(1,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "point_tags: ordered list of point refs (≥2; repeat first for closed); label optional",
   "outputs": "int curve tag; metadata kind='spline'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_surface_filling",
   "label": "add_surface_filling",
   "composite": "model.geometry",
   "signature": "add_surface_filling(wire_tag, *, label=None, sync=True) -> int",
   "summary": "Create a Coons-patch (non-planar) surface bounded by a single curve loop.",
   "file": "src/apeGmsh/core/_model_geometry.py:1144",
   "flow": [
    {
     "node": "model.geometry",
     "action": "_resolve_entity_tag(wire_tag, dim=1)",
     "passes": "loop tag:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string ref to single dim-1 curve-loop tag",
     "passes": "(1,tag)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addSurfaceFilling(wire_tag); if sync synchronize()",
     "passes": "surface tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(2,tag,label,'surface_filling')",
     "passes": "(2,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "wire_tag: single curve-loop ref; label optional",
   "outputs": "int surface tag; metadata kind='surface_filling'; optional Tier-1 label PG",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_torus",
   "label": "add_torus",
   "composite": "model.geometry",
   "signature": "add_torus(cx, cy, cz, r1, r2, *, angle=2*pi, label=None, sync=True) -> int",
   "summary": "Add a torus solid (major radius r1, tube radius r2).",
   "file": "src/apeGmsh/core/_model_geometry.py:2248",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addTorus(cx,cy,cz,r1,r2,angle)",
     "passes": "centre,r1,r2,angle: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_add_solid: if sync synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(3,tag,label,'torus')",
     "passes": "(3,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "cx,cy,cz centre; r1 major radius; r2 minor (tube) radius; angle radians sweep; label optional",
   "outputs": "int volume tag; metadata kind='torus'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_wedge",
   "label": "add_wedge",
   "composite": "model.geometry",
   "signature": "add_wedge(x, y, z, dx, dy, dz, ltx, *, label=None, sync=True) -> int",
   "summary": "Add a right-angle wedge solid.",
   "file": "src/apeGmsh/core/_model_geometry.py:2273",
   "flow": [
    {
     "node": "model.geometry",
     "action": "gmsh.model.occ.addWedge(x,y,z,dx,dy,dz,ltx)",
     "passes": "origin,size,ltx: float",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_add_solid: if sync synchronize(); return tag",
     "passes": "tag:int",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_register(3,tag,label,'wedge')",
     "passes": "(3,tag)+label",
     "to": "labels"
    }
   ],
   "inputs": "x,y,z origin corner; dx,dy,dz extents; ltx top-X extent (0=sharp); label optional",
   "outputs": "int volume tag; metadata kind='wedge'; optional Tier-1 label PG",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.add_wire",
   "label": "add_wire",
   "composite": "model.geometry",
   "signature": "add_wire(curve_tags, *, check_closed=False, label=None, sync=True) -> int",
   "summary": "Assemble curves into a transient OCC wire (sweep/loft path); label= is rejected.",
   "file": "src/apeGmsh/core/_model_geometry.py:976",
   "flow": [
    {
     "node": "model.geometry",
     "action": "raise ValueError if label is not None (wire is transient, not a model entity)",
     "passes": "curve_tags",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "_resolve_entity_tag each (dim=1, allow_sign=True) — leading '-'/neg reverses orientation",
     "passes": "signed curve tags:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve string refs to single dim-1 tags (sign re-applied)",
     "passes": "[curve tag...]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addWire(curve_tags,checkClosed); if sync synchronize()",
     "passes": "wire tag:int",
     "to": "model.geometry"
    }
   ],
   "inputs": "curve_tags: ordered curve refs end-to-end (sign reverses orientation); check_closed verifies closed loop; label MUST be None",
   "outputs": "int OCC wire tag (NOT registered, not a persistent entity) — use directly as sweep/thru_sections path",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.cut_by_plane",
   "label": "cut_by_plane",
   "composite": "model.geometry",
   "signature": "cut_by_plane(solid, plane, *, keep_plane=True, remove_original=True, above_direction=None, label_above=None, label_below=None, sync=True) -> tuple[list[int], list[int]]",
   "summary": "Split solids with a plane and classify fragments above/below by the plane normal.",
   "file": "src/apeGmsh/core/_model_geometry.py:1746",
   "flow": [
    {
     "node": "model.geometry",
     "action": "occ.synchronize(); resolve_to_single_dimtag(plane, dim=2) must be 2-D; _resolve_plane_normal (above_direction|stashed|getNormal)",
     "passes": "plane_tag:int, normal/point: ndarray",
     "to": "labels"
    },
    {
     "node": "model.geometry",
     "action": "delegate self.cut_by_surface(solid,plane_tag,keep_surface=keep_plane,label=None,sync=True)",
     "passes": "fragments:list[int]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_classify_fragments: sign of (COM-point)·normal; _try_label each side via labels.add",
     "passes": "above_tags,below_tags",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "labels.add(3,[tag],label_above/below) per fragment; if sync synchronize()",
     "passes": "(above,below)",
     "to": "model.geometry"
    }
   ],
   "inputs": "solid as cut_by_surface; plane: 2-D ref (ideally from add_cutting_plane so normal/point stashed); above_direction overrides normal; label_above/label_below per side; keep_plane/remove_original",
   "outputs": "tuple(above_tags, below_tags) classified by centroid side; warns if only one side produced",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.cut_by_surface",
   "label": "cut_by_surface",
   "composite": "model.geometry",
   "signature": "cut_by_surface(solid, surface, *, keep_surface=True, remove_original=True, label=None, sync=True) -> list[int]",
   "summary": "Split solids with an arbitrary cutting surface via OCC fragment (all pieces kept).",
   "file": "src/apeGmsh/core/_model_geometry.py:1609",
   "flow": [
    {
     "node": "model.geometry",
     "action": "_normalize_solid_input (None=all vols; str label->dim3 tags via labels.entities; int/list mix)",
     "passes": "solid_tags:list[int]",
     "to": "labels"
    },
    {
     "node": "model.geometry",
     "action": "occ.synchronize() then resolve_to_single_dimtag(surface, dim=2) — must be 2-D",
     "passes": "surf (2,tag)",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "tier-resolve surface ref; snapshot original solid labels for inheritance",
     "passes": "obj_dt[(3,t)], tool_dt[(2,t)]",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "pg_preserved(): occ.fragment(obj,tool,remove flags); synchronize(); pg.set_result remaps PGs",
     "passes": "out_dimtags, result_map",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "collect dim-3 fragments; metadata pop originals; _register fragments 'cut_fragment' + kept 'cut_interface'",
     "passes": "list[int] volume frags",
     "to": "model.geometry"
    }
   ],
   "inputs": "solid: tag|label|list|None (None=all vols); surface: 2-D ref (must resolve to one); keep_surface keeps trimmed face; remove_original consumes inputs; label applied to fragments (else single original label inherited)",
   "outputs": "list[int] new volume fragment tags; PGs remapped; metadata kinds cut_fragment/cut_interface",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.replace_line",
   "label": "replace_line",
   "composite": "model.geometry",
   "signature": "replace_line(line_tag, *, magnitude=0.0, direction, shape='kink', n_segments=8, modes=None, sync=True) -> list[int]",
   "summary": "Retrofit an existing straight line with an imperfection, re-wiring all PGs that referenced it.",
   "file": "src/apeGmsh/core/_model_geometry.py:372",
   "flow": [
    {
     "node": "model.geometry",
     "action": "validate metadata kind='line'; getBoundary -> 2 endpoint pts; scan all dim-1 PGs containing the line",
     "passes": "start/end tags + captured PG (tag,name,other_ents)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "removePhysicalGroups(old) then occ.remove([(1,line)]) then synchronize()",
     "passes": "removed line; metadata pop",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "call self.add_imperfect_line(start,end,...,label=None,sync=True)",
     "passes": "endpoints + imperfection params",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "re-add each captured PG as addPhysicalGroup(1, other∪new, name); if sync synchronize()",
     "passes": "list[new line tags]",
     "to": "model.geometry"
    }
   ],
   "inputs": "line_tag: int of an existing kind='line' straight line (arcs/splines/imperfect rejected); magnitude/direction/shape/n_segments/modes as add_imperfect_line",
   "outputs": "list[int] new segment tags; old line + its PGs deleted, every PG (incl _label:) re-created with the polyline spliced in",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.slice",
   "label": "slice",
   "composite": "model.geometry",
   "signature": "slice(solid=None, *, axis, offset=0.0, classify=False, label=None, sync=True) -> list[int] | tuple[list[int], list[int]]",
   "summary": "Atomically slice solids at an axis-aligned plane with no orphan geometry left behind.",
   "file": "src/apeGmsh/core/_model_geometry.py:2028",
   "flow": [
    {
     "node": "model.geometry",
     "action": "snapshot all pre-entities per dim; self.add_axis_cutting_plane(axis,offset,sync=False)",
     "passes": "plane_dt=(2,plane_tag)",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "if classify: cut_by_plane(...,keep_plane=False,sync=False) else cut_by_surface(...,keep_surface=False,sync=False)",
     "passes": "fragments / (above,below)",
     "to": "gmsh"
    },
    {
     "node": "model.geometry",
     "action": "_cleanup_slice_orphans: synchronize(); walk kept volumes' boundary; occ.remove anything new not on a survivor; cleanup_label_pgs",
     "passes": "removed dimtags",
     "to": "labels"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize()",
     "passes": "result",
     "to": "model.geometry"
    }
   ],
   "inputs": "solid: tag|label|list|None (None=all vols); axis 'x'|'y'|'z'; offset signed dist from origin; classify returns (pos,neg) sides; label on all fragments",
   "outputs": "list[int] all fragments (classify=False) or tuple(positive,negative) (classify=True); temporary cutting plane and trimmed surfaces fully removed",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.geometry.sweep",
   "label": "sweep",
   "composite": "model.geometry",
   "signature": "sweep(profile_face, path_curves, *, label=None, cleanup=True, sync=True) -> dict",
   "summary": "Sweep a planar profile face along a curve chain into a solid, optionally cleaning the profile/path orphans.",
   "file": "src/apeGmsh/core/_model_geometry.py:531",
   "flow": [
    {
     "node": "model.geometry",
     "action": "snapshot existing vols/surfs; compute profile COM + path endpoints",
     "passes": "profile_face,path_curves:int tags",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "occ.addWire(path) + occ.addPipe([(2,profile)],wire) + synchronize()",
     "passes": "new volume/surface entities",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if cleanup: occ.remove profile (recursive) + path curves; synchronize()",
     "passes": "surviving entities",
     "to": "model.geometry"
    },
    {
     "node": "model.geometry",
     "action": "identify volume + caps by bbox/projection; if label _register(3,volume,label,'swept_solid')",
     "passes": "(3,volume)+label",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "if label & _auto_pg_from_label: labels.add(3,[volume],name)",
     "passes": "name+(3,tag)",
     "to": "physical"
    }
   ],
   "inputs": "profile_face: planar surface tag; path_curves: ordered curve tag list; label names only the volume; cleanup removes profile+path; sync",
   "outputs": "dict {'volume','start_cap','end_cap'} tags (None if a cap not identified); metadata kind='swept_solid'",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.gui",
   "label": "gui",
   "composite": "model.queries",
   "signature": "gui() -> None",
   "summary": "Open the native Gmsh FLTK GUI window.",
   "file": "src/apeGmsh/core/Model.py:293",
   "flow": [
    {
     "node": "model.queries",
     "action": "call gmsh.fltk.run()",
     "passes": "(no args)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "blocking FLTK window until closed",
     "passes": "(none)",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "None; opens a blocking FLTK window",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.heal_shapes",
   "label": "heal_shapes",
   "composite": "model.io",
   "signature": "heal_shapes(tags=None, *, dim=3, tolerance=1e-8, fix_degenerated=True, fix_small_edges=True, fix_small_faces=True, sew_faces=True, make_solids=True, sync=True) -> _IO",
   "summary": "Heal topology issues in imported CAD (degenerate edges, tiny faces, open shells).",
   "file": "src/apeGmsh/core/_model_io.py:340",
   "flow": [
    {
     "node": "model.io",
     "action": "tags->_as_dimtags else [] (heal all); gmsh.model.occ.healShapes(dimTags,tolerance,fix*,sew,makeSolids)",
     "passes": "healed [(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize()",
     "passes": "out [(dim,tag)]",
     "to": "model.io"
    },
    {
     "node": "model.io",
     "action": "_register(d,t,None,'healed') for new entities; return self",
     "passes": "_IO",
     "to": "model.io"
    }
   ],
   "inputs": "tags: TagsLike|None (None=all); dim fallback; tolerance; fix_degenerated/small_edges/small_faces/sew_faces/make_solids flags; sync",
   "outputs": "self (_IO) for chaining; healed geometry; new entities metadata kind='healed'",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.load_dxf",
   "label": "load_dxf",
   "composite": "model.io",
   "signature": "load_dxf(file_path, *, point_tolerance=1e-6, create_physical_groups=True, sync=True) -> dict[str, dict[int, list[int]]]",
   "summary": "Import a DXF via ezdxf, build OCC geometry; AutoCAD layers become physical groups.",
   "file": "src/apeGmsh/core/_model_io.py:432",
   "flow": [
    {
     "node": "model.io",
     "action": "instantiate _DXFImporter(model, point_tolerance); importer.run(path,create_pgs,sync)",
     "passes": "file path + opts",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "ezdxf parse; per-entity occ.addPoint/Line/Circle/BSpline (point dedup); occ.removeAllDuplicates; synchronize()",
     "passes": "surviving entities",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "_rebuild_layers via bbox match; _register(...,'dxf'); if create_physical_groups addPhysicalGroup per layer",
     "passes": "{layer:{dim:[tags]}}",
     "to": "model.io"
    }
   ],
   "inputs": "file_path Path|str; point_tolerance coincident-point merge distance; create_physical_groups makes a PG per DXF layer; sync",
   "outputs": "dict {layer_name:{dim:[tag,...]}}; entities registered kind='dxf'/'dxf_point'; PGs created per layer (Tier-2, user-facing)",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.load_geo",
   "label": "load_geo",
   "composite": "model.io",
   "signature": "load_geo(file_path) -> dict[int, list[int]]",
   "summary": "Import/execute a Gmsh .geo script via gmsh.merge; auto-detects OCC vs geo kernel.",
   "file": "src/apeGmsh/core/_model_io.py:552",
   "flow": [
    {
     "node": "model.io",
     "action": "check exists; scan file head for SetFactory(\"OpenCASCADE\"); gmsh.merge(path)",
     "passes": "file path:str",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "merge+execute script; occ.synchronize() if OCC else geo.synchronize()",
     "passes": "entities",
     "to": "model.io"
    },
    {
     "node": "model.io",
     "action": "enumerate getEntities(0..3); build {dim:[tags]}",
     "passes": "dict[int,list[int]]",
     "to": "model.io"
    }
   ],
   "inputs": "file_path Path|str (must exist; may contain Mesh N; statements that run on merge)",
   "outputs": "dict {dim:[tag,...]} of all entities after merge; kernel auto-detected from file head; does NOT call _register",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.load_iges",
   "label": "load_iges",
   "composite": "model.io",
   "signature": "load_iges(file_path, *, highest_dim_only=True, sync=True) -> dict[int, list[int]]",
   "summary": "Import an IGES file; registers all imported entities and returns them by dimension.",
   "file": "src/apeGmsh/core/_model_io.py:262",
   "flow": [
    {
     "node": "model.io",
     "action": "_import_shapes: gmsh.model.occ.importShapes(path,highestDimOnly)",
     "passes": "raw [(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize()",
     "passes": "raw [(dim,tag)]",
     "to": "model.io"
    },
    {
     "node": "model.io",
     "action": "_register(d,t,None,'iges') per entity; build {dim:[tags]}",
     "passes": "dict[int,list[int]]",
     "to": "labels"
    }
   ],
   "inputs": "file_path Path|str; highest_dim_only keeps only top-dim entities (False = all sub-entities); sync",
   "outputs": "dict {dim:[tag,...]} of imported entities; metadata kind='iges'",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.load_msh",
   "label": "load_msh",
   "composite": "model.io",
   "signature": "load_msh(file_path) -> dict[int, list[int]]",
   "summary": "Import a Gmsh .msh via gmsh.merge, preserving PGs/mesh/partition data.",
   "file": "src/apeGmsh/core/_model_io.py:516",
   "flow": [
    {
     "node": "model.io",
     "action": "check exists; gmsh.merge(path)",
     "passes": "file path:str",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "merge MSH into active model",
     "passes": "entities",
     "to": "model.io"
    },
    {
     "node": "model.io",
     "action": "enumerate getEntities(0..3); build {dim:[tags]}",
     "passes": "dict[int,list[int]]",
     "to": "model.io"
    }
   ],
   "inputs": "file_path Path|str (must exist)",
   "outputs": "dict {dim:[tag,...]} of all entities after merge; note: does NOT call _register (no metadata kind added)",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.load_step",
   "label": "load_step",
   "composite": "model.io",
   "signature": "load_step(file_path, *, highest_dim_only=True, sync=True) -> dict[int, list[int]]",
   "summary": "Import a STEP file; registers all imported entities and returns them by dimension.",
   "file": "src/apeGmsh/core/_model_io.py:303",
   "flow": [
    {
     "node": "model.io",
     "action": "_import_shapes: gmsh.model.occ.importShapes(path,highestDimOnly)",
     "passes": "raw [(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize()",
     "passes": "raw [(dim,tag)]",
     "to": "model.io"
    },
    {
     "node": "model.io",
     "action": "_register(d,t,None,'step') per entity; build {dim:[tags]}",
     "passes": "dict[int,list[int]]",
     "to": "labels"
    }
   ],
   "inputs": "file_path Path|str; highest_dim_only keeps only top-dim entities; sync",
   "outputs": "dict {dim:[tag,...]} of imported entities; metadata kind='step'",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.save_dxf",
   "label": "save_dxf",
   "composite": "model.io",
   "signature": "save_dxf(file_path) -> None",
   "summary": "Export the current model to DXF (.dxf appended automatically).",
   "file": "src/apeGmsh/core/_model_io.py:492",
   "flow": [
    {
     "node": "model.io",
     "action": "Path(file_path).with_suffix('.dxf'); gmsh.write(path)",
     "passes": "file path:str",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "write DXF to disk",
     "passes": "(file written)",
     "to": "model.io"
    }
   ],
   "inputs": "file_path Path|str (extension forced to .dxf)",
   "outputs": "None; writes a DXF file to disk",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.save_iges",
   "label": "save_iges",
   "composite": "model.io",
   "signature": "save_iges(file_path) -> None",
   "summary": "Export the current model to IGES (.iges appended automatically).",
   "file": "src/apeGmsh/core/_model_io.py:408",
   "flow": [
    {
     "node": "model.io",
     "action": "Path(file_path).with_suffix('.iges'); gmsh.write(path)",
     "passes": "file path:str",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "write IGES to disk",
     "passes": "(file written)",
     "to": "model.io"
    }
   ],
   "inputs": "file_path Path|str (extension forced to .iges)",
   "outputs": "None; writes an IGES file to disk",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.save_msh",
   "label": "save_msh",
   "composite": "model.io",
   "signature": "save_msh(file_path) -> None",
   "summary": "Export the model to Gmsh native MSH preserving geometry, mesh, PGs and partitions.",
   "file": "src/apeGmsh/core/_model_io.py:502",
   "flow": [
    {
     "node": "model.io",
     "action": "with_suffix('.msh'); gmsh.option.setNumber('Mesh.SaveAll',1); gmsh.write(path)",
     "passes": "file path:str + SaveAll opt",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "write MSH to disk",
     "passes": "(file written)",
     "to": "model.io"
    }
   ],
   "inputs": "file_path Path|str (extension forced to .msh)",
   "outputs": "None; writes a full MSH file (geometry+mesh+PGs+partitions)",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.io.save_step",
   "label": "save_step",
   "composite": "model.io",
   "signature": "save_step(file_path) -> None",
   "summary": "Export the current model to STEP (.step appended automatically).",
   "file": "src/apeGmsh/core/_model_io.py:418",
   "flow": [
    {
     "node": "model.io",
     "action": "Path(file_path).with_suffix('.step'); gmsh.write(path)",
     "passes": "file path:str",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "write STEP to disk",
     "passes": "(file written)",
     "to": "model.io"
    }
   ],
   "inputs": "file_path Path|str (extension forced to .step)",
   "outputs": "None; writes a STEP file to disk",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.launch_picker",
   "label": "launch_picker",
   "composite": "model.queries",
   "signature": "launch_picker(*, show_points=True, show_curves=True, show_surfaces=True, show_volumes=False, verbose=True) -> None",
   "summary": "Open Gmsh's FLTK viewer with entity-tag labels pre-enabled for reading tags off the 3D view.",
   "file": "src/apeGmsh/core/Model.py:297",
   "flow": [
    {
     "node": "model.queries",
     "action": "gmsh.model.occ.synchronize() then set Geometry.*Labels options",
     "passes": "label/visibility option numbers",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "blocking gmsh.fltk.run()",
     "passes": "(none)",
     "to": "model.queries"
    }
   ],
   "inputs": "show_points/curves/surfaces/volumes bools; verbose prints usage hint",
   "outputs": "None; syncs OCC then opens a blocking FLTK window",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.preview",
   "label": "preview",
   "composite": "model.queries",
   "signature": "preview(*, dims:list[int]|None=None, browser:bool=False, return_fig:bool=False) -> Any",
   "summary": "WebGL/plotly BRep preview, inline or in a browser tab, zero Qt dependency.",
   "file": "src/apeGmsh/core/Model.py:258",
   "flow": [
    {
     "node": "model.queries",
     "action": "call viz.NotebookPreview.preview_model(parent, dims, browser, return_fig)",
     "passes": "session + render opts",
     "to": "session"
    },
    {
     "node": "session",
     "action": "build plotly figure from BRep entities",
     "passes": "Figure or display",
     "to": "model.queries"
    }
   ],
   "inputs": "dims: BRep dims to render (default [0,1,2,3]); browser: open new tab; return_fig: return plotly Figure instead of displaying",
   "outputs": "None (displays) or plotly Figure when return_fig=True",
   "reads": [
    "session",
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.adjacencies",
   "label": "adjacencies",
   "composite": "model.queries",
   "signature": "adjacencies(tag, *, dim=3) -> tuple[list[int], list[int]]",
   "summary": "Return (upward, downward) adjacent entity tags for an entity.",
   "file": "src/apeGmsh/core/_model_queries.py:496",
   "flow": [
    {
     "node": "model.queries",
     "action": "_model._resolve_dim(tag,dim) — live-model dim lookup falling back to dim",
     "passes": "d:int, tag",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "gmsh.model.getAdjacencies(d,tag)",
     "passes": "(up,down)",
     "to": "model.queries"
    }
   ],
   "inputs": "tag: bare int (dim resolved from live model, fallback dim); dim fallback",
   "outputs": "(upward tags of dim+1 containing it, downward tags of dim-1 on its boundary)",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.boundary",
   "label": "boundary",
   "composite": "model.queries",
   "signature": "boundary(tags, *, dim=3, oriented=False, combined=True, recursive=False) -> list[tuple[int,int]]",
   "summary": "Return boundary entities of the given entities as (dim,tag) pairs.",
   "file": "src/apeGmsh/core/_model_queries.py:367",
   "flow": [
    {
     "node": "model.queries",
     "action": "if str / list-of-str: _resolve_string_to_dimtags per ref else _as_dimtags(tags,dim)",
     "passes": "dt:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "tier-resolve string refs (label->PG->part)",
     "passes": "dt:[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "gmsh.model.getBoundary(dt,combined,oriented,recursive)",
     "passes": "list[(dim,tag)]",
     "to": "model.queries"
    }
   ],
   "inputs": "tags: int|label|PG|(dim,tag)|list; dim default for bare ints/strings; oriented adds signs; combined boundary of union; recursive down to dim 0",
   "outputs": "list[(dim,tag)] boundary entities",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.boundary_curves",
   "label": "boundary_curves",
   "composite": "model.queries",
   "signature": "boundary_curves(tag) -> list[tuple[int,int]]",
   "summary": "Return all unique curves on an entity's boundary (correct edge walk for volumes).",
   "file": "src/apeGmsh/core/_model_queries.py:435",
   "flow": [
    {
     "node": "model.queries",
     "action": "_resolve_to_dimtags(tag) (string->_resolve_string_to_dimtags else _as_dimtags)",
     "passes": "owners:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "tier-resolve string ref",
     "passes": "owners",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "curves->dedupe; surfaces->boundary(combined=False); volumes->faces then per-face boundary",
     "passes": "list[(1,tag)]",
     "to": "model.queries"
    }
   ],
   "inputs": "tag: int|label|PG|(dim,tag)|list",
   "outputs": "list[(1,tag)] deduplicated boundary curves",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.boundary_points",
   "label": "boundary_points",
   "composite": "model.queries",
   "signature": "boundary_points(tag) -> list[tuple[int,int]]",
   "summary": "Return all unique points on an entity's boundary.",
   "file": "src/apeGmsh/core/_model_queries.py:476",
   "flow": [
    {
     "node": "model.queries",
     "action": "_resolve_to_dimtags(tag) tier-resolve",
     "passes": "owners:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "tier-resolve string ref",
     "passes": "owners",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "boundary(owners,recursive=True) filtered to dim==0, deduped",
     "passes": "list[(0,tag)]",
     "to": "model.queries"
    }
   ],
   "inputs": "tag: int|label|PG|(dim,tag)|list",
   "outputs": "list[(0,tag)] deduplicated boundary points",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.bounding_box",
   "label": "bounding_box",
   "composite": "model.queries",
   "signature": "bounding_box(tag, *, dim=3) -> tuple[float,float,float,float,float,float]",
   "summary": "Return an entity's axis-aligned bounding box (xmin..zmax).",
   "file": "src/apeGmsh/core/_model_queries.py:281",
   "flow": [
    {
     "node": "model.queries",
     "action": "if bare int: gmsh.model.getBoundingBox(dim,tag) directly",
     "passes": "(dim,tag)",
     "to": "gmsh"
    },
    {
     "node": "model.queries",
     "action": "else resolve_to_single_dimtag(tag,default_dim=dim) tier walk",
     "passes": "(d,t)",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "tier-resolve to one entity; gmsh.model.getBoundingBox(d,t)",
     "passes": "6-tuple float",
     "to": "model.queries"
    }
   ],
   "inputs": "tag: int|label|PG|(dim,tag) (must resolve to one entity); dim is an explicit hint for bare ints (no live lookup)",
   "outputs": "(xmin,ymin,zmin,xmax,ymax,zmax) floats",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.center_of_mass",
   "label": "center_of_mass",
   "composite": "model.queries",
   "signature": "center_of_mass(tag, *, dim=3) -> tuple[float,float,float]",
   "summary": "Return an entity's centre of mass.",
   "file": "src/apeGmsh/core/_model_queries.py:314",
   "flow": [
    {
     "node": "model.queries",
     "action": "if bare int: gmsh.model.occ.getCenterOfMass(dim,tag) directly",
     "passes": "(dim,tag)",
     "to": "gmsh"
    },
    {
     "node": "model.queries",
     "action": "else resolve_to_single_dimtag(tag,default_dim=dim) tier walk",
     "passes": "(d,t)",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "tier-resolve to one entity; occ.getCenterOfMass(d,t)",
     "passes": "3-tuple float",
     "to": "model.queries"
    }
   ],
   "inputs": "tag: int|label|PG|(dim,tag) (must resolve to one); dim hint for bare ints",
   "outputs": "(cx,cy,cz) floats",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.entities_in_bounding_box",
   "label": "entities_in_bounding_box",
   "composite": "model.queries",
   "signature": "entities_in_bounding_box(xmin, ymin, zmin, xmax, ymax, zmax, *, dim=-1) -> list[tuple[int,int]]",
   "summary": "Return all entities whose bounding box lies inside a query box.",
   "file": "src/apeGmsh/core/_model_queries.py:525",
   "flow": [
    {
     "node": "model.queries",
     "action": "gmsh.model.getEntitiesInBoundingBox(xmin..zmax,dim)",
     "passes": "box bounds + dim",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "BRep containment query",
     "passes": "list[(dim,tag)]",
     "to": "model.queries"
    }
   ],
   "inputs": "xmin,ymin,zmin,xmax,ymax,zmax box limits; dim restrict (-1=all dims)",
   "outputs": "list[(dim,tag)] entities inside the box (Gmsh bbox-containment)",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.fragment_all",
   "label": "fragment_all",
   "composite": "model.queries",
   "signature": "fragment_all(*, dims=None, tolerance=None, sync=True) -> _Queries",
   "summary": "Alias of make_conformal — fragment all entities into a conformal topology.",
   "file": "src/apeGmsh/core/_model_queries.py:275",
   "flow": [
    {
     "node": "model.queries",
     "action": "fragment_all is make_conformal (class-level alias); identical call path",
     "passes": "dims,tolerance,sync",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "see make_conformal flow",
     "passes": "reconciled state",
     "to": "parts"
    }
   ],
   "inputs": "identical to make_conformal (dims/tolerance/sync)",
   "outputs": "identical to make_conformal; provided so model.fragment_all() and model.make_conformal() both work",
   "reads": [
    "gmsh",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.line",
   "label": "line",
   "composite": "model.queries",
   "signature": "line(p1, p2) -> Line",
   "summary": "Factory for a Line primitive through two points for select(on=/crossing=).",
   "file": "src/apeGmsh/core/_model_queries.py:603",
   "flow": [
    {
     "node": "model.queries",
     "action": "Line.through(p1,p2)",
     "passes": "p1,p2: xyz",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "construct Line dataclass (in-plane normal, anchor)",
     "passes": "Line",
     "to": "model.queries"
    }
   ],
   "inputs": "p1,p2: two 3-point coordinates",
   "outputs": "a Line for use with queries.select(on=/crossing=) on 2-D geometry",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.make_conformal",
   "label": "make_conformal",
   "composite": "model.queries",
   "signature": "make_conformal(*, dims=None, tolerance=None, sync=True) -> _Queries",
   "summary": "Fragment all entities against each other to produce a connected conformal topology.",
   "file": "src/apeGmsh/core/_model_queries.py:152",
   "flow": [
    {
     "node": "model.queries",
     "action": "dims default = non-empty dims; collect all dimtags at those dims",
     "passes": "all_dimtags:[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "pg_preserved()+_temporary_tolerance: occ.fragment(all,[],remove True); if sync synchronize(); pg.set_result",
     "passes": "_,result_map",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "rebuild metadata from scratch (kind preserved else 'fragment'); remap parts._instances.entities via dt_remap",
     "passes": "reconciled state",
     "to": "parts"
    }
   ],
   "inputs": "dims: list[int]|None (None=all non-empty dims, e.g. [1] wireframe); tolerance temporary ToleranceBoolean override; sync",
   "outputs": "self (_Queries); single connected topology; metadata rebuilt; PGs preserved+remapped; part instances remapped. Alias: fragment_all",
   "reads": [
    "gmsh",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.mass",
   "label": "mass",
   "composite": "model.queries",
   "signature": "mass(tag, *, dim=3) -> float",
   "summary": "Return an entity's mass (volume 3D / area 2D / length 1D).",
   "file": "src/apeGmsh/core/_model_queries.py:340",
   "flow": [
    {
     "node": "model.queries",
     "action": "if bare int: gmsh.model.occ.getMass(dim,tag) directly",
     "passes": "(dim,tag)",
     "to": "gmsh"
    },
    {
     "node": "model.queries",
     "action": "else resolve_to_single_dimtag(tag,default_dim=dim) tier walk",
     "passes": "(d,t)",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "tier-resolve to one entity; occ.getMass(d,t)",
     "passes": "float",
     "to": "model.queries"
    }
   ],
   "inputs": "tag: int|label|PG|(dim,tag) (must resolve to one); dim hint for bare ints",
   "outputs": "float mass (volume/area/length by dim)",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.plane",
   "label": "plane",
   "composite": "model.queries",
   "signature": "plane(*args, **kwargs) -> Plane",
   "summary": "Factory for a Plane primitive (axis kwarg, 3 points, or normal/through) for select(on=/crossing=).",
   "file": "src/apeGmsh/core/_model_queries.py:561",
   "flow": [
    {
     "node": "model.queries",
     "action": "3 positional -> Plane.through(p1,p2,p3); normal&through kwargs -> Plane(normal,anchor); else Plane.at(axis=val)",
     "passes": "args/kwargs",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "construct Plane dataclass (normal, anchor)",
     "passes": "Plane",
     "to": "model.queries"
    }
   ],
   "inputs": "plane(z=0)/plane(x=5) axis-aligned, or plane(p1,p2,p3) three points, or plane(normal=,through=)",
   "outputs": "a Plane (normal+anchor) for use with queries.select(on=/crossing=)",
   "reads": [],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.registry",
   "label": "registry",
   "composite": "model.queries",
   "signature": "registry() -> pandas.DataFrame",
   "summary": "Return a DataFrame of all entities created through the model, indexed by (dim,tag).",
   "file": "src/apeGmsh/core/_model_queries.py:800",
   "flow": [
    {
     "node": "model.queries",
     "action": "read _model._metadata; build label reverse-map from g.labels.reverse_map()",
     "passes": "metadata + label_map",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "labels_comp.reverse_map() -> {(dim,tag):name}",
     "passes": "label_map",
     "to": "model.queries"
    },
    {
     "node": "model.queries",
     "action": "assemble rows {dim,tag,kind,label}; DataFrame.set_index(['dim','tag'])",
     "passes": "DataFrame",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "pandas DataFrame indexed by (dim,tag) with columns kind, label (label sourced from g.labels)",
   "reads": [
    "labels"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.remove",
   "label": "remove",
   "composite": "model.queries",
   "signature": "remove(tags, *, dim=3, recursive=False, sync=True) -> None",
   "summary": "Delete entities from the model and prune their metadata and label PGs.",
   "file": "src/apeGmsh/core/_model_queries.py:49",
   "flow": [
    {
     "node": "model.queries",
     "action": "_as_dimtags(tags,dim); gmsh.model.occ.remove(dim_tags,recursive)",
     "passes": "[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize()",
     "passes": "removed dimtags",
     "to": "model.queries"
    },
    {
     "node": "model.queries",
     "action": "metadata pop each; cleanup_label_pgs(dim_tags)",
     "passes": "(dim,tag) set",
     "to": "labels"
    }
   ],
   "inputs": "tags: TagsLike; dim fallback for bare ints; recursive also deletes exclusively-owned lower-dim entities; sync",
   "outputs": "None; entities removed, metadata + label PGs cleaned",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.remove_duplicates",
   "label": "remove_duplicates",
   "composite": "model.queries",
   "signature": "remove_duplicates(*, tolerance=None, sync=True) -> _Queries",
   "summary": "Merge all coincident OCC entities and reconcile metadata + label PGs.",
   "file": "src/apeGmsh/core/_model_queries.py:75",
   "flow": [
    {
     "node": "model.queries",
     "action": "_temporary_tolerance(tolerance): gmsh.model.occ.removeAllDuplicates()",
     "passes": "(merged)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync occ.synchronize()",
     "passes": "surviving entities",
     "to": "model.queries"
    },
    {
     "node": "model.queries",
     "action": "drop stale metadata not in surviving; reconcile_label_pgs() drops dead label-PG tags",
     "passes": "reconciled state",
     "to": "labels"
    }
   ],
   "inputs": "tolerance: optional temporary Geometry.Tolerance/ToleranceBoolean override; sync",
   "outputs": "self (_Queries) for chaining; coincident entities merged; metadata + label PGs reconciled",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.select",
   "label": "select",
   "composite": "model.queries",
   "signature": "select(tags, *, dim=None, on=None, crossing=None, not_on=None, not_crossing=None, tol=1e-6) -> Selection",
   "summary": "Filter entities by a geometric predicate (on/crossing/negations) into a chainable Selection.",
   "file": "src/apeGmsh/core/_model_queries.py:669",
   "flow": [
    {
     "node": "model.queries",
     "action": "normalise tags: Selection->list; str/list-of-str -> _string_ref_to_dimtags (tier resolve + dim-walk); else _as_dimtags",
     "passes": "dimtags:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "_resolve_string_to_dimtags then boundary walk to requested dim",
     "passes": "dimtags",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "name-ref + no predicate -> Selection(dimtags); else _select_impl per-entity bbox signed-distance test",
     "passes": "Selection",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getBoundingBox per dimtag for the on/crossing test",
     "passes": "Selection",
     "to": "model.queries"
    }
   ],
   "inputs": "tags: dimtag list|Selection|label/PG/part string|bare int (+dim)|mixed list; on/crossing/not_on/not_crossing primitive ({'z':0}|2pts line|3pts plane|Plane|Line); tol",
   "outputs": "a Selection (list subclass of (dim,tag)); name-ref with no predicate = resolve-only chainable Selection",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.select_all",
   "label": "select_all",
   "composite": "model.queries",
   "signature": "select_all() -> Selection",
   "summary": "Every entity in the model (all dims) as a chainable Selection.",
   "file": "src/apeGmsh/core/_model_queries.py:624",
   "flow": [
    {
     "node": "model.queries",
     "action": "gmsh.model.getEntities() (all dims)",
     "passes": "[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return all dimtags",
     "passes": "[(dim,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(dimtags, _queries=self)",
     "passes": "Selection",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "Selection of every (dim,tag) in the model, bound to the queries engine",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.select_all_curves",
   "label": "select_all_curves",
   "composite": "model.queries",
   "signature": "select_all_curves() -> Selection",
   "summary": "Every curve (dim=1) as a chainable Selection.",
   "file": "src/apeGmsh/core/_model_queries.py:632",
   "flow": [
    {
     "node": "model.queries",
     "action": "_select_all(1): gmsh.model.getEntities(1)",
     "passes": "[(1,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim-1 dimtags",
     "passes": "[(1,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(dimtags, _queries=self)",
     "passes": "Selection",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "Selection of all curves, bound to the queries engine",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.select_all_points",
   "label": "select_all_points",
   "composite": "model.queries",
   "signature": "select_all_points() -> Selection",
   "summary": "Every point (dim=0) as a chainable Selection.",
   "file": "src/apeGmsh/core/_model_queries.py:628",
   "flow": [
    {
     "node": "model.queries",
     "action": "_select_all(0): gmsh.model.getEntities(0)",
     "passes": "[(0,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim-0 dimtags",
     "passes": "[(0,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(dimtags, _queries=self)",
     "passes": "Selection",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "Selection of all points, bound to the queries engine",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.select_all_surfaces",
   "label": "select_all_surfaces",
   "composite": "model.queries",
   "signature": "select_all_surfaces() -> Selection",
   "summary": "Every surface (dim=2) as a chainable Selection.",
   "file": "src/apeGmsh/core/_model_queries.py:636",
   "flow": [
    {
     "node": "model.queries",
     "action": "_select_all(2): gmsh.model.getEntities(2)",
     "passes": "[(2,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim-2 dimtags",
     "passes": "[(2,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(dimtags, _queries=self)",
     "passes": "Selection",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "Selection of all surfaces, bound to the queries engine",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.queries.select_all_volumes",
   "label": "select_all_volumes",
   "composite": "model.queries",
   "signature": "select_all_volumes() -> Selection",
   "summary": "Every volume (dim=3) as a chainable Selection.",
   "file": "src/apeGmsh/core/_model_queries.py:640",
   "flow": [
    {
     "node": "model.queries",
     "action": "_select_all(3): gmsh.model.getEntities(3)",
     "passes": "[(3,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim-3 dimtags",
     "passes": "[(3,tag)]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "Selection(dimtags, _queries=self)",
     "passes": "Selection",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "Selection of all volumes, bound to the queries engine",
   "reads": [
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.select",
   "label": "select",
   "composite": "model.queries",
   "signature": "select(target=None, *, dim:int|None=None) -> GeometryChain",
   "summary": "Start a fluent CAD-entity selection chain seeded by a tiered-resolved target.",
   "file": "src/apeGmsh/core/Model.py:153",
   "flow": [
    {
     "node": "model.queries",
     "action": "reject target=None & dim=None; pick default_dim=3 or dim",
     "passes": "target, default_dim:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "resolve_to_dimtags() tier walk: label(T1)->physical(T2)->parts(T3); int dim via resolve_dim; (dim,tag)/None passthrough",
     "passes": "resolved [(dim,tag),...]",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "construct GeometryChain(dimtags, _engine=self.queries)",
     "passes": "GeometryChain (entity family)",
     "to": "session"
    }
   ],
   "inputs": "target: label/PG/part name | int | (dim,tag) | list thereof | None; dim: default dim for bare ints and target=None",
   "outputs": "a GeometryChain whose verbs compose and whose .result() yields a legacy Selection",
   "reads": [
    "labels",
    "physical",
    "parts",
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.sync",
   "label": "sync",
   "composite": "model.queries",
   "signature": "sync() -> Model",
   "summary": "Flush the OCC kernel so batched sync=False geometry edits become visible to Gmsh.",
   "file": "src/apeGmsh/core/Model.py:138",
   "flow": [
    {
     "node": "model.queries",
     "action": "call gmsh.model.occ.synchronize()",
     "passes": "(no args)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "OCC kernel commits pending topology",
     "passes": "synced model state",
     "to": "model.queries"
    }
   ],
   "inputs": "nothing",
   "outputs": "returns self (Model) for chaining; commits all pending OCC operations",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.copy",
   "label": "copy",
   "composite": "model.transforms",
   "signature": "copy(tags, *, dim=3, sync=True) -> list[int]",
   "summary": "Duplicate entities; returns the new copy tags.",
   "file": "src/apeGmsh/core/_model_transforms.py:153",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt -> resolve_to_dimtags(tags,default_dim) tier walk",
     "passes": "[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): new=occ.copy(dimtags); if sync synchronize()",
     "passes": "new_dimtags",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "extract tags; _log; return new_tags",
     "passes": "list[int]",
     "to": "model.transforms"
    }
   ],
   "inputs": "tags: EntityRefs; dim fallback for bare ints",
   "outputs": "list[int] tags of the new copies (NOT registered in metadata); originals untouched",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.extrude",
   "label": "extrude",
   "composite": "model.transforms",
   "signature": "extrude(tags, dx, dy, dz, *, dim=2, num_elements=None, heights=None, recombine=False, sync=True) -> list[tuple[int,int]]",
   "summary": "Linear extrusion sweeping entities one dimension up along (dx,dy,dz).",
   "file": "src/apeGmsh/core/_model_transforms.py:181",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt -> resolve_to_dimtags(tags,default_dim) tier walk",
     "passes": "dt:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): occ.extrude(dt,dx,dy,dz,numElements,heights,recombine); if sync synchronize()",
     "passes": "result:[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_register(d,t,None,'extrude') per result; _log",
     "passes": "list[(dim,tag)]",
     "to": "model.transforms"
    }
   ],
   "inputs": "tags: EntityRefs to extrude; dx,dy,dz vector; dim default for bare ints; num_elements structured layer counts; heights relative (sum 1.0); recombine for hex/quad",
   "outputs": "list[(dim,tag)] all generated entities (top face, volume, lateral faces); metadata kind='extrude'",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.mirror",
   "label": "mirror",
   "composite": "model.transforms",
   "signature": "mirror(tags, a, b, c, d, *, dim=3, sync=True) -> _Transforms",
   "summary": "Mirror entities through the plane ax+by+cz+d=0; returns self.",
   "file": "src/apeGmsh/core/_model_transforms.py:129",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt -> resolve_to_dimtags(tags,default_dim) tier walk",
     "passes": "[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): occ.mirror(dimtags,a,b,c,d); if sync synchronize()",
     "passes": "mirrored model",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_log; return self",
     "passes": "_Transforms",
     "to": "model.transforms"
    }
   ],
   "inputs": "tags: EntityRefs; a,b,c,d mirror-plane coefficients; dim fallback",
   "outputs": "self (_Transforms); entities reflected in place; PG identity preserved",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.revolve",
   "label": "revolve",
   "composite": "model.transforms",
   "signature": "revolve(tags, angle, *, x=0.0, y=0.0, z=0.0, ax=0.0, ay=0.0, az=1.0, dim=2, num_elements=None, heights=None, recombine=False, sync=True) -> list[tuple[int,int]]",
   "summary": "Revolution sweeping entities around an axis by angle radians.",
   "file": "src/apeGmsh/core/_model_transforms.py:245",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt -> resolve_to_dimtags(tags,default_dim) tier walk",
     "passes": "dt:[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): occ.revolve(dt,x,y,z,ax,ay,az,angle,numElements,heights,recombine); if sync synchronize()",
     "passes": "result:[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_register(d,t,None,'revolve') per result; _log",
     "passes": "list[(dim,tag)]",
     "to": "model.transforms"
    }
   ],
   "inputs": "tags: EntityRefs; angle radians (2π=full); (x,y,z) point on axis; (ax,ay,az) axis direction; dim default; num_elements/heights/recombine as extrude",
   "outputs": "list[(dim,tag)] all generated entities; metadata kind='revolve'",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.rotate",
   "label": "rotate",
   "composite": "model.transforms",
   "signature": "rotate(tags, angle, *, ax=0.0, ay=0.0, az=1.0, cx=0.0, cy=0.0, cz=0.0, dim=3, sync=True) -> _Transforms",
   "summary": "Rotate entities by angle radians about an axis through a centre; returns self.",
   "file": "src/apeGmsh/core/_model_transforms.py:65",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt -> resolve_to_dimtags(tags,default_dim) tier walk",
     "passes": "[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): occ.rotate(dimtags,cx,cy,cz,ax,ay,az,angle); if sync synchronize()",
     "passes": "rotated model",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_log; return self",
     "passes": "_Transforms",
     "to": "model.transforms"
    }
   ],
   "inputs": "tags: EntityRefs; angle radians; (ax,ay,az) axis direction; (cx,cy,cz) point on axis; dim fallback",
   "outputs": "self (_Transforms); entities rotated in place; PG identity preserved",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.scale",
   "label": "scale",
   "composite": "model.transforms",
   "signature": "scale(tags, sx, sy, sz, *, cx=0.0, cy=0.0, cz=0.0, dim=3, sync=True) -> _Transforms",
   "summary": "Scale (dilate) entities by (sx,sy,sz) from a centre; returns self.",
   "file": "src/apeGmsh/core/_model_transforms.py:100",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt -> resolve_to_dimtags(tags,default_dim) tier walk",
     "passes": "[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): occ.dilate(dimtags,cx,cy,cz,sx,sy,sz); if sync synchronize()",
     "passes": "scaled model",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_log; return self",
     "passes": "_Transforms",
     "to": "model.transforms"
    }
   ],
   "inputs": "tags: EntityRefs; sx,sy,sz scale factors; (cx,cy,cz) dilation centre; dim fallback",
   "outputs": "self (_Transforms); entities scaled in place; PG identity preserved",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.sweep",
   "label": "sweep",
   "composite": "model.transforms",
   "signature": "sweep(profiles, path, *, dim=2, trihedron='DiscreteTrihedron', label=None, sync=True) -> list[tuple[int,int]]",
   "summary": "Constant-section sweep of profile(s) along an arbitrary OCC wire path.",
   "file": "src/apeGmsh/core/_model_transforms.py:308",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt(profiles,dim) -> resolve_to_dimtags tier walk",
     "passes": "dt:[(dim,tag)], path:int",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): occ.addPipe(dt,path,trihedron); if sync synchronize()",
     "passes": "result:[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if label: _register only max-dim survivor with label else None; _log",
     "passes": "list[(dim,tag)]",
     "to": "labels"
    }
   ],
   "inputs": "profiles: EntityRefs to sweep; path: OCC wire tag (from add_wire) or curve_loop; dim default; trihedron frame-transport mode; label names only highest-dim survivor",
   "outputs": "list[(dim,tag)] all generated entities; metadata kind='sweep'; optional label on volume",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.thru_sections",
   "label": "thru_sections",
   "composite": "model.transforms",
   "signature": "thru_sections(wires, *, make_solid=True, make_ruled=False, max_degree=-1, continuity='', parametrization='', smoothing=False, label=None, sync=True) -> list[tuple[int,int]]",
   "summary": "Variable-section sweep — loft a volume/shell through an ordered list of wires.",
   "file": "src/apeGmsh/core/_model_transforms.py:394",
   "flow": [
    {
     "node": "model.transforms",
     "action": "require >=2 wires; pg_preserved_identity(): occ.addThruSections(wires,makeSolid,makeRuled,...)",
     "passes": "wire tags:list[int]",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "if sync synchronize()",
     "passes": "result:[(dim,tag)]",
     "to": "model.transforms"
    },
    {
     "node": "model.transforms",
     "action": "if label: _register max-dim survivor with label else None; _log",
     "passes": "list[(dim,tag)]",
     "to": "labels"
    }
   ],
   "inputs": "wires: ordered list of ≥2 wire tags (from add_wire); make_solid caps ends; make_ruled linear faces; max_degree/continuity/parametrization/smoothing OCC tuning; label names highest-dim survivor",
   "outputs": "list[(dim,tag)] all generated entities; metadata kind='thru_sections'; optional label on volume",
   "reads": [
    "gmsh"
   ],
   "writes": [
    "gmsh",
    "labels",
    "physical"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.transforms.translate",
   "label": "translate",
   "composite": "model.transforms",
   "signature": "translate(tags, dx, dy, dz, *, dim=3, sync=True) -> _Transforms",
   "summary": "Translate entities by (dx,dy,dz); returns self for chaining.",
   "file": "src/apeGmsh/core/_model_transforms.py:37",
   "flow": [
    {
     "node": "model.transforms",
     "action": "_resolve_dt -> resolve_to_dimtags(tags,default_dim) tier walk",
     "passes": "[(dim,tag)]",
     "to": "labels"
    },
    {
     "node": "labels",
     "action": "pg_preserved_identity(): occ.translate(dimtags,dx,dy,dz); if sync synchronize()",
     "passes": "transformed model",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "_log; return self",
     "passes": "_Transforms",
     "to": "model.transforms"
    }
   ],
   "inputs": "tags: EntityRefs (int|label|PG|(dim,tag)|list mix); dx,dy,dz translation; dim fallback for bare ints",
   "outputs": "self (_Transforms) for chaining; entities moved in place; PG identity preserved",
   "reads": [
    "gmsh",
    "labels",
    "physical",
    "parts"
   ],
   "writes": [
    "gmsh"
   ],
   "_cluster": "A_model"
  },
  {
   "id": "g.model.viewer",
   "label": "viewer",
   "composite": "model.queries",
   "signature": "viewer(**kwargs) -> Any",
   "summary": "Open the interactive Qt geometry picker (BRep-only; no loads/constraints).",
   "file": "src/apeGmsh/core/Model.py:242",
   "flow": [
    {
     "node": "model.queries",
     "action": "delegate to self.selection.picker(**kwargs)",
     "passes": "**kwargs",
     "to": "selection"
    },
    {
     "node": "selection",
     "action": "SelectionComposite.picker opens Qt viewer",
     "passes": "viewer handle",
     "to": "session"
    }
   ],
   "inputs": "**kwargs forwarded to selection.picker (dims, point_size, line_width, surface_opacity, ...)",
   "outputs": "the Qt viewer object; opens an interactive window (side effect)",
   "reads": [
    "selection",
    "gmsh"
   ],
   "writes": [],
   "_cluster": "A_model"
  },
  {
   "id": "g.parts.add",
   "label": "add",
   "composite": "parts",
   "signature": "add(part: Part, *, label=None, translate=(0,0,0), rotate=None, highest_dim_only=True) -> Instance",
   "summary": "Import a saved Part's STEP/IGES file into the session, apply placement transforms, and rebind the Part's labels from its JSON sidecar.",
   "file": "src/apeGmsh/core/_parts_registry.py:416",
   "flow": [
    {
     "node": "parts",
     "action": "check part.has_file (auto-persist tempfile or explicit save)",
     "passes": "part.file_path: Path",
     "to": "part"
    },
    {
     "node": "parts",
     "action": "auto-generate label as '{part.name}_{counter}' if None",
     "passes": "label: str",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "delegate to _import_cad with part.file_path + dict(part.properties)",
     "passes": "file_path: Path, properties: dict",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "importShapes(STEP) + synchronize",
     "passes": "file_path str + highestDimOnly: bool",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "rotate-then-translate top-dim handles",
     "passes": "transform_dimtags: list[DimTag], translate, rotate",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "read_sidecar + rebind_physical_groups by transformed COM",
     "passes": "anchors: list[dict], translate, rotate",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "recreate matched anchors as prefixed label PGs '{label}.{pg}'",
     "passes": "pg_matches: dict[str,list[(dim,tag)]]",
     "to": "labels"
    },
    {
     "node": "parts",
     "action": "create umbrella label == instance label over top-dim",
     "passes": "top_dim entities, name=label",
     "to": "labels"
    },
    {
     "node": "parts",
     "action": "register Instance with label_names + bbox",
     "passes": "Instance(label, file_path, entities, label_names)",
     "to": "parts"
    }
   ],
   "inputs": "part (Part with has_file True); optional label, translate, rotate, highest_dim_only",
   "outputs": "Instance with .labels/.edit, label PGs created in the model",
   "reads": [
    "part.has_file / part.file_path / part.name / part.properties / part._auto_persist",
    "{file}.apegmsh.json sidecar via read_sidecar",
    "gmsh.model.occ.getCenterOfMass / getBoundingBox (rebind)"
   ],
   "writes": [
    "gmsh.model.occ.importShapes / rotate / translate / synchronize",
    "g.labels.add (prefixed + umbrella label PGs)",
    "self._instances[label] = Instance",
    "Instance.edit"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.build_face_map",
   "label": "build_face_map",
   "composite": "parts",
   "signature": "build_face_map(node_map: dict[str, set[int]]) -> dict[str, np.ndarray]",
   "summary": "Partition surface-element connectivity rows into per-instance arrays by whole-face node ownership against a node map.",
   "file": "src/apeGmsh/core/_parts_registry.py:530",
   "flow": [
    {
     "node": "parts",
     "action": "collect all surface element connectivity as rectangular array",
     "passes": "faces: ndarray(M,npe) via gmsh.model.mesh.getElements",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "mask faces whose all nodes belong to each instance",
     "passes": "node_map: dict[str,set[int]]",
     "to": "parts"
    }
   ],
   "inputs": "node_map (output of build_node_map)",
   "outputs": "dict {instance_label: ndarray face connectivity}",
   "reads": [
    "gmsh.model.getEntities(2)",
    "gmsh.model.mesh.getElements / getElementProperties"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.build_node_map",
   "label": "build_node_map",
   "composite": "parts",
   "signature": "build_node_map(node_tags: np.ndarray, node_coords: np.ndarray) -> dict[str, set[int]]",
   "summary": "Partition mesh node tags into per-instance sets by testing each node against the instance's cached bounding box.",
   "file": "src/apeGmsh/core/_parts_registry.py:514",
   "flow": [
    {
     "node": "parts",
     "action": "reshape coords to (-1,3)",
     "passes": "tags: ndarray, coords: ndarray(N,3)",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "for each instance test nodes inside bbox+tol",
     "passes": "inst.bbox: tuple[6 floats]",
     "to": "parts"
    }
   ],
   "inputs": "node_tags ndarray, node_coords ndarray (typically from g.mesh.queries.get_fem_data)",
   "outputs": "dict {instance_label: set[int] node tags}",
   "reads": [
    "self._instances[*].bbox"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.delete",
   "label": "delete",
   "composite": "parts",
   "signature": "delete(label: str) -> None",
   "summary": "Remove an instance from the registry only; its entities stay in the Gmsh session and become untracked.",
   "file": "src/apeGmsh/core/_parts_registry.py:600",
   "flow": [
    {
     "node": "parts",
     "action": "pop label from registry (entities remain in gmsh)",
     "passes": "label: str",
     "to": "parts"
    }
   ],
   "inputs": "label",
   "outputs": "None (instance removed from registry; geometry untouched)",
   "reads": [
    "self._instances"
   ],
   "writes": [
    "self._instances.pop(label)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.fragment_all",
   "label": "fragment_all",
   "composite": "parts.fragmentation",
   "signature": "fragment_all(*, dim=None) -> list[int]",
   "summary": "OCC-fragment every entity at the target dimension so shared interfaces become conformal, remapping each Instance's entity tags and preserving all physical/label groups.",
   "file": "src/apeGmsh/core/_parts_fragmentation.py:92",
   "flow": [
    {
     "node": "parts.fragmentation",
     "action": "auto-detect highest dim with entities if dim=None",
     "passes": "dim: int",
     "to": "gmsh"
    },
    {
     "node": "parts.fragmentation",
     "action": "warn about untracked orphan tags at dim",
     "passes": "orphans: set[int]",
     "to": "parts.fragmentation"
    },
    {
     "node": "parts.fragmentation",
     "action": "snapshot all PGs (label + user) on context enter",
     "passes": "snapshot: list[dict] (pg_preserved)",
     "to": "labels"
    },
    {
     "node": "parts.fragmentation",
     "action": "occ.fragment(obj, tool, removeObject/Tool) + synchronize",
     "passes": "obj=[ent0], tool=[ent1..]: list[DimTag]",
     "to": "model.boolean"
    },
    {
     "node": "parts.fragmentation",
     "action": "remap PGs via result_map on context exit",
     "passes": "input_ents, result_map: list[list[DimTag]]",
     "to": "labels"
    },
    {
     "node": "parts.fragmentation",
     "action": "rewrite Instance.entities per-dim old_tag->[new_tags]",
     "passes": "result_map applied to self._instances",
     "to": "parts"
    }
   ],
   "inputs": "optional dim=int (auto-detects highest present)",
   "outputs": "list[int] surviving entity tags at the target dimension; Instance.entities mutated in-place",
   "reads": [
    "gmsh.model.getEntities(dim)",
    "self._instances[*].entities"
   ],
   "writes": [
    "gmsh.model.occ.fragment / synchronize",
    "PG remap via remap_physical_groups (label + user PGs)",
    "self._instances[*].entities (in place)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.fragment_pair",
   "label": "fragment_pair",
   "composite": "parts.fragmentation",
   "signature": "fragment_pair(label_a, label_b, *, dim=None) -> list[int]",
   "summary": "Fragment only two named instances against each other (not the whole model), remapping their tags and preserving groups.",
   "file": "src/apeGmsh/core/_parts_fragmentation.py:151",
   "flow": [
    {
     "node": "parts.fragmentation",
     "action": "look up inst_a/inst_b, auto-detect common dim",
     "passes": "label_a, label_b: str",
     "to": "parts"
    },
    {
     "node": "parts.fragmentation",
     "action": "snapshot PGs (pg_preserved)",
     "passes": "snapshot: list[dict]",
     "to": "labels"
    },
    {
     "node": "parts.fragmentation",
     "action": "occ.fragment(obj=inst_a ents, tool=inst_b ents) + synchronize",
     "passes": "obj/tool: list[DimTag]",
     "to": "model.boolean"
    },
    {
     "node": "parts.fragmentation",
     "action": "remap PGs + rewrite both Instances' entities",
     "passes": "input_ents, result_map",
     "to": "labels"
    }
   ],
   "inputs": "label_a, label_b (existing instance labels); optional dim=int",
   "outputs": "list[int] surviving tags at target dim; both Instances' entities remapped",
   "reads": [
    "self._instances[label_a/label_b].entities"
   ],
   "writes": [
    "gmsh.model.occ.fragment / synchronize",
    "PG remap",
    "self._instances[*].entities"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.from_model",
   "label": "from_model",
   "composite": "parts",
   "signature": "from_model(label, *, dim=None, tags=None) -> Instance",
   "summary": "Adopt geometry already present in the Gmsh session (e.g. after g.model.io.load_step) as a tracked part; defaults to all untracked entities.",
   "file": "src/apeGmsh/core/_parts_registry.py:332",
   "flow": [
    {
     "node": "parts",
     "action": "collect already-tracked tags across all Instances",
     "passes": "tracked: dict[int,set[int]]",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "scan gmsh entities per dim, adopt by tags= or untracked",
     "passes": "dims: list[int]",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "warn if nothing to adopt",
     "passes": "entities: dict[int,list[int]] (possibly empty)",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "register Instance with bbox",
     "passes": "Instance(label, entities, bbox)",
     "to": "parts"
    }
   ],
   "inputs": "label; optional dim=int; optional tags=list[int]",
   "outputs": "Instance (registered)",
   "reads": [
    "gmsh.model.getEntities (per dim)",
    "gmsh.model.getBoundingBox (via _compute_bbox)"
   ],
   "writes": [
    "self._instances[label] = Instance",
    "Instance.edit"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.fuse_group",
   "label": "fuse_group",
   "composite": "parts.fragmentation",
   "signature": "fuse_group(labels: list[str], *, label=None, dim=None, properties=None) -> Instance",
   "summary": "OCC-fuse multiple instances into a single new Instance (internal interfaces vanish), dropping the old instances from the registry.",
   "file": "src/apeGmsh/core/_parts_fragmentation.py:192",
   "flow": [
    {
     "node": "parts.fragmentation",
     "action": "validate >=2 unique existing labels, pick survivor name",
     "passes": "labels: list[str]",
     "to": "parts"
    },
    {
     "node": "parts.fragmentation",
     "action": "auto-detect highest common dim across all instances",
     "passes": "instances: list[Instance]",
     "to": "parts"
    },
    {
     "node": "parts.fragmentation",
     "action": "snapshot PGs (pg_preserved)",
     "passes": "snapshot: list[dict]",
     "to": "labels"
    },
    {
     "node": "parts.fragmentation",
     "action": "occ.fuse(obj, tool) + synchronize, absorbed_into_result=True",
     "passes": "obj/tool_dt: list[DimTag]",
     "to": "model.boolean"
    },
    {
     "node": "parts.fragmentation",
     "action": "remap PGs with absorbed fallback, delete old instances",
     "passes": "result_map, result",
     "to": "labels"
    },
    {
     "node": "parts.fragmentation",
     "action": "build + store new fused Instance (no .edit wiring)",
     "passes": "Instance(new_label, new_entities, bbox)",
     "to": "parts"
    }
   ],
   "inputs": "labels (>=2 existing); optional label (survivor name), dim, properties",
   "outputs": "Instance (new fused, registered directly into self._instances); old instances deleted",
   "reads": [
    "self._instances[*].entities / properties"
   ],
   "writes": [
    "gmsh.model.occ.fuse / synchronize",
    "PG remap (absorbed_into_result)",
    "del self._instances[old labels]",
    "self._instances[new_label] = Instance"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.get",
   "label": "get",
   "composite": "parts",
   "signature": "get(label: str) -> Instance",
   "summary": "Return the registered Instance for a label (so you can reach inst.edit later); raises KeyError listing available labels on typo.",
   "file": "src/apeGmsh/core/_parts_registry.py:556",
   "flow": [
    {
     "node": "parts",
     "action": "dict lookup, KeyError with available labels if missing",
     "passes": "label: str",
     "to": "parts"
    }
   ],
   "inputs": "label (instance label)",
   "outputs": "Instance",
   "reads": [
    "self._instances"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.import_step",
   "label": "import_step",
   "composite": "parts",
   "signature": "import_step(file_path, *, label=None, translate=(0,0,0), rotate=None, highest_dim_only=True, properties=None) -> Instance",
   "summary": "Import a STEP/IGES file from disk as a named Instance, applying placement transforms and rebinding any sidecar labels.",
   "file": "src/apeGmsh/core/_parts_registry.py:469",
   "flow": [
    {
     "node": "parts",
     "action": "verify file exists, auto-label from file stem",
     "passes": "file_path: Path, label: str",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "delegate to _import_cad",
     "passes": "file_path, part_name=stem, translate, rotate, properties",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "importShapes + synchronize + transform + sidecar rebind + register",
     "passes": "Instance(label, file_path, entities, label_names)",
     "to": "gmsh"
    }
   ],
   "inputs": "file_path (.step/.stp/.iges/.igs); optional label, translate, rotate, highest_dim_only, properties",
   "outputs": "Instance (registered) with rebound label PGs",
   "reads": [
    "filesystem (file exists)",
    "{file}.apegmsh.json sidecar",
    "gmsh.model.occ.getCenterOfMass / getBoundingBox"
   ],
   "writes": [
    "gmsh.model.occ.importShapes / rotate / translate / synchronize",
    "g.labels.add",
    "self._instances[label]",
    "Instance.edit"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.instances",
   "label": "instances",
   "composite": "parts",
   "signature": "instances -> dict[str, Instance]  (property)",
   "summary": "Read-only shallow-copied view of all registered instances keyed by label.",
   "file": "src/apeGmsh/core/_parts_registry.py:160",
   "flow": [
    {
     "node": "parts",
     "action": "return dict(self._instances) shallow copy",
     "passes": "instances view: dict[str,Instance]",
     "to": "parts"
    }
   ],
   "inputs": "none (property access)",
   "outputs": "dict {label: Instance} (copy)",
   "reads": [
    "self._instances"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.labels",
   "label": "labels",
   "composite": "parts",
   "signature": "labels() -> list[str]",
   "summary": "Return all registered instance labels in insertion order (note: this is parts.labels(), distinct from the g.labels composite).",
   "file": "src/apeGmsh/core/_parts_registry.py:580",
   "flow": [
    {
     "node": "parts",
     "action": "return list(self._instances.keys())",
     "passes": "instance labels: list[str]",
     "to": "parts"
    }
   ],
   "inputs": "none",
   "outputs": "list[str] instance labels",
   "reads": [
    "self._instances"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.part",
   "label": "part",
   "composite": "parts",
   "signature": "part(label: str) -> contextmanager[str]",
   "summary": "Context manager that tracks all Gmsh entities created inside the block and registers them as a named Instance via entity-set diffing.",
   "file": "src/apeGmsh/core/_parts_registry.py:187",
   "flow": [
    {
     "node": "parts",
     "action": "snapshot entity tags per dim before block",
     "passes": "before: dict[int,set[int]] from gmsh.model.getEntities(d)",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "yield label, user builds geometry in block",
     "passes": "label: str",
     "to": "model.geometry"
    },
    {
     "node": "parts",
     "action": "diff after-vs-before to find new tags",
     "passes": "entities: dict[int,list[int]]",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "compute AABB then register Instance with edit wired",
     "passes": "Instance(label, part_name, entities, bbox)",
     "to": "parts"
    }
   ],
   "inputs": "label (unique part name string)",
   "outputs": "yields the label string; Instance stored in self._instances on block exit",
   "reads": [
    "gmsh.model.getEntities (dims 0-3, before and after)",
    "gmsh.model.getBoundingBox (via _compute_bbox)"
   ],
   "writes": [
    "self._instances[label] = Instance",
    "Instance.edit (InstanceEdit attached)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.register",
   "label": "register",
   "composite": "parts",
   "signature": "register(name, dimtags=None, *, label=None, pg=None, dim=None) -> Instance",
   "summary": "Manually tag existing entities (by raw dimtags, by a g.labels label, or by a g.physical group) under a new part name with an ownership-conflict check.",
   "file": "src/apeGmsh/core/_parts_registry.py:224",
   "flow": [
    {
     "node": "parts",
     "action": "validate exactly one of dimtags/label/pg given",
     "passes": "provided count: int",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "resolve label= via labels.entities(name, dim=)",
     "passes": "label name: str, dim: int|None",
     "to": "labels"
    },
    {
     "node": "parts",
     "action": "resolve pg= via physical.get_tag/get_entities per dim",
     "passes": "pg name: str",
     "to": "physical"
    },
    {
     "node": "parts",
     "action": "check each (dim,tag) not owned by another Instance",
     "passes": "resolved: list[DimTag]",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "register Instance with computed bbox",
     "passes": "Instance(label=name, entities, bbox)",
     "to": "parts"
    }
   ],
   "inputs": "name; one of dimtags=list[(dim,tag)] / label=str / pg=str; optional dim=int",
   "outputs": "Instance (registered in self._instances)",
   "reads": [
    "g.labels.entities(name, dim=)",
    "g.physical.get_tag / get_entities",
    "gmsh.model.getBoundingBox (via _compute_bbox)"
   ],
   "writes": [
    "self._instances[name] = Instance",
    "Instance.edit"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.parts.rename",
   "label": "rename",
   "composite": "parts",
   "signature": "rename(old_label: str, new_label: str) -> None",
   "summary": "Rename a registered instance in the registry (KeyError if missing, ValueError if new label taken); does not touch geometry.",
   "file": "src/apeGmsh/core/_parts_registry.py:584",
   "flow": [
    {
     "node": "parts",
     "action": "pop old, set inst.label, reinsert under new",
     "passes": "old_label, new_label: str",
     "to": "parts"
    }
   ],
   "inputs": "old_label, new_label",
   "outputs": "None (registry key + Instance.label changed)",
   "reads": [
    "self._instances"
   ],
   "writes": [
    "self._instances (key remap)",
    "Instance.label"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.physical.add",
   "label": "add",
   "composite": "physical",
   "signature": "add(dim, tags, *, name='', tag=-1) -> Tag",
   "summary": "Create or upsert a physical group, resolving tag refs via labels then physical, enforcing one-dim-per-name.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:66",
   "flow": [
    {
     "node": "physical",
     "action": "resolve refs to entity tags via resolve_to_tags (labels then PG)",
     "passes": "resolve_to_tags(tags, dim, session) -> list[int]",
     "to": "labels"
    },
    {
     "node": "physical",
     "action": "if name reused at another dim raise; if name exists at dim merge entities (remove+re-add PG)",
     "passes": "get_tag(d,name); gmsh.model.getEntitiesForPhysicalGroup; removePhysicalGroups",
     "to": "gmsh"
    },
    {
     "node": "physical",
     "action": "create the physical group and set its name",
     "passes": "gmsh.model.addPhysicalGroup(dim:int, resolved:list[int], tag:int); gmsh.model.setPhysicalName(dim, pg_tag, name:str)",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tags (int|str|dimtag|list), name, tag",
   "outputs": "Tag (physical-group tag)",
   "reads": [
    "g.labels + g.physical registries via resolve_to_tags",
    "gmsh physical groups"
   ],
   "writes": [
    "gmsh physical group + name"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.add_curve",
   "label": "add_curve",
   "composite": "physical",
   "signature": "add_curve(tags, *, name='', tag=-1) -> PhysicalGroups",
   "summary": "Shorthand for add(dim=1, ...) returning self for chaining.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:163",
   "flow": [
    {
     "node": "physical",
     "action": "delegate to add(1, tags, name, tag)",
     "passes": "add(1, tags, name, tag)",
     "to": "physical"
    }
   ],
   "inputs": "tags, name, tag",
   "outputs": "self (PhysicalGroups)",
   "reads": [
    "g.labels/g.physical registries (via add)"
   ],
   "writes": [
    "gmsh physical group (dim 1)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.add_point",
   "label": "add_point",
   "composite": "physical",
   "signature": "add_point(tags, *, name='', tag=-1) -> PhysicalGroups",
   "summary": "Shorthand for add(dim=0, ...) returning self for chaining.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:158",
   "flow": [
    {
     "node": "physical",
     "action": "delegate to add(0, tags, name, tag)",
     "passes": "add(0, tags, name, tag)",
     "to": "physical"
    }
   ],
   "inputs": "tags, name, tag",
   "outputs": "self (PhysicalGroups)",
   "reads": [
    "g.labels/g.physical registries (via add)"
   ],
   "writes": [
    "gmsh physical group (dim 0)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.add_surface",
   "label": "add_surface",
   "composite": "physical",
   "signature": "add_surface(tags, *, name='', tag=-1) -> PhysicalGroups",
   "summary": "Shorthand for add(dim=2, ...) returning self for chaining.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:168",
   "flow": [
    {
     "node": "physical",
     "action": "delegate to add(2, tags, name, tag)",
     "passes": "add(2, tags, name, tag)",
     "to": "physical"
    }
   ],
   "inputs": "tags, name, tag",
   "outputs": "self (PhysicalGroups)",
   "reads": [
    "g.labels/g.physical registries (via add)"
   ],
   "writes": [
    "gmsh physical group (dim 2)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.add_volume",
   "label": "add_volume",
   "composite": "physical",
   "signature": "add_volume(tags, *, name='', tag=-1) -> PhysicalGroups",
   "summary": "Shorthand for add(dim=3, ...) returning self for chaining.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:173",
   "flow": [
    {
     "node": "physical",
     "action": "delegate to add(3, tags, name, tag)",
     "passes": "add(3, tags, name, tag)",
     "to": "physical"
    }
   ],
   "inputs": "tags, name, tag",
   "outputs": "self (PhysicalGroups)",
   "reads": [
    "g.labels/g.physical registries (via add)"
   ],
   "writes": [
    "gmsh physical group (dim 3)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.entities",
   "label": "entities",
   "composite": "physical",
   "signature": "entities(name_or_tag, *, dim=None) -> list[Tag]",
   "summary": "Resolve a physical group to its entity tags by name (any/given dim) or by raw tag.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:350",
   "flow": [
    {
     "node": "physical",
     "action": "for a name resolve via get_tag (search dims; raise on multi-dim ambiguity) then query entities; for a raw tag require dim",
     "passes": "get_tag(d,name); gmsh.model.getEntitiesForPhysicalGroup(dim, pg_tag) -> list[int]",
     "to": "gmsh"
    }
   ],
   "inputs": "name_or_tag (str|int), dim",
   "outputs": "list[Tag] of model-entity tags",
   "reads": [
    "gmsh physical groups"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.from_label",
   "label": "from_label",
   "composite": "physical",
   "signature": "from_label(label_name, *, name=None, dim=None) -> Tag",
   "summary": "Create or append a PG from a single label's entities, inferring dim from the label's PG if omitted.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:182",
   "flow": [
    {
     "node": "physical",
     "action": "resolve label entities via g.labels.entities and infer dim from the label's _label: PG",
     "passes": "labels.entities(label_name, dim); gmsh.model.getPhysicalGroups/getPhysicalName",
     "to": "labels"
    },
    {
     "node": "physical",
     "action": "delegate to add(dim, ent_tags, name)",
     "passes": "add(dim, ent_tags, name=pg_name)",
     "to": "physical"
    }
   ],
   "inputs": "label_name, name, dim",
   "outputs": "Tag (PG tag)",
   "reads": [
    "g.labels registry",
    "gmsh physical groups"
   ],
   "writes": [
    "gmsh physical group + name"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.from_labels",
   "label": "from_labels",
   "composite": "physical",
   "signature": "from_labels(label_names, *, name, dim=None) -> Tag",
   "summary": "Create or append a PG from the union of multiple labels' entities.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:224",
   "flow": [
    {
     "node": "physical",
     "action": "union each label's entities via g.labels.entities, inferring dim from the first label",
     "passes": "labels.entities(lbl, dim) accumulated; gmsh.model.getPhysicalGroups/getPhysicalName",
     "to": "labels"
    },
    {
     "node": "physical",
     "action": "delegate to add(dim, union_tags, name)",
     "passes": "add(inferred_dim, sorted(set(all_tags)), name)",
     "to": "physical"
    }
   ],
   "inputs": "label_names list[str], name, dim",
   "outputs": "Tag (PG tag)",
   "reads": [
    "g.labels registry",
    "gmsh physical groups"
   ],
   "writes": [
    "gmsh physical group + name"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.get_all",
   "label": "get_all",
   "composite": "physical",
   "signature": "get_all(dim=-1) -> list[DimTag]",
   "summary": "Return all user-facing physical groups as (dim,tag), filtering internal _label: PGs.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:323",
   "flow": [
    {
     "node": "physical",
     "action": "list gmsh physical groups and drop label PGs via is_label_pg",
     "passes": "gmsh.model.getPhysicalGroups(dim) -> filtered list[(dim,tag)]",
     "to": "gmsh"
    }
   ],
   "inputs": "dim (-1=all)",
   "outputs": "list[(dim,tag)]",
   "reads": [
    "gmsh physical groups"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.get_entities",
   "label": "get_entities",
   "composite": "physical",
   "signature": "get_entities(dim, tag) -> list[Tag]",
   "summary": "Return the model-entity tags contained in a physical group.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:339",
   "flow": [
    {
     "node": "physical",
     "action": "query entities for the PG from gmsh",
     "passes": "gmsh.model.getEntitiesForPhysicalGroup(dim:int, tag:int) -> list[int]",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "list[Tag]",
   "reads": [
    "gmsh physical groups"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.get_groups_for_entity",
   "label": "get_groups_for_entity",
   "composite": "physical",
   "signature": "get_groups_for_entity(dim, tag) -> list[Tag]",
   "summary": "Return the physical-group tags that contain a given model entity.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:424",
   "flow": [
    {
     "node": "physical",
     "action": "query gmsh for PGs containing the entity",
     "passes": "gmsh.model.getPhysicalGroupsForEntity(dim:int, tag:int) -> list[int]",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "list[Tag] of PG tags",
   "reads": [
    "gmsh physical groups"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.get_name",
   "label": "get_name",
   "composite": "physical",
   "signature": "get_name(dim, tag) -> str",
   "summary": "Return the name of a physical group, or empty string if unnamed.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:435",
   "flow": [
    {
     "node": "physical",
     "action": "query the PG name from gmsh",
     "passes": "gmsh.model.getPhysicalName(dim:int, tag:int) -> str",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "str name",
   "reads": [
    "gmsh physical groups"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.get_nodes",
   "label": "get_nodes",
   "composite": "physical",
   "signature": "get_nodes(dim, tag) -> dict",
   "summary": "Return the mesh nodes ({'tags','coords'}) belonging to a physical group (after meshing).",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:515",
   "flow": [
    {
     "node": "physical",
     "action": "fetch the PG's mesh nodes from gmsh and pack ndarrays",
     "passes": "gmsh.model.mesh.getNodesForPhysicalGroup(dim:int, tag:int) -> {'tags':ndarray(N), 'coords':ndarray(N,3)}",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "dict {'tags','coords'}",
   "reads": [
    "gmsh physical groups + mesh nodes"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.get_tag",
   "label": "get_tag",
   "composite": "physical",
   "signature": "get_tag(dim, name) -> Tag | None",
   "summary": "Look up the tag of a named physical group (skipping label PGs); None if absent.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:446",
   "flow": [
    {
     "node": "physical",
     "action": "scan gmsh PGs at dim for a non-label name match",
     "passes": "gmsh.model.getPhysicalGroups(dim)/getPhysicalName -> tag:int|None",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, name",
   "outputs": "Tag or None",
   "reads": [
    "gmsh physical groups"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.remove",
   "label": "remove",
   "composite": "physical",
   "signature": "remove(dim_tags) -> PhysicalGroups",
   "summary": "Remove specific physical groups by (dim, pg_tag).",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:301",
   "flow": [
    {
     "node": "physical",
     "action": "remove the named physical groups in gmsh",
     "passes": "gmsh.model.removePhysicalGroups(dimTags:list[(int,int)])",
     "to": "gmsh"
    }
   ],
   "inputs": "dim_tags list[(dim,pg_tag)]",
   "outputs": "self (PhysicalGroups)",
   "reads": [],
   "writes": [
    "gmsh physical groups (removed)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.remove_all",
   "label": "remove_all",
   "composite": "physical",
   "signature": "remove_all() -> PhysicalGroups",
   "summary": "Remove every physical group in the current model.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:313",
   "flow": [
    {
     "node": "physical",
     "action": "remove all physical groups in gmsh",
     "passes": "gmsh.model.removePhysicalGroups()",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "self (PhysicalGroups)",
   "reads": [],
   "writes": [
    "gmsh physical groups (all removed)"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.remove_name",
   "label": "remove_name",
   "composite": "physical",
   "signature": "remove_name(name) -> PhysicalGroups",
   "summary": "Remove the name->tag mapping for a physical group (group itself kept).",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:287",
   "flow": [
    {
     "node": "physical",
     "action": "remove the physical name mapping in gmsh",
     "passes": "gmsh.model.removePhysicalName(name:str)",
     "to": "gmsh"
    }
   ],
   "inputs": "name",
   "outputs": "self (PhysicalGroups)",
   "reads": [],
   "writes": [
    "gmsh physical-name mapping"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.set_name",
   "label": "set_name",
   "composite": "physical",
   "signature": "set_name(dim, tag, name) -> PhysicalGroups",
   "summary": "Assign or rename the label of an existing physical group.",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:273",
   "flow": [
    {
     "node": "physical",
     "action": "set the physical group name in gmsh",
     "passes": "gmsh.model.setPhysicalName(dim:int, tag:int, name:str)",
     "to": "gmsh"
    }
   ],
   "inputs": "dim, tag, name",
   "outputs": "self (PhysicalGroups)",
   "reads": [],
   "writes": [
    "gmsh physical-group name"
   ],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.physical.summary",
   "label": "summary",
   "composite": "physical",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame describing every user-facing physical group (dim, pg_tag, name, n_entities, entity_tags).",
   "file": "src/apeGmsh/mesh/PhysicalGroups.py:465",
   "flow": [
    {
     "node": "physical",
     "action": "iterate gmsh PGs (excluding label PGs) into a DataFrame indexed by (dim,pg_tag)",
     "passes": "gmsh.model.getPhysicalGroups/getPhysicalName/getEntitiesForPhysicalGroup -> pd.DataFrame",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "pd.DataFrame",
   "reads": [
    "gmsh physical groups"
   ],
   "writes": [],
   "_cluster": "C_mesh"
  },
  {
   "id": "g.sections.W_shell",
   "label": "W_shell",
   "composite": "sections",
   "signature": "g.sections.W_shell(bf, tf, h, tw, length, *, anchor='start', align='z', label='W_shell', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a W-shape as 3 mid-surface shell rectangles (top/bottom flange, web) directly in the session with prefixed labels, apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:709",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): _build_rect_surface x3 (points/lines/loop/plane_surface) with label='{label}.top_flange/.bottom_flange/.web', model.sync",
     "passes": "3 quad surfaces + label PGs",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (dim=2 surfaces, with .labels '{label}.top_flange/.bottom_flange/.web')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities"
   ],
   "writes": [
    "g.model.geometry.add_point/line/curve_loop/plane_surface + sync",
    "g.labels.add (via label= and umbrella)",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.W_solid",
   "label": "W_solid",
   "composite": "sections",
   "signature": "g.sections.W_solid(bf, tf, h, tw, length, *, anchor='start', align='z', label='W_solid', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a 7-volume hex-ready W-section directly in the current session (no Part/STEP), label volumes/faces by structural role, apply placement, and register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:224",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section: check label not in parts._instances, snapshot entities",
     "passes": "label: str, before: dict[int,set]",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): add_rectangle x3, boolean.cut, transforms.extrude, geo.slice x4",
     "passes": "rectangles -> cut -> extrude -> 4 slices",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "classify volumes/faces by COM, labels.add prefixed '{label}.top_flange'…",
     "passes": "top/bot/web tags, start/end face tags",
     "to": "labels"
    },
    {
     "node": "sections",
     "action": "diff after-before, setSize(new pts, lc), apply_placement(anchor/align/translate/rotate)",
     "passes": "entities delta, affine matrix",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "harvest labels touching delta, add umbrella label, compute bbox",
     "passes": "label_names, bbox",
     "to": "labels"
    },
    {
     "node": "sections",
     "action": "register Instance into parts registry with .edit",
     "passes": "Instance(label, entities, label_names)",
     "to": "parts"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (registered in g.parts, with .labels '{label}.top_flange/.web/.start_face/...')",
   "reads": [
    "parts._instances (label clash)",
    "gmsh.model.getEntities (delta + COM classify)",
    "gmsh.model.occ.getCenterOfMass"
   ],
   "writes": [
    "g.model.geometry/boolean/transforms (build geometry)",
    "g.labels.add (role + umbrella label PGs)",
    "gmsh.model.mesh.setSize",
    "apply_placement (occ.affineTransform + PG snap/restore)",
    "parts._instances[label] = Instance"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.angle_solid",
   "label": "angle_solid",
   "composite": "sections",
   "signature": "g.sections.angle_solid(b, h, t, length, *, anchor='start', align='z', label='angle_solid', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build an L-shape (two fused rectangles, extruded, sliced at the corner) directly in the session, label legs/faces by COM, apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:503",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): add_rectangle x2, boolean.fuse, transforms.extrude, geo.slice x2",
     "passes": "h_leg,v_leg -> fuse -> extrude -> 2 slices",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "classify by COM, _PrefixedLabels.add legs, classify_end/angle_outer_faces",
     "passes": "h_tags,v_tags + face tags",
     "to": "labels"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "b, h, t, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (with .labels '{label}.horizontal_leg/.vertical_leg/.*_face')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities",
    "gmsh.model.occ.getCenterOfMass / getAdjacencies"
   ],
   "writes": [
    "g.model.geometry/boolean/transforms",
    "g.labels.add (prefixed)",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.channel_solid",
   "label": "channel_solid",
   "composite": "sections",
   "signature": "g.sections.channel_solid(bf, tf, h, tw, length, *, anchor='start', align='z', label='channel_solid', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a C-shape (outer rect minus open-side void, extruded, sliced) directly in the session, label volumes/faces, apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:572",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): add_rectangle x2, boolean.cut, transforms.extrude, geo.slice x3",
     "passes": "outer,void -> cut -> extrude -> 3 slices",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "classify_w_volumes/end_faces/w_outer_faces via _PrefixedLabels",
     "passes": "volume + face tags",
     "to": "labels"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (with .labels '{label}.top_flange/.bottom_flange/.web/.*_face')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities",
    "gmsh.model.occ.getCenterOfMass"
   ],
   "writes": [
    "g.model.geometry/boolean/transforms",
    "g.labels.add (prefixed)",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.pipe_hollow",
   "label": "pipe_hollow",
   "composite": "sections",
   "signature": "g.sections.pipe_hollow(r_outer, t, length, *, anchor='start', align='z', label='pipe_hollow', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a hollow circular pipe (outer cylinder minus inner) directly in the session, label body + end faces, apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:457",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): add_cylinder outer/inner, boolean.cut, _PrefixedLabels.add 'body', classify_end_faces",
     "passes": "outer,inner -> cut",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "r_outer, t, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (with .labels '{label}.body/.start_face/.end_face')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities",
    "gmsh.model.occ.getCenterOfMass"
   ],
   "writes": [
    "g.model.geometry.add_cylinder / boolean.cut",
    "g.labels.add (prefixed)",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.pipe_solid",
   "label": "pipe_solid",
   "composite": "sections",
   "signature": "g.sections.pipe_solid(r, length, *, anchor='start', align='z', label='pipe_solid', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a solid circular bar (add_cylinder) directly in the session, label body + end faces, apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:419",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): geometry.add_cylinder, _PrefixedLabels.add 'body', classify_end_faces",
     "passes": "cylinder tag",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "r, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (with .labels '{label}.body/.start_face/.end_face')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities",
    "gmsh.model.occ.getCenterOfMass"
   ],
   "writes": [
    "g.model.geometry.add_cylinder",
    "g.labels.add",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.rect_hollow",
   "label": "rect_hollow",
   "composite": "sections",
   "signature": "g.sections.rect_hollow(b, h, t, length, *, anchor='start', align='z', label='rect_hollow', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a hollow rectangular tube (outer box minus inner box) directly in the session, label body + end faces, apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:370",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): add_box outer/inner, boolean.cut, _PrefixedLabels.add 'body', classify_end_faces",
     "passes": "outer,inner -> cut",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "b, h, t, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (with .labels '{label}.body/.start_face/.end_face')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities",
    "gmsh.model.occ.getCenterOfMass (classify)"
   ],
   "writes": [
    "g.model.geometry.add_box / boolean.cut",
    "g.labels.add (prefixed)",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.rect_solid",
   "label": "rect_solid",
   "composite": "sections",
   "signature": "g.sections.rect_solid(b, h, length, *, anchor='start', align='z', label='rect', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a solid rectangular bar directly in the session via add_box, label it '{label}.body', apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:335",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): geometry.add_box, labels.add '{label}.body'",
     "passes": "box tag",
     "to": "model.geometry"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella label + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "b, h, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (with .labels '{label}.body')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities"
   ],
   "writes": [
    "g.model.geometry.add_box",
    "g.labels.add",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "g.sections.tee_solid",
   "label": "tee_solid",
   "composite": "sections",
   "signature": "g.sections.tee_solid(bf, tf, h, tw, length, *, anchor='start', align='z', label='tee_solid', lc=1e22, translate=(0,0,0), rotate=None) -> Instance",
   "summary": "Build a T-shape (flange+stem rectangles fused, extruded, sliced) directly in the session, label flange/stem/faces by COM, apply placement, register an Instance.",
   "file": "src/apeGmsh/sections/_builder.py:636",
   "flow": [
    {
     "node": "sections",
     "action": "_build_section snapshot",
     "passes": "label, before delta",
     "to": "parts"
    },
    {
     "node": "sections",
     "action": "_build(): add_rectangle x2, boolean.fuse, transforms.extrude, geo.slice x3",
     "passes": "flange,stem -> fuse -> extrude -> 3 slices",
     "to": "model.boolean"
    },
    {
     "node": "sections",
     "action": "classify by COM, _PrefixedLabels.add flange/stem, classify_end/tee_outer_faces",
     "passes": "flange_tags,stem_tags + face tags",
     "to": "labels"
    },
    {
     "node": "sections",
     "action": "diff + setSize + apply_placement + umbrella + register",
     "passes": "entities delta, Instance",
     "to": "parts"
    }
   ],
   "inputs": "bf, tf, h, tw, length; optional anchor, align, label, lc, translate, rotate",
   "outputs": "Instance (with .labels '{label}.flange/.stem/.*_face')",
   "reads": [
    "parts._instances",
    "gmsh.model.getEntities",
    "gmsh.model.occ.getCenterOfMass"
   ],
   "writes": [
    "g.model.geometry/boolean/transforms",
    "g.labels.add (prefixed)",
    "gmsh.model.mesh.setSize",
    "apply_placement",
    "parts._instances[label]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "gm.GroundMotion",
   "label": "GroundMotion",
   "composite": "gm",
   "signature": "GroundMotion(*, accel: np.ndarray, dt: float|None=None, time: np.ndarray|None=None, source: str=\"\", metadata: Mapping=...)",
   "summary": "Immutable (frozen, slots, kw-only) dataclass holding one acceleration time history. __post_init__ normalises accel to contiguous float64 1-D, validates time monotonic/length, resolves dt (derives mean Δt from time if dt omitted, requires dt for uniform). Unit-agnostic: values stored as parsed. Exposed via `from apeGmsh.ground_motions import GroundMotion`.",
   "file": "src/apeGmsh/ground_motions/_motion.py:27",
   "flow": [
    {
     "node": "gm",
     "action": "validate+normalise accel/dt/time in __post_init__",
     "passes": "accel:ndarray float64, dt:float",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "expose derived props npts/duration/pga/time_vector/is_uniform",
     "passes": "scalars+ndarray time_vector",
     "to": "gm"
    }
   ],
   "inputs": "accel ndarray, dt seconds, optional time ndarray, source str, metadata dict",
   "outputs": "frozen GroundMotion with normalised accel/dt/time + derived properties",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.GroundMotion.to_time_series",
   "label": "to_time_series",
   "composite": "gm",
   "signature": "GroundMotion.to_time_series(ops: apeSees, *, factor=1.0, prepend_zero=False) -> Path",
   "summary": "OpenSees adapter — declares an ops.timeSeries.Path primitive from this record; uniform records pass dt=, non-uniform pass time=; factor applied at OpenSees runtime for unit conversion.",
   "file": "src/apeGmsh/ground_motions/_motion.py:146",
   "flow": [
    {
     "node": "gm",
     "action": "build kwargs (values tuple, dt or time, factor, prepend_zero)",
     "passes": "accel values tuple + dt/time",
     "to": "apesees"
    },
    {
     "node": "apesees",
     "action": "ops.timeSeries.Path(**kwargs) constructs Path primitive",
     "passes": "Path time-series primitive",
     "to": "gm"
    }
   ],
   "inputs": "apeSees session, factor multiplier, prepend_zero flag",
   "outputs": "ops.timeSeries.Path primitive",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.from_file",
   "label": "from_file",
   "composite": "gm",
   "signature": "from_file(path, *, scale_factor=1.0, dt=None, obspy_component=0) -> GroundMotion",
   "summary": "Top-level package function (also GroundMotion.from_file classmethod) — sniffs the record format from header content then dispatches to the matching first-party parser; falls back to the obspy bridge for unknown formats. The recommended public loader.",
   "file": "src/apeGmsh/ground_motions/_sniffer.py:87",
   "flow": [
    {
     "node": "gm",
     "action": "call sniff_format(path) to identify format",
     "passes": "path str",
     "to": "gm.sniffer"
    },
    {
     "node": "gm.sniffer",
     "action": "return FormatTag (peer_at2/itaca/two_column/one_column/unknown)",
     "passes": "FormatTag str",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "dispatch to read_peer_at2/read_itaca/read_two_column/read_one_column/read_obspy by tag",
     "passes": "path str + scale_factor/dt",
     "to": "gm.parsers"
    },
    {
     "node": "gm.parsers",
     "action": "parse file body into accel/dt arrays and construct",
     "passes": "GroundMotion dataclass",
     "to": "gm"
    }
   ],
   "inputs": "record file path, scale_factor multiplier, dt (required for one-column), obspy_component",
   "outputs": "GroundMotion snapshot (accel ndarray + dt)",
   "reads": [
    "record file (header + numeric body)"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.from_itaca",
   "label": "from_itaca",
   "composite": "gm.parsers",
   "signature": "read_itaca(path, *, scale_factor=1.0) -> GroundMotion  (GroundMotion.from_itaca)",
   "summary": "ITACA/ESM ASCII parser — keyword-value header (KEY: value), locates DT from SAMPLING_INTERVAL_S/etc, optional NPTS cross-check, UNITS preserved in metadata['units_declared'] (not applied). Public entry: GroundMotion.from_itaca / sniff→'itaca'.",
   "file": "src/apeGmsh/ground_motions/_parsers/_itaca.py:86",
   "flow": [
    {
     "node": "gm.parsers",
     "action": "split header dict vs numeric data block",
     "passes": "header dict + data lines",
     "to": "gm.parsers"
    },
    {
     "node": "gm.parsers",
     "action": "resolve DT/NPTS/UNITS keys, flatten+scale accel",
     "passes": "accel ndarray, dt float",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "construct with metadata{header,npts_declared,units_declared}",
     "passes": "GroundMotion dataclass",
     "to": "gm"
    }
   ],
   "inputs": "ITACA/ESM .asc file path, scale_factor",
   "outputs": "GroundMotion with full header metadata",
   "reads": [
    "ITACA/ESM ASCII file"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.from_obspy",
   "label": "from_obspy",
   "composite": "gm.parsers",
   "signature": "read_obspy(path, *, scale_factor=1.0, component=0) -> GroundMotion  (GroundMotion.from_obspy)",
   "summary": "Opt-in obspy bridge for the long tail (K-NET, SAC, MiniSEED, ...). Lazy-imports obspy (clear ImportError if absent), obspy.read() the file, selects the component-th trace, wraps via from_obspy_trace. Reached publicly via GroundMotion.from_obspy or from_file's unknown-format fallback.",
   "file": "src/apeGmsh/ground_motions/_parsers/_obspy_bridge.py:80",
   "flow": [
    {
     "node": "gm.parsers",
     "action": "lazy import obspy, obspy.read(path) → Stream",
     "passes": "obspy Stream",
     "to": "external"
    },
    {
     "node": "external",
     "action": "decode native format into traces",
     "passes": "obspy Trace list",
     "to": "gm.parsers"
    },
    {
     "node": "gm.parsers",
     "action": "pick component, delegate to from_obspy_trace",
     "passes": "obspy.Trace",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "construct accel*scale, dt=stats.delta, station/channel metadata",
     "passes": "GroundMotion dataclass",
     "to": "gm"
    }
   ],
   "inputs": "any obspy-readable file path, scale_factor, component index",
   "outputs": "GroundMotion from selected trace",
   "reads": [
    "obspy-supported seismic file"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.from_obspy_trace",
   "label": "from_obspy_trace",
   "composite": "gm.parsers",
   "signature": "from_obspy_trace(trace: obspy.Trace, *, scale_factor=1.0) -> GroundMotion  (GroundMotion.from_obspy_trace)",
   "summary": "Wrap an already-loaded obspy Trace as a GroundMotion — accel = trace.data*scale_factor, dt = stats.delta, network/station/channel/location/starttime preserved in metadata. Public entry: GroundMotion.from_obspy_trace.",
   "file": "src/apeGmsh/ground_motions/_parsers/_obspy_bridge.py:38",
   "flow": [
    {
     "node": "gm.parsers",
     "action": "contiguous float64 trace.data*scale, read stats.delta",
     "passes": "accel ndarray, dt float",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "construct with obspy stats metadata",
     "passes": "GroundMotion dataclass",
     "to": "gm"
    }
   ],
   "inputs": "obspy Trace object, scale_factor",
   "outputs": "GroundMotion snapshot",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.from_one_column",
   "label": "from_one_column",
   "composite": "gm.parsers",
   "signature": "read_one_column(path, *, dt: float, scale_factor=1.0, skiprows=0) -> GroundMotion  (GroundMotion.from_one_column)",
   "summary": "Generic accel-only parser (one or many values per line, PEER-style after header strip). dt is not encoded and must be supplied. Comment lines auto-skipped. Public entry: GroundMotion.from_one_column / sniff→'one_column' (requires dt).",
   "file": "src/apeGmsh/ground_motions/_parsers/_one_column.py:32",
   "flow": [
    {
     "node": "gm.parsers",
     "action": "strip skiprows+comments, flatten all numeric tokens",
     "passes": "list[float] accel",
     "to": "gm.parsers"
    },
    {
     "node": "gm.parsers",
     "action": "scale, asarray float64",
     "passes": "accel ndarray + dt arg",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "construct with metadata{format,scale_factor}",
     "passes": "GroundMotion dataclass",
     "to": "gm"
    }
   ],
   "inputs": "accel-only file path, mandatory dt, scale_factor, skiprows",
   "outputs": "GroundMotion (uniform, dt from caller)",
   "reads": [
    "one-column ASCII record file"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.from_peer_at2",
   "label": "from_peer_at2",
   "composite": "gm.parsers",
   "signature": "read_peer_at2(path, *, scale_factor=1.0) -> GroundMotion  (GroundMotion.from_peer_at2)",
   "summary": "PEER NGA .AT2 parser — 4-line ASCII header, line 4 regex-parsed for NPTS=/DT=, remaining lines flattened to accel[:npts]. Declared units (line 3) preserved in metadata['units_declared'] but not applied. Public entry: GroundMotion.from_peer_at2 / sniff→'peer_at2'.",
   "file": "src/apeGmsh/ground_motions/_parsers/_peer_at2.py:47",
   "flow": [
    {
     "node": "gm.parsers",
     "action": "read lines, regex line 4 for NPTS/DT",
     "passes": "npts int, dt float",
     "to": "gm.parsers"
    },
    {
     "node": "gm.parsers",
     "action": "flatten data tokens, slice [:npts], scale",
     "passes": "accel ndarray, dt float",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "construct with metadata{header1..4,npts_declared,units_declared}",
     "passes": "GroundMotion dataclass",
     "to": "gm"
    }
   ],
   "inputs": "PEER .AT2 file path, scale_factor",
   "outputs": "GroundMotion with PEER header metadata",
   "reads": [
    "PEER NGA .AT2 file"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.from_two_column",
   "label": "from_two_column",
   "composite": "gm.parsers",
   "signature": "read_two_column(path, *, scale_factor=1.0, uniform_rtol=1e-4) -> GroundMotion  (GroundMotion.from_two_column)",
   "summary": "Generic `time accel` two-column parser. Strips #/%///; comments, np.loadtxt to (N,2), infers mean Δt, classifies uniform vs non-uniform by relative deviation, preserves full time vector when non-uniform. The public entry is GroundMotion.from_two_column / sniff→'two_column'.",
   "file": "src/apeGmsh/ground_motions/_parsers/_two_column.py:39",
   "flow": [
    {
     "node": "gm.parsers",
     "action": "strip comments, np.loadtxt to (N,2) array",
     "passes": "ndarray (N,2) t|accel",
     "to": "gm.parsers"
    },
    {
     "node": "gm.parsers",
     "action": "scale accel, infer dt_mean, test uniform_rtol",
     "passes": "accel ndarray, dt float, time|None",
     "to": "gm"
    },
    {
     "node": "gm",
     "action": "construct with metadata{format,t0,uniform,scale_factor}",
     "passes": "GroundMotion dataclass",
     "to": "gm"
    }
   ],
   "inputs": "two-column file path, scale_factor, uniform_rtol",
   "outputs": "GroundMotion (uniform→dt only, non-uniform→time vector)",
   "reads": [
    "two-column ASCII record file"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "gm.sniff_format",
   "label": "sniff_format",
   "composite": "gm.sniffer",
   "signature": "sniff_format(path: str|os.PathLike) -> FormatTag",
   "summary": "Public format detector — peeks PEER banner / ITACA keyword header, else counts numeric columns on first data line, returning one of 'peer_at2'|'itaca'|'two_column'|'one_column'|'unknown'. Used by from_file and callable directly.",
   "file": "src/apeGmsh/ground_motions/_sniffer.py:65",
   "flow": [
    {
     "node": "gm.sniffer",
     "action": "call is_peer_at2(path) then is_itaca(path) detectors",
     "passes": "bool match",
     "to": "gm.parsers"
    },
    {
     "node": "gm.parsers",
     "action": "return detector verdict",
     "passes": "bool",
     "to": "gm.sniffer"
    },
    {
     "node": "gm.sniffer",
     "action": "else read first non-comment line, count numeric tokens",
     "passes": "FormatTag str",
     "to": "gm"
    }
   ],
   "inputs": "record file path",
   "outputs": "FormatTag string literal",
   "reads": [
    "record file head (first ~11 lines)"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "h5_reader.open",
   "label": "open",
   "composite": "os.emit",
   "signature": "open(path: str) -> H5Model",
   "summary": "Public reference reader for the bridge's model.h5 archive: opens the file, strictly checks /meta/schema_version major == 2 (else SchemaVersionError) and presence of /meta (else MalformedH5Error), returns a read-only H5Model accessor (materials/sections/transforms/recorders/element_meta/neutral-zone reads + validate()).",
   "file": "src/apeGmsh/opensees/emitter/h5_reader.py:65",
   "flow": [
    {
     "node": "external",
     "action": "user/viewer calls h5_reader.open(path) on a model.h5 written by apeSees.h5",
     "passes": "<model.h5 Path>",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "h5py opens file, validates /meta + semver major==EXPECTED_SCHEMA_MAJOR(2); wraps handle in H5Model (typed accessors return dict attrs / numpy datasets directly, no transform layer)",
     "passes": "<H5Model read-only accessor>",
     "to": "external"
    }
   ],
   "inputs": "path:str to a bridge model.h5",
   "outputs": "H5Model (raises SchemaVersionError / MalformedH5Error on mismatch)",
   "reads": [
    "femdata.h5"
   ],
   "writes": [],
   "_cluster": "G_os_emit"
  },
  {
   "id": "inst.edit.affine",
   "label": "affine",
   "composite": "part",
   "signature": "inst.edit.affine(matrix4x4) -> InstanceEdit",
   "summary": "Apply a general 4x4 affine transform to a placed Instance's top-dim entities in the live session; chainable.",
   "file": "src/apeGmsh/core/_instance_edit.py:193",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, flatten matrix to 16 floats",
     "passes": "matrix4x4 -> flat: list[16]",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "occ.affineTransform(top-dim dimtags, flat) + synchronize",
     "passes": "top_dimtags, flat",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "recompute Instance.bbox",
     "passes": "all dimtags -> bbox",
     "to": "parts"
    }
   ],
   "inputs": "matrix4x4 (16-seq / 4x4 / ndarray)",
   "outputs": "InstanceEdit (self); Instance transformed, bbox refreshed",
   "reads": [
    "self._inst.entities",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.affineTransform / synchronize",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.align_to",
   "label": "align_to",
   "composite": "part",
   "signature": "inst.edit.align_to(other: Instance, *, source, target, on, offset=0.0) -> InstanceEdit",
   "summary": "Translate this Instance so its source label centroid aligns with another Instance's target label centroid; both centroids read live from gmsh (same session).",
   "file": "src/apeGmsh/core/_instance_edit.py:450",
   "flow": [
    {
     "node": "parts",
     "action": "require alive; duck-check other is Instance; reject self",
     "passes": "other: Instance",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "normalize source/target to '{label}.{suffix}'",
     "passes": "source_full, target_full label names",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "both centroids via label_centroid_live (live label PGs)",
     "passes": "source/target label names",
     "to": "labels"
    },
    {
     "node": "parts",
     "action": "compute masked translation then self.translate",
     "passes": "on mask + offset",
     "to": "parts"
    }
   ],
   "inputs": "other (Instance, same session), source (suffix or qualified), target, on, offset",
   "outputs": "InstanceEdit (self); this Instance translated into alignment",
   "reads": [
    "live label PGs of both instances (label_centroid_live)",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.translate / synchronize (via self.translate)",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.align_to_point",
   "label": "align_to_point",
   "composite": "part",
   "signature": "inst.edit.align_to_point(point, *, source, on, offset=0.0) -> InstanceEdit",
   "summary": "Translate this Instance so its source label centroid (live) lands at an explicit world point on the chosen axes.",
   "file": "src/apeGmsh/core/_instance_edit.py:523",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, normalize source to '{label}.{suffix}'",
     "passes": "source_full label name",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "source centroid via label_centroid_live",
     "passes": "source label name",
     "to": "labels"
    },
    {
     "node": "parts",
     "action": "compute masked translation then self.translate",
     "passes": "target point + on mask + offset",
     "to": "parts"
    }
   ],
   "inputs": "point=(px,py,pz), source (suffix or qualified), on, offset",
   "outputs": "InstanceEdit (self); this Instance translated",
   "reads": [
    "live label PGs of this instance (label_centroid_live)",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.translate / synchronize (via self.translate)",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.copy",
   "label": "copy",
   "composite": "part",
   "signature": "inst.edit.copy(*, label: str) -> Instance",
   "summary": "Duplicate this Instance's geometry via occ.copy in the same session and recreate all its Part-level label PGs under the new instance's prefix.",
   "file": "src/apeGmsh/core/_instance_edit.py:243",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, resolve unique instance label",
     "passes": "label -> new_label: str",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "occ.copy(src dimtags) + synchronize, build src->new tag map",
     "passes": "src_dimtags -> new_dimtags",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "_rebrand_labels: per Part-label query+remap tags, labels.add under new prefix",
     "passes": "tag_map, '{new_label}.{suffix}'",
     "to": "labels"
    },
    {
     "node": "parts",
     "action": "create umbrella label, register new Instance",
     "passes": "Instance(new_label, new_entities, label_names)",
     "to": "parts"
    }
   ],
   "inputs": "label (required non-empty)",
   "outputs": "Instance (new, registered with .edit, label PGs rebranded)",
   "reads": [
    "self._inst.entities / label_names",
    "g.labels.entities (per src label/dim)"
   ],
   "writes": [
    "gmsh.model.occ.copy / synchronize",
    "g.labels.add (rebranded + umbrella label PGs)",
    "self._registry._instances[new_label]",
    "Instance.edit"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.delete",
   "label": "delete",
   "composite": "part",
   "signature": "inst.edit.delete() -> None",
   "summary": "Remove the Instance's entities from the assembly session AND unregister it from g.parts (freeing the label); subsequent edit calls raise.",
   "file": "src/apeGmsh/core/_instance_edit.py:217",
   "flow": [
    {
     "node": "parts",
     "action": "require alive",
     "passes": "_deleted: bool",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "occ.remove(all dimtags, recursive=True) + synchronize",
     "passes": "all dimtags: list[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "pop label from registry, wipe entities/bbox, mark deleted",
     "passes": "self._inst.label",
     "to": "parts"
    }
   ],
   "inputs": "none",
   "outputs": "None (geometry removed; Instance unregistered; label freed)",
   "reads": [
    "self._inst.entities",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.remove / synchronize",
    "self._registry._instances.pop(label)",
    "self._inst.entities={} / bbox=None / _deleted=True"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.dilate",
   "label": "dilate",
   "composite": "part",
   "signature": "inst.edit.dilate(sx, sy, sz, *, center=(0,0,0)) -> InstanceEdit",
   "summary": "Non-uniformly scale a placed Instance's top-dim entities by (sx,sy,sz) about center in the live session; chainable.",
   "file": "src/apeGmsh/core/_instance_edit.py:169",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, no-op if all factors==1",
     "passes": "_deleted: bool",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "occ.dilate(top-dim dimtags, center, sx/sy/sz) + synchronize",
     "passes": "top_dimtags, cx/cy/cz, sx/sy/sz",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "recompute Instance.bbox",
     "passes": "all dimtags -> bbox",
     "to": "parts"
    }
   ],
   "inputs": "sx, sy, sz; optional center",
   "outputs": "InstanceEdit (self); Instance dilated, bbox refreshed",
   "reads": [
    "self._inst.entities",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.dilate / synchronize",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.mirror",
   "label": "mirror",
   "composite": "part",
   "signature": "inst.edit.mirror(*, plane=None, normal=None, point=(0,0,0)) -> InstanceEdit",
   "summary": "Reflect a placed Instance's top-dim entities across a plane in the live session, refreshing bbox; chainable.",
   "file": "src/apeGmsh/core/_instance_edit.py:119",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, require exactly one of plane/normal",
     "passes": "plane|normal",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "compute plane coeffs, occ.mirror(top-dim dimtags) + synchronize",
     "passes": "nx,ny,nz,d",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "recompute Instance.bbox",
     "passes": "all dimtags -> bbox",
     "to": "parts"
    }
   ],
   "inputs": "plane XOR normal; optional point",
   "outputs": "InstanceEdit (self); Instance mirrored, bbox refreshed",
   "reads": [
    "self._inst.entities",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.mirror / synchronize",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.pattern_linear",
   "label": "pattern_linear",
   "composite": "part",
   "signature": "inst.edit.pattern_linear(*, label, n, dx, dy, dz) -> list[Instance]",
   "summary": "Create n copies of this Instance (occ.copy + label rebrand) each translated by i*(dx,dy,dz), all registered in g.parts.",
   "file": "src/apeGmsh/core/_instance_edit.py:385",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, validate n/label",
     "passes": "n: int, label: str",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "per i: self.copy(label_i) then new_inst.edit.translate(i*dx,i*dy,i*dz)",
     "passes": "i*(dx,dy,dz)",
     "to": "parts"
    }
   ],
   "inputs": "label (base), n>=1, dx, dy, dz",
   "outputs": "list[Instance] length n (registered)",
   "reads": [
    "self._inst.entities / label_names",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.copy + translate / synchronize",
    "g.labels.add (rebranded)",
    "self._registry._instances[*]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.pattern_polar",
   "label": "pattern_polar",
   "composite": "part",
   "signature": "inst.edit.pattern_polar(*, label, n, axis, total_angle, center=(0,0,0)) -> list[Instance]",
   "summary": "Create n copies of this Instance each rotated by i*total_angle/n about axis through center, all registered in g.parts.",
   "file": "src/apeGmsh/core/_instance_edit.py:415",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, validate n/label",
     "passes": "n: int, axis, total_angle",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "per i: self.copy(label_i) then new_inst.edit.rotate(i*step, axis, center)",
     "passes": "i*step rotation",
     "to": "parts"
    }
   ],
   "inputs": "label (base), n>=1, axis=(ax,ay,az), total_angle (rad); optional center",
   "outputs": "list[Instance] length n (registered)",
   "reads": [
    "self._inst.entities / label_names",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.copy + rotate / synchronize",
    "g.labels.add (rebranded)",
    "self._registry._instances[*]"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.rotate",
   "label": "rotate",
   "composite": "part",
   "signature": "inst.edit.rotate(angle, ax, ay, az, *, center=(0,0,0)) -> InstanceEdit",
   "summary": "Rotate a placed Instance's top-dim entities by angle radians about an axis in the live session, refreshing bbox; chainable.",
   "file": "src/apeGmsh/core/_instance_edit.py:86",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, no-op if angle==0",
     "passes": "_deleted: bool",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "occ.rotate(top-dim dimtags, center, axis, angle) + synchronize",
     "passes": "top_dimtags, cx/cy/cz, ax/ay/az, angle",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "recompute Instance.bbox",
     "passes": "all dimtags -> bbox",
     "to": "parts"
    }
   ],
   "inputs": "angle (rad), ax, ay, az; optional center",
   "outputs": "InstanceEdit (self); Instance rotated, bbox refreshed",
   "reads": [
    "self._inst.entities",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.rotate / synchronize",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.scale",
   "label": "scale",
   "composite": "part",
   "signature": "inst.edit.scale(factor, *, center=(0,0,0)) -> InstanceEdit",
   "summary": "Uniformly scale a placed Instance by factor about center (delegates to dilate); chainable.",
   "file": "src/apeGmsh/core/_instance_edit.py:157",
   "flow": [
    {
     "node": "parts",
     "action": "require alive, no-op if factor==1",
     "passes": "factor: float",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "delegate to dilate(factor,factor,factor, center)",
     "passes": "sx=sy=sz=factor",
     "to": "parts"
    }
   ],
   "inputs": "factor; optional center",
   "outputs": "InstanceEdit (self); Instance scaled, bbox refreshed",
   "reads": [
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.dilate / synchronize (via dilate)",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.edit.translate",
   "label": "translate",
   "composite": "part",
   "signature": "inst.edit.translate(dx, dy, dz) -> InstanceEdit",
   "summary": "Translate a placed Instance's top-dim entities within the live assembly session, refreshing its cached bbox; chainable.",
   "file": "src/apeGmsh/core/_instance_edit.py:71",
   "flow": [
    {
     "node": "parts",
     "action": "require alive (not deleted), no-op if zero",
     "passes": "_deleted: bool",
     "to": "parts"
    },
    {
     "node": "parts",
     "action": "occ.translate(top-dim dimtags) + synchronize",
     "passes": "top_dimtags: list[(dim,tag)]",
     "to": "gmsh"
    },
    {
     "node": "parts",
     "action": "recompute Instance.bbox",
     "passes": "all dimtags -> bbox",
     "to": "parts"
    }
   ],
   "inputs": "dx, dy, dz",
   "outputs": "InstanceEdit (self); Instance entities moved, bbox refreshed",
   "reads": [
    "self._inst.entities",
    "self._deleted"
   ],
   "writes": [
    "gmsh.model.occ.translate / synchronize",
    "self._inst.bbox"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.get",
   "label": "Instance dataclass (label/part_name/entities/bbox/properties/edit)",
   "composite": "part",
   "signature": "Instance(label, part_name, file_path, entities, translate, rotate, properties, bbox, label_names, labels, edit)",
   "summary": "Bookkeeping record returned by every parts entry point; user-reachable fields include .label, .entities, .bbox, .properties, .label_names, .labels, .edit.",
   "file": "src/apeGmsh/core/_parts_registry.py:53",
   "flow": [
    {
     "node": "parts",
     "action": "dataclass holding placement state; .edit attached at _register_instance",
     "passes": "Instance fields + InstanceEdit",
     "to": "parts"
    }
   ],
   "inputs": "none (returned object; fields read by user)",
   "outputs": "attributes: label:str, part_name:str, entities:dict[int,list[int]], translate, rotate, properties:dict, bbox, label_names:list[str], labels:_InstanceLabels, edit:InstanceEdit",
   "reads": [
    "self (dataclass fields)"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "inst.labels",
   "label": "labels (Instance.labels accessor)",
   "composite": "part",
   "signature": "inst.labels.<name> -> str  (attribute proxy)",
   "summary": "Attribute-access proxy returning the fully-qualified prefixed label string (e.g. inst.labels.web -> 'col.web'); raises AttributeError listing available labels on typo.",
   "file": "src/apeGmsh/core/_parts_registry.py:96",
   "flow": [
    {
     "node": "parts",
     "action": "__getattr__ builds '{inst.label}.{name}', validate in label_names",
     "passes": "name -> prefixed label string",
     "to": "parts"
    }
   ],
   "inputs": "attribute name (label suffix)",
   "outputs": "str (qualified label name, ready to pass to any resolver / align_to)",
   "reads": [
    "self._inst.label_names"
   ],
   "writes": [],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.affine",
   "label": "affine",
   "composite": "part",
   "signature": "part.edit.affine(matrix4x4) -> PartEdit",
   "summary": "Apply a general 4x4 affine transform to every entity in the active Part; accepts flat-16/nested/ndarray; chainable.",
   "file": "src/apeGmsh/core/_part_edit.py:300",
   "flow": [
    {
     "node": "part",
     "action": "require active session, flatten matrix to 16 floats",
     "passes": "matrix4x4 (any form) -> flat: list[16]",
     "to": "part"
    },
    {
     "node": "part",
     "action": "occ.affineTransform(all dimtags, flat) + synchronize",
     "passes": "dimtags, flat",
     "to": "gmsh"
    }
   ],
   "inputs": "matrix4x4 (16-seq / 4x4 nested / ndarray)",
   "outputs": "PartEdit (self); Part geometry transformed",
   "reads": [
    "gmsh.model.getEntities()",
    "self._part._active"
   ],
   "writes": [
    "gmsh.model.occ.affineTransform / synchronize"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.align_to",
   "label": "align_to",
   "composite": "part",
   "signature": "part.edit.align_to(other: Part, *, source, target, on, offset=0.0) -> PartEdit",
   "summary": "Translate this active Part so its source label centroid (live) aligns with another saved Part's target label centroid (read from that Part's sidecar) on chosen axes.",
   "file": "src/apeGmsh/core/_part_edit.py:539",
   "flow": [
    {
     "node": "part",
     "action": "require active; reject Instance/non-Part/self; require other.has_file",
     "passes": "other: Part",
     "to": "part"
    },
    {
     "node": "part",
     "action": "source centroid from live label PGs (label_centroid_live)",
     "passes": "source label name",
     "to": "labels"
    },
    {
     "node": "part",
     "action": "target centroid from other's STEP sidecar (label_centroid_from_sidecar)",
     "passes": "other.file_path + target name",
     "to": "part"
    },
    {
     "node": "part",
     "action": "compute masked translation then self.translate(dx,dy,dz)",
     "passes": "on axes mask + offset",
     "to": "part"
    }
   ],
   "inputs": "other (saved Part), source (label on self), target (label on other), on ('x'/'y'/'z'/'all'/iterable), offset",
   "outputs": "PartEdit (self); this Part translated into alignment",
   "reads": [
    "gmsh label PGs of this Part (label_centroid_live)",
    "other.file_path + {file}.apegmsh.json sidecar",
    "other.has_file/name"
   ],
   "writes": [
    "gmsh.model.occ.translate / synchronize (via self.translate)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.align_to_point",
   "label": "align_to_point",
   "composite": "part",
   "signature": "part.edit.align_to_point(point, *, source, on, offset=0.0) -> PartEdit",
   "summary": "Translate this active Part so its source label centroid (live) lands at an explicit world point on the chosen axes (no sidecar lookup).",
   "file": "src/apeGmsh/core/_part_edit.py:630",
   "flow": [
    {
     "node": "part",
     "action": "require active session",
     "passes": "_part._active: bool",
     "to": "part"
    },
    {
     "node": "part",
     "action": "source centroid from live label PGs (label_centroid_live)",
     "passes": "source label name",
     "to": "labels"
    },
    {
     "node": "part",
     "action": "compute masked translation then self.translate(dx,dy,dz)",
     "passes": "target point + on mask + offset",
     "to": "part"
    }
   ],
   "inputs": "point=(px,py,pz), source (label on self), on, offset",
   "outputs": "PartEdit (self); this Part translated",
   "reads": [
    "gmsh label PGs of this Part (label_centroid_live)"
   ],
   "writes": [
    "gmsh.model.occ.translate / synchronize (via self.translate)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.copy",
   "label": "copy",
   "composite": "part",
   "signature": "part.edit.copy(*, label: str) -> Part",
   "summary": "Produce a brand-new Part with its own STEP file (+sidecar) by dumping the live geometry or file-copying the saved STEP; not entered as a session.",
   "file": "src/apeGmsh/core/_part_edit.py:346",
   "flow": [
    {
     "node": "part",
     "action": "resolve unique name (hex suffix + warn on clash), mkdtemp",
     "passes": "label: str -> new_label: str",
     "to": "part"
    },
    {
     "node": "part",
     "action": "active source: occ.synchronize + gmsh.write + collect_anchors/write_sidecar",
     "passes": "new_step: Path, anchors: list[dict]",
     "to": "model.io"
    },
    {
     "node": "part",
     "action": "inactive source: shutil.copy2 STEP + sidecar",
     "passes": "src file_path + sidecar -> new_step",
     "to": "part"
    },
    {
     "node": "part",
     "action": "construct new Part, set file_path/_owns_file/_temp_dir + finalizer",
     "passes": "Part(new_label)",
     "to": "part"
    }
   ],
   "inputs": "label (required non-empty)",
   "outputs": "Part (new, has_file=True, not active, owns its tempfile)",
   "reads": [
    "gmsh.model.getEntities() (active source)",
    "self._part.file_path + sidecar (inactive source)",
    "gmsh label PGs (collect_anchors)"
   ],
   "writes": [
    "filesystem: new tempdir + {label}.step + {label}.step.apegmsh.json",
    "_LIVE_PART_NAMES (new Part registers)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.delete",
   "label": "delete",
   "composite": "part",
   "signature": "part.edit.delete() -> None",
   "summary": "Remove every entity from the active Part's Gmsh session (recursive); labels pointing at them become stale.",
   "file": "src/apeGmsh/core/_part_edit.py:325",
   "flow": [
    {
     "node": "part",
     "action": "require active session",
     "passes": "_part._active: bool",
     "to": "part"
    },
    {
     "node": "part",
     "action": "occ.remove(all dimtags, recursive=True) + synchronize",
     "passes": "dimtags: list[(dim,tag)]",
     "to": "gmsh"
    }
   ],
   "inputs": "none",
   "outputs": "None (Part geometry emptied)",
   "reads": [
    "gmsh.model.getEntities()",
    "self._part._active"
   ],
   "writes": [
    "gmsh.model.occ.remove / synchronize"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.dilate",
   "label": "dilate",
   "composite": "part",
   "signature": "part.edit.dilate(sx, sy, sz, *, center=(0,0,0)) -> PartEdit",
   "summary": "Non-uniformly scale every entity in the active Part by (sx,sy,sz) about center; chainable.",
   "file": "src/apeGmsh/core/_part_edit.py:277",
   "flow": [
    {
     "node": "part",
     "action": "require active session, no-op if all factors==1",
     "passes": "_part._active: bool",
     "to": "part"
    },
    {
     "node": "part",
     "action": "occ.dilate(all dimtags, center, sx/sy/sz) + synchronize",
     "passes": "dimtags, cx/cy/cz, sx/sy/sz",
     "to": "gmsh"
    }
   ],
   "inputs": "sx, sy, sz; optional center",
   "outputs": "PartEdit (self); Part geometry dilated",
   "reads": [
    "gmsh.model.getEntities()",
    "self._part._active"
   ],
   "writes": [
    "gmsh.model.occ.dilate / synchronize"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.mirror",
   "label": "mirror",
   "composite": "part",
   "signature": "part.edit.mirror(*, plane=None, normal=None, point=(0,0,0)) -> PartEdit",
   "summary": "Reflect every entity in the active Part across a coordinate plane or an explicit-normal plane through point; chainable.",
   "file": "src/apeGmsh/core/_part_edit.py:205",
   "flow": [
    {
     "node": "part",
     "action": "require active session, require exactly one of plane/normal",
     "passes": "plane: str|None, normal: tuple|None",
     "to": "part"
    },
    {
     "node": "part",
     "action": "compute plane coefficients, occ.mirror(all dimtags) + synchronize",
     "passes": "nx,ny,nz,d",
     "to": "gmsh"
    }
   ],
   "inputs": "plane ('xy'/'xz'/'yz') XOR normal=(nx,ny,nz); optional point",
   "outputs": "PartEdit (self); Part geometry mirrored",
   "reads": [
    "gmsh.model.getEntities()",
    "self._part._active"
   ],
   "writes": [
    "gmsh.model.occ.mirror / synchronize"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.pattern_linear",
   "label": "pattern_linear",
   "composite": "part",
   "signature": "part.edit.pattern_linear(*, label, n, dx, dy, dz) -> list[Part]",
   "summary": "Produce n new Parts each translated by i*(dx,dy,dz), by file-cloning the (inactive) source STEP and baking the transform via a transient session.",
   "file": "src/apeGmsh/core/_part_edit.py:435",
   "flow": [
    {
     "node": "part",
     "action": "require source inactive + has_file, validate n/label",
     "passes": "n: int, label: str",
     "to": "part"
    },
    {
     "node": "part",
     "action": "per i: _make_pattern_item -> copy() then open+translate+auto-persist",
     "passes": "translate_offset=(i*dx,i*dy,i*dz)",
     "to": "part"
    }
   ],
   "inputs": "label (base), n>=1, dx, dy, dz",
   "outputs": "list[Part] of length n (each with baked-in translated STEP)",
   "reads": [
    "self._part._active / has_file / file_path"
   ],
   "writes": [
    "filesystem: n new tempdirs + STEP/sidecar each (via copy + auto-persist)",
    "_LIVE_PART_NAMES"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.pattern_polar",
   "label": "pattern_polar",
   "composite": "part",
   "signature": "part.edit.pattern_polar(*, label, n, axis, total_angle, center=(0,0,0)) -> list[Part]",
   "summary": "Produce n new Parts each rotated by i*total_angle/n about axis through center, by file-cloning the inactive source and baking the rotation.",
   "file": "src/apeGmsh/core/_part_edit.py:484",
   "flow": [
    {
     "node": "part",
     "action": "require source inactive + has_file, validate n/label",
     "passes": "n: int, axis, total_angle",
     "to": "part"
    },
    {
     "node": "part",
     "action": "per i: _make_pattern_item -> copy() then open+rotate+auto-persist",
     "passes": "rotate_spec=(i*step,ax,ay,az,cx,cy,cz)",
     "to": "part"
    }
   ],
   "inputs": "label (base), n>=1, axis=(ax,ay,az), total_angle (rad); optional center",
   "outputs": "list[Part] of length n (each with baked-in rotated STEP)",
   "reads": [
    "self._part._active / has_file / file_path"
   ],
   "writes": [
    "filesystem: n new tempdirs + STEP/sidecar each",
    "_LIVE_PART_NAMES"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.rotate",
   "label": "rotate",
   "composite": "part",
   "signature": "part.edit.rotate(angle, ax, ay, az, *, center=(0,0,0)) -> PartEdit",
   "summary": "Rotate every entity in the active Part by angle radians about an axis through center; chainable.",
   "file": "src/apeGmsh/core/_part_edit.py:160",
   "flow": [
    {
     "node": "part",
     "action": "require active session, no-op if angle==0",
     "passes": "_part._active: bool",
     "to": "part"
    },
    {
     "node": "part",
     "action": "occ.rotate(all dimtags, center, axis, angle) + synchronize",
     "passes": "dimtags, cx/cy/cz, ax/ay/az, angle",
     "to": "gmsh"
    }
   ],
   "inputs": "angle (rad), ax, ay, az; optional center=(cx,cy,cz)",
   "outputs": "PartEdit (self); Part geometry rotated",
   "reads": [
    "gmsh.model.getEntities()",
    "self._part._active"
   ],
   "writes": [
    "gmsh.model.occ.rotate / synchronize"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.scale",
   "label": "scale",
   "composite": "part",
   "signature": "part.edit.scale(factor, *, center=(0,0,0)) -> PartEdit",
   "summary": "Uniformly scale every entity in the active Part by factor about center (delegates to dilate); chainable.",
   "file": "src/apeGmsh/core/_part_edit.py:262",
   "flow": [
    {
     "node": "part",
     "action": "require active session, no-op if factor==1",
     "passes": "factor: float",
     "to": "part"
    },
    {
     "node": "part",
     "action": "delegate to dilate(factor,factor,factor, center)",
     "passes": "sx=sy=sz=factor",
     "to": "part"
    }
   ],
   "inputs": "factor; optional center",
   "outputs": "PartEdit (self); Part geometry scaled",
   "reads": [
    "self._part._active"
   ],
   "writes": [
    "gmsh.model.occ.dilate / synchronize (via dilate)"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "part.edit.translate",
   "label": "translate",
   "composite": "part",
   "signature": "part.edit.translate(dx, dy, dz) -> PartEdit",
   "summary": "Translate every entity in the active Part by (dx,dy,dz); returns self for fluent chaining.",
   "file": "src/apeGmsh/core/_part_edit.py:133",
   "flow": [
    {
     "node": "part",
     "action": "require active session, no-op if zero vector",
     "passes": "_part._active: bool",
     "to": "part"
    },
    {
     "node": "part",
     "action": "occ.translate(all dimtags) + synchronize",
     "passes": "dimtags: list[(dim,tag)] = getEntities()",
     "to": "gmsh"
    }
   ],
   "inputs": "dx, dy, dz floats",
   "outputs": "PartEdit (self for chaining); Part geometry moved",
   "reads": [
    "gmsh.model.getEntities()",
    "self._part._active"
   ],
   "writes": [
    "gmsh.model.occ.translate / synchronize"
   ],
   "_cluster": "B_parts"
  },
  {
   "id": "physical.NamedGroupSet.__contains__",
   "label": "name in physical",
   "composite": "physical",
   "signature": "__contains__(name: str) -> bool",
   "summary": "True if a group with the given name exists (via the name index).",
   "file": "src/apeGmsh/mesh/_group_set.py:323",
   "flow": [
    {
     "node": "physical",
     "action": "_build_name_index then membership test",
     "passes": "bool",
     "to": "physical"
    }
   ],
   "inputs": "group name",
   "outputs": "bool",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [
    "NamedGroupSet._name_index"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.__getitem__",
   "label": "physical[name]",
   "composite": "physical",
   "signature": "__getitem__(name: str) -> dict",
   "summary": "Info dict for a group by name (KeyError if not found); multi-dim labels return the merged view.",
   "file": "src/apeGmsh/mesh/_group_set.py:328",
   "flow": [
    {
     "node": "physical",
     "action": "return self._resolve(name)",
     "passes": "info dict",
     "to": "physical"
    }
   ],
   "inputs": "group name",
   "outputs": "info dict (name, node_ids, node_coords, element_ids?)",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.__iter__",
   "label": "iter(physical)",
   "composite": "physical",
   "signature": "__iter__() -> Iterator[tuple[int, int]]",
   "summary": "Iterate sorted (dim, tag) keys of the group set.",
   "file": "src/apeGmsh/mesh/_group_set.py:374",
   "flow": [
    {
     "node": "physical",
     "action": "return iter(sorted(self._groups.keys()))",
     "passes": "Iterator[(dim,tag)]",
     "to": "physical"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of (dim, tag) tuples",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.__len__",
   "label": "len(physical)",
   "composite": "physical",
   "signature": "__len__() -> int",
   "summary": "Number of groups in the set.",
   "file": "src/apeGmsh/mesh/_group_set.py:368",
   "flow": [
    {
     "node": "physical",
     "action": "return len(self._groups)",
     "passes": "int",
     "to": "physical"
    }
   ],
   "inputs": "none",
   "outputs": "group count int",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.connectivity",
   "label": "connectivity",
   "composite": "physical",
   "signature": "connectivity(target, *, dim: int | None = None) -> ndarray",
   "summary": "Element connectivity for a dim>=1 group (object dtype); builds from per-type groups with -1 padding for mixed types; ValueError if no element data.",
   "file": "src/apeGmsh/mesh/_group_set.py:252",
   "flow": [
    {
     "node": "physical",
     "action": "_resolve(target, dim); use 'connectivity' or build padded from per-type 'groups'",
     "passes": "ndarray(E,npe) object | ValueError",
     "to": "physical"
    }
   ],
   "inputs": "group name/tag/(dim,tag), optional dim",
   "outputs": "ndarray(E,npe) object dtype connectivity",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.element_ids",
   "label": "element_ids",
   "composite": "physical",
   "signature": "element_ids(target, *, dim: int | None = None) -> ndarray",
   "summary": "Element IDs for a dim>=1 physical-group/label (object dtype); raises ValueError if the group has no element data (dim=0).",
   "file": "src/apeGmsh/mesh/_group_set.py:231",
   "flow": [
    {
     "node": "physical",
     "action": "_resolve(target, dim); if no 'element_ids' raise ValueError",
     "passes": "ndarray(E,) object | ValueError",
     "to": "physical"
    }
   ],
   "inputs": "group name/tag/(dim,tag), optional dim",
   "outputs": "ndarray(E,) object dtype element IDs",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.get_all",
   "label": "get_all",
   "composite": "physical",
   "signature": "get_all(dim: int = -1) -> list[tuple[int, int]]",
   "summary": "All groups as sorted (dim, tag) pairs, optionally filtered by dimension.",
   "file": "src/apeGmsh/mesh/_group_set.py:299",
   "flow": [
    {
     "node": "physical",
     "action": "return sorted _groups keys (filtered by dim)",
     "passes": "list[(dim,tag)]",
     "to": "physical"
    }
   ],
   "inputs": "optional dim filter",
   "outputs": "list of (dim, tag) tuples",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.get_name",
   "label": "get_name",
   "composite": "physical",
   "signature": "get_name(dim: int, tag: int) -> str",
   "summary": "Name of a group by (dim, tag), or empty string if unnamed; KeyError if the group doesn't exist.",
   "file": "src/apeGmsh/mesh/_group_set.py:305",
   "flow": [
    {
     "node": "physical",
     "action": "lookup _groups[(dim,tag)]['name']",
     "passes": "str | KeyError",
     "to": "physical"
    }
   ],
   "inputs": "dim, tag",
   "outputs": "group name string",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.get_tag",
   "label": "get_tag",
   "composite": "physical",
   "signature": "get_tag(dim: int, name: str) -> int | None",
   "summary": "Tag of a named group at a given dim, or None if not found.",
   "file": "src/apeGmsh/mesh/_group_set.py:314",
   "flow": [
    {
     "node": "physical",
     "action": "scan _groups for matching (dim, name)",
     "passes": "int | None",
     "to": "physical"
    }
   ],
   "inputs": "dim, group name",
   "outputs": "tag int or None",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.names",
   "label": "names",
   "composite": "physical",
   "signature": "names(dim: int = -1) -> list[str]",
   "summary": "All group names, optionally filtered by dimension.",
   "file": "src/apeGmsh/mesh/_group_set.py:290",
   "flow": [
    {
     "node": "physical",
     "action": "iterate sorted _groups, collect names matching dim",
     "passes": "list[str]",
     "to": "physical"
    }
   ],
   "inputs": "optional dim filter",
   "outputs": "list of group name strings",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.node_coords",
   "label": "node_coords",
   "composite": "physical",
   "signature": "node_coords(target, *, dim: int | None = None) -> ndarray",
   "summary": "Node coordinates for a physical-group/label, ndarray(N, 3) float64; multi-dim labels reindexed to deduped node IDs.",
   "file": "src/apeGmsh/mesh/_group_set.py:222",
   "flow": [
    {
     "node": "physical",
     "action": "_resolve(target, dim) then return info['node_coords']",
     "passes": "ndarray(N,3) float64",
     "to": "physical"
    }
   ],
   "inputs": "group name/tag/(dim,tag), optional dim",
   "outputs": "ndarray(N,3) float64 coordinates",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.node_ids",
   "label": "node_ids",
   "composite": "physical",
   "signature": "node_ids(target, *, dim: int | None = None) -> ndarray",
   "summary": "Node IDs for a physical-group/label by name/tag/(dim,tag); multi-dim labels return a merged union; raises KeyError on miss.",
   "file": "src/apeGmsh/mesh/_group_set.py:205",
   "flow": [
    {
     "node": "physical",
     "action": "_resolve(target, dim) name-index lookup / multi-dim _merge_infos",
     "passes": "target str/int/(dim,tag), dim:int|None",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "return info['node_ids'] (object dtype)",
     "passes": "ndarray(N,) object",
     "to": "physical"
    }
   ],
   "inputs": "group name/tag/(dim,tag), optional dim",
   "outputs": "ndarray(N,) object dtype node IDs",
   "reads": [
    "NamedGroupSet._groups",
    "_name_index",
    "_merged_cache"
   ],
   "writes": [
    "NamedGroupSet._name_index",
    "_merged_cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "physical.NamedGroupSet.summary",
   "label": "summary",
   "composite": "physical",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame describing every group, indexed by (dim, pg_tag) with name/n_nodes/n_elems columns.",
   "file": "src/apeGmsh/mesh/_group_set.py:337",
   "flow": [
    {
     "node": "physical",
     "action": "build rows from each _groups info dict",
     "passes": "pd.DataFrame",
     "to": "physical"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame",
   "reads": [
    "NamedGroupSet._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "python -m apeGmsh.viewers",
   "label": "viewers.__main__",
   "composite": "viewer.results",
   "signature": "python -m apeGmsh.viewers <path> [--title TITLE]  -> exit code int",
   "summary": "CLI entry that opens a Results file in the post-solve viewer as a standalone subprocess (used by Results.viewer(blocking=False) so the viewer survives a notebook/kernel crash). Picks Results.from_mpco for .mpco else Results.from_native, then calls results.viewer(blocking=True, title=).",
   "file": "src/apeGmsh/viewers/__main__.py:24",
   "flow": [
    {
     "node": "viewer.results",
     "action": "argparse parses path + --title; error exit 2 if the file does not exist",
     "passes": "Path, title str|None",
     "to": "viewer.results"
    },
    {
     "node": "viewer.results",
     "action": "_open_results picks Results.from_mpco (.mpco) or Results.from_native by extension",
     "passes": "results file path -> Results object",
     "to": "results.readers"
    },
    {
     "node": "viewer.results",
     "action": "results.viewer(blocking=True, title=) launches the ResultsViewer Qt loop and blocks until the window closes; returns exit code 0",
     "passes": "Results -> ResultsViewer (viewer internals out of scope)",
     "to": "viewer"
    }
   ],
   "inputs": "argv: path (.h5 native or .mpco STKO), --title",
   "outputs": "process exit code (0 success, 2 file-not-found); opens the blocking Results viewer window",
   "reads": [
    "results file on disk via Results.from_native/from_mpco"
   ],
   "writes": [],
   "_cluster": "J1_view_cut"
  },
  {
   "id": "records.MassSet.by_node",
   "label": "by_node",
   "composite": "records",
   "signature": "by_node(node_id: int) -> MassRecord | None",
   "summary": "The MassRecord for a node, or None if none.",
   "file": "src/apeGmsh/mesh/_record_set.py:698",
   "flow": [
    {
     "node": "records",
     "action": "scan records for r.node_id == node_id",
     "passes": "MassRecord | None",
     "to": "records"
    }
   ],
   "inputs": "node ID",
   "outputs": "MassRecord or None",
   "reads": [
    "MassSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.MassSet.summary",
   "label": "summary",
   "composite": "records",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame with one row per node: node_id, mx, my, mz, Ixx, Iyy, Izz.",
   "file": "src/apeGmsh/mesh/_record_set.py:712",
   "flow": [
    {
     "node": "records",
     "action": "build per-record mass rows",
     "passes": "pd.DataFrame",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame",
   "reads": [
    "MassSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.MassSet.total_mass",
   "label": "total_mass",
   "composite": "records",
   "signature": "total_mass() -> float",
   "summary": "Sum of translational mass (mx) over all records (assumes isotropic mx==my==mz).",
   "file": "src/apeGmsh/mesh/_record_set.py:705",
   "flow": [
    {
     "node": "records",
     "action": "sum float(r.mass[0]) over records",
     "passes": "float",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "float total translational mass",
   "reads": [
    "MassSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodalLoadSet.Kind",
   "label": "Kind",
   "composite": "records",
   "signature": "class attribute Kind = LoadKind",
   "summary": "Constant class of load kinds for magic-string-free comparisons (also on SurfaceConstraintSet/ElementLoadSet).",
   "file": "src/apeGmsh/mesh/_record_set.py:585",
   "flow": [
    {
     "node": "records",
     "action": "expose LoadKind constants",
     "passes": "LoadKind",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "LoadKind constant class",
   "reads": [
    "records._kinds.LoadKind"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodalLoadSet.by_pattern",
   "label": "by_pattern",
   "composite": "records",
   "signature": "by_pattern(name: str) -> list[NodalLoadRecord]",
   "summary": "All nodal-load records belonging to the named pattern (also on ElementLoadSet).",
   "file": "src/apeGmsh/mesh/_record_set.py:595",
   "flow": [
    {
     "node": "records",
     "action": "filter records by r.pattern == name",
     "passes": "list[NodalLoadRecord]",
     "to": "records"
    }
   ],
   "inputs": "pattern name",
   "outputs": "list of records for that pattern",
   "reads": [
    "NodalLoadSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodalLoadSet.patterns",
   "label": "patterns",
   "composite": "records",
   "signature": "patterns() -> list[str]",
   "summary": "Unique nodal-load pattern names in insertion order (also on ElementLoadSet).",
   "file": "src/apeGmsh/mesh/_record_set.py:587",
   "flow": [
    {
     "node": "records",
     "action": "collect distinct r.pattern preserving order",
     "passes": "list[str]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "list of pattern-name strings",
   "reads": [
    "NodalLoadSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodalLoadSet.summary",
   "label": "summary",
   "composite": "records",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame of (pattern, kind) → count for nodal loads (also on ElementLoadSet/MassSet with their own columns).",
   "file": "src/apeGmsh/mesh/_record_set.py:599",
   "flow": [
    {
     "node": "records",
     "action": "tally counts per (pattern, kind)",
     "passes": "pd.DataFrame",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame",
   "reads": [
    "NodalLoadSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.Kind",
   "label": "Kind",
   "composite": "records",
   "signature": "class attribute Kind = ConstraintKind",
   "summary": "Linter-friendly constant class of constraint kinds (RIGID_BEAM, EQUAL_DOF, …) for magic-string-free comparisons.",
   "file": "src/apeGmsh/mesh/_record_set.py:223",
   "flow": [
    {
     "node": "records",
     "action": "expose ConstraintKind constants",
     "passes": "ConstraintKind",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "ConstraintKind constant class",
   "reads": [
    "records._kinds.ConstraintKind"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.equal_dofs",
   "label": "equal_dofs",
   "composite": "records",
   "signature": "equal_dofs() -> Iterator[NodePairRecord]",
   "summary": "Yield equal_dof pairs (direct EQUAL_DOF records + node_to_surface phantom→slave equal-DOF records).",
   "file": "src/apeGmsh/mesh/_record_set.py:418",
   "flow": [
    {
     "node": "records",
     "action": "filter EQUAL_DOF pairs + NTS.equal_dof_records",
     "passes": "Iterator[NodePairRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of NodePairRecord",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.node_to_surfaces",
   "label": "node_to_surfaces",
   "composite": "records",
   "signature": "node_to_surfaces() -> Iterator[NodeToSurfaceRecord]",
   "summary": "Yield only NodeToSurfaceRecord instances (for fields the flattened pairs can't expose, e.g. phantom_coords).",
   "file": "src/apeGmsh/mesh/_record_set.py:251",
   "flow": [
    {
     "node": "records",
     "action": "filter records to NodeToSurfaceRecord",
     "passes": "Iterator[NodeToSurfaceRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of NodeToSurfaceRecord",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.pairs",
   "label": "pairs",
   "composite": "records",
   "signature": "pairs() -> Iterator[NodePairRecord]",
   "summary": "Flat sequence of constraint pairs — compound NodeGroup/NodeToSurface records expanded automatically into NodePairRecords.",
   "file": "src/apeGmsh/mesh/_record_set.py:227",
   "flow": [
    {
     "node": "records",
     "action": "iterate records; expand_to_pairs()/expand() for compound",
     "passes": "Iterator[NodePairRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of NodePairRecord",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.phantom_nodes",
   "label": "phantom_nodes",
   "composite": "records",
   "signature": "phantom_nodes() -> NodeResult",
   "summary": "Phantom nodes (from node_to_surface) that solvers must create before emitting constraints; returned as a NodeResult of (id, xyz) pairs.",
   "file": "src/apeGmsh/mesh/_record_set.py:448",
   "flow": [
    {
     "node": "records",
     "action": "collect phantom tags+coords from NodeToSurfaceRecords",
     "passes": "ids object ndarray, coords float64(N,3)",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "deferred import NodeResult, construct",
     "passes": "NodeResult",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "NodeResult (empty if no phantom nodes)",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.rigid_diaphragms",
   "label": "rigid_diaphragms",
   "composite": "records",
   "signature": "rigid_diaphragms() -> Iterator[tuple[int, int, list[int]]]",
   "summary": "Yield (perp_dirn, master, slaves) for rigid_diaphragm records; perp_dirn derived from the resolved plane normal (not hardcoded 3).",
   "file": "src/apeGmsh/mesh/_record_set.py:389",
   "flow": [
    {
     "node": "records",
     "action": "filter NodeGroupRecord RIGID_DIAPHRAGM, _perp_dirn(plane_normal)",
     "passes": "Iterator[(perp:int, master:int, [slave:int])]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of (perp_dirn, master, slave-list) tuples",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.rigid_link_groups",
   "label": "rigid_link_groups",
   "composite": "records",
   "signature": "rigid_link_groups() -> Iterator[tuple[int, list[int]]]",
   "summary": "Yield (master, slaves) for rigid_beam/rigid_rod/rigid_body + node_to_surface phantoms; excludes rigid_diaphragm and kinematic_coupling to avoid double-constraint.",
   "file": "src/apeGmsh/mesh/_record_set.py:266",
   "flow": [
    {
     "node": "records",
     "action": "accumulate slaves by master from rigid pair/group/NTS records",
     "passes": "Iterator[(master:int, [slave:int])]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of (master, slave-list) tuples",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.stiff_beam_groups",
   "label": "stiff_beam_groups",
   "composite": "records",
   "signature": "stiff_beam_groups() -> Iterator[tuple[int, list[int]]]",
   "summary": "Yield (master, slaves) for the spring variant of node_to_surface (rigid_beam_stiff kind) — emitted as stiff elasticBeamColumn elements, not rigidLink.",
   "file": "src/apeGmsh/mesh/_record_set.py:339",
   "flow": [
    {
     "node": "records",
     "action": "accumulate RIGID_BEAM_STIFF pairs by master",
     "passes": "Iterator[(master:int, [slave:int])]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of (master, slave-list) tuples",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.NodeConstraintSet.summary",
   "label": "summary",
   "composite": "records",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame of the constraint set: kind, count, n_node_pairs (compound records expanded for the pair count).",
   "file": "src/apeGmsh/mesh/_record_set.py:491",
   "flow": [
    {
     "node": "records",
     "action": "tally counts + node-pair counts per kind",
     "passes": "pd.DataFrame",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame",
   "reads": [
    "NodeConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.SPSet.by_node",
   "label": "by_node",
   "composite": "records",
   "signature": "by_node(node_id: int) -> list[SPRecord]",
   "summary": "All single-point-constraint records for a given node.",
   "file": "src/apeGmsh/mesh/_record_set.py:655",
   "flow": [
    {
     "node": "records",
     "action": "filter records where r.node_id == node_id",
     "passes": "list[SPRecord]",
     "to": "records"
    }
   ],
   "inputs": "node ID",
   "outputs": "list of SPRecords for that node",
   "reads": [
    "SPSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.SPSet.homogeneous",
   "label": "homogeneous",
   "composite": "records",
   "signature": "homogeneous() -> list[SPRecord]",
   "summary": "Only the homogeneous (fix) single-point-constraint records.",
   "file": "src/apeGmsh/mesh/_record_set.py:647",
   "flow": [
    {
     "node": "records",
     "action": "filter records where is_homogeneous",
     "passes": "list[SPRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "list of fix SPRecords",
   "reads": [
    "SPSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.SPSet.prescribed",
   "label": "prescribed",
   "composite": "records",
   "signature": "prescribed() -> list[SPRecord]",
   "summary": "Only the non-zero prescribed-displacement single-point-constraint records.",
   "file": "src/apeGmsh/mesh/_record_set.py:651",
   "flow": [
    {
     "node": "records",
     "action": "filter records where not is_homogeneous",
     "passes": "list[SPRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "list of prescribed SPRecords",
   "reads": [
    "SPSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.SurfaceConstraintSet.couplings",
   "label": "couplings",
   "composite": "records",
   "signature": "couplings() -> Iterator[SurfaceCouplingRecord]",
   "summary": "Yield top-level SurfaceCouplingRecords (mortar/tied_contact) without expanding slave lists — needed for mortar_operator.",
   "file": "src/apeGmsh/mesh/_record_set.py:835",
   "flow": [
    {
     "node": "records",
     "action": "filter records to SurfaceCouplingRecord",
     "passes": "Iterator[SurfaceCouplingRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of SurfaceCouplingRecord",
   "reads": [
    "SurfaceConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.SurfaceConstraintSet.interpolations",
   "label": "interpolations",
   "composite": "records",
   "signature": "interpolations() -> Iterator[InterpolationRecord]",
   "summary": "Yield InterpolationRecords (tie/distributing/embedded plus tied_contact/mortar slave records, expanded automatically).",
   "file": "src/apeGmsh/mesh/_record_set.py:818",
   "flow": [
    {
     "node": "records",
     "action": "yield InterpolationRecord; expand SurfaceCouplingRecord.slave_records",
     "passes": "Iterator[InterpolationRecord]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of InterpolationRecord",
   "reads": [
    "SurfaceConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records.SurfaceConstraintSet.summary",
   "label": "summary",
   "composite": "records",
   "signature": "summary() -> pd.DataFrame",
   "summary": "DataFrame of surface constraints: kind, count, n_interpolations (coupling records expanded for the count).",
   "file": "src/apeGmsh/mesh/_record_set.py:847",
   "flow": [
    {
     "node": "records",
     "action": "tally counts + interpolation counts per kind",
     "passes": "pd.DataFrame",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "pandas DataFrame",
   "reads": [
    "SurfaceConstraintSet._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records._RecordSetBase.__bool__",
   "label": "bool(record set)",
   "composite": "records",
   "signature": "__bool__() -> bool",
   "summary": "True if the set holds at least one record.",
   "file": "src/apeGmsh/mesh/_record_set.py:92",
   "flow": [
    {
     "node": "records",
     "action": "return len(self._records) > 0",
     "passes": "bool",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "bool",
   "reads": [
    "_RecordSetBase._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records._RecordSetBase.__getitem__",
   "label": "record_set[idx]",
   "composite": "records",
   "signature": "__getitem__(idx: int) -> Record",
   "summary": "Direct index access to a record in the set.",
   "file": "src/apeGmsh/mesh/_record_set.py:86",
   "flow": [
    {
     "node": "records",
     "action": "return self._records[idx]",
     "passes": "Record",
     "to": "records"
    }
   ],
   "inputs": "integer index",
   "outputs": "single record",
   "reads": [
    "_RecordSetBase._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records._RecordSetBase.__iter__",
   "label": "iter(record set)",
   "composite": "records",
   "signature": "__iter__() -> Iterator[Record]",
   "summary": "Iterate all records in the set (mixed subclass types, no compound expansion).",
   "file": "src/apeGmsh/mesh/_record_set.py:83",
   "flow": [
    {
     "node": "records",
     "action": "return iter(self._records)",
     "passes": "Iterator[Record]",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of records",
   "reads": [
    "_RecordSetBase._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records._RecordSetBase.__len__",
   "label": "len(record set)",
   "composite": "records",
   "signature": "__len__() -> int",
   "summary": "Number of records in the set.",
   "file": "src/apeGmsh/mesh/_record_set.py:89",
   "flow": [
    {
     "node": "records",
     "action": "return len(self._records)",
     "passes": "int",
     "to": "records"
    }
   ],
   "inputs": "none",
   "outputs": "record count int",
   "reads": [
    "_RecordSetBase._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "records._RecordSetBase.by_kind",
   "label": "by_kind",
   "composite": "records",
   "signature": "by_kind(kind: str) -> list[Record]",
   "summary": "Return all records in a record set matching a constraint/load kind (shared across all sub-composites).",
   "file": "src/apeGmsh/mesh/_record_set.py:76",
   "flow": [
    {
     "node": "records",
     "action": "filter self._records by getattr(r,'kind')==kind",
     "passes": "list[Record]",
     "to": "records"
    }
   ],
   "inputs": "kind string",
   "outputs": "list of matching records",
   "reads": [
    "_RecordSetBase._records"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "results.capture.DomainCapture",
   "label": "DomainCapture",
   "composite": "results.capture",
   "signature": "DomainCapture(spec: ResolvedDomainCaptureSpec, path, fem: FEMData, *, ops=None)",
   "summary": "Context manager that wraps a ResolvedDomainCaptureSpec + target HDF5 path and records an in-process openseespy analysis (user drives the loop, calls step(t)). ndm/ndf ride on the resolved spec. Usually constructed via ops.domain_capture(...) or DomainCapture.from_h5(...).",
   "file": "src/apeGmsh/results/capture/_domain.py:266",
   "flow": [
    {
     "node": "results.capture",
     "action": "store resolved spec, output Path, fem, ndm/ndf from spec; defer NativeWriter to __enter__",
     "passes": "<ResolvedDomainCaptureSpec, path:Path, fem:FEMData>",
     "to": "results.capture"
    }
   ],
   "inputs": "ResolvedDomainCaptureSpec, output path, FEMData, optional ops module",
   "outputs": "DomainCapture context manager",
   "reads": [
    "ResolvedDomainCaptureSpec.ndm/ndf/records"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.capture.DomainCapture.begin_stage",
   "label": "begin_stage",
   "composite": "results.capture",
   "signature": "begin_stage(name: str, kind: str = 'transient') -> str",
   "summary": "Open a new capture stage, reset all per-record buffers; the time vector and datasets are written lazily at end_stage (step count not known up-front). Returns the stage name.",
   "file": "src/apeGmsh/results/capture/_domain.py:441",
   "flow": [
    {
     "node": "results.capture",
     "action": "validate writer open + no stage already open; reset every category capturer's per-stage buffers",
     "passes": "<name:str, kind:str>",
     "to": "results.capture"
    }
   ],
   "inputs": "stage name, kind ('transient'|'static')",
   "outputs": "stage name (str)",
   "reads": [
    "DomainCapture._writer",
    "DomainCapture._current_stage"
   ],
   "writes": [
    "DomainCapture._current_stage",
    "DomainCapture._stage_kind",
    "per-capturer buffers"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.capture.DomainCapture.capture_modes",
   "label": "capture_modes",
   "composite": "results.capture",
   "signature": "capture_modes(n_modes: Optional[int] = None) -> None",
   "summary": "Run ops.eigen(n_modes) and write one kind='mode' single-step stage per mode (eigenvalue/frequency/period metadata + nodeEigenvector translational/rotational shapes). n_modes defaults to the max across the spec's modal records.",
   "file": "src/apeGmsh/results/capture/_domain.py:696",
   "flow": [
    {
     "node": "results.capture",
     "action": "determine n_modes from modal records; lazily resolve ops and call ops.eigen(n_modes)",
     "passes": "<n_modes:int>",
     "to": "external"
    },
    {
     "node": "external",
     "action": "ops.eigen returns eigenvalues; ops.nodeEigenvector queried per node/mode/axis",
     "passes": "<eigenvalues:list[float], shapes:ndarray>",
     "to": "results.capture"
    },
    {
     "node": "results.capture",
     "action": "per mode NativeWriter.begin_stage(kind='mode', eigenvalue/freq/period) + write_nodes(displacement_*/rotation_*) + end_stage",
     "passes": "<node_ids:ndarray, components:dict[str,ndarray(1,N)]>",
     "to": "results.writers"
    },
    {
     "node": "results.writers",
     "action": "one mode stage written per eigenpair",
     "passes": "<HDF5 mode stage>",
     "to": "femdata.h5"
    }
   ],
   "inputs": "optional n_modes override; live openseespy domain",
   "outputs": "None (one mode-kind stage per mode written)",
   "reads": [
    "DomainCapture._spec modal records",
    "live openseespy domain",
    "fem.nodes.ids"
   ],
   "writes": [
    "native HDF5 mode stages (via NativeWriter)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.capture.DomainCapture.close",
   "label": "close",
   "composite": "results.capture",
   "signature": "close() -> None",
   "summary": "Finalise any open stage (auto end_stage) and close the underlying NativeWriter; also invoked by __exit__.",
   "file": "src/apeGmsh/results/capture/_domain.py:365",
   "flow": [
    {
     "node": "results.capture",
     "action": "if a stage is open call end_stage(), then NativeWriter.close()",
     "passes": "<>",
     "to": "results.writers"
    },
    {
     "node": "results.writers",
     "action": "close the h5py.File",
     "passes": "<closed HDF5 file>",
     "to": "femdata.h5"
    }
   ],
   "inputs": "none",
   "outputs": "None (file finalised)",
   "reads": [
    "DomainCapture._current_stage",
    "DomainCapture._writer"
   ],
   "writes": [
    "DomainCapture._writer:=None"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.capture.DomainCapture.end_stage",
   "label": "end_stage",
   "composite": "results.capture",
   "signature": "end_stage() -> None",
   "summary": "Flush all buffered stage data to disk: derive the shared time vector, NativeWriter.begin_stage, merge per-record node buffers (union ids, NaN-fill), write gauss/line/nodal/fiber/layer groups, close the stage; emits a consolidated skipped-element warning.",
   "file": "src/apeGmsh/results/capture/_domain.py:504",
   "flow": [
    {
     "node": "results.capture",
     "action": "pick the stage time vector from the first non-empty capturer",
     "passes": "<time_vec:ndarray(T,)>",
     "to": "results.capture"
    },
    {
     "node": "results.capture",
     "action": "NativeWriter.begin_stage(name, kind, time)",
     "passes": "<name:str, kind:str, time:ndarray>",
     "to": "results.writers"
    },
    {
     "node": "results.capture",
     "action": "merge node buffers to one (T,Nunion) slab; call writer.write_nodes / write_gauss_group / write_line_stations_group / write_nodal_forces_group / write_fibers_group / write_layers_group",
     "passes": "<node_ids:ndarray, components:dict[str,ndarray(T,N)]>",
     "to": "results.writers"
    },
    {
     "node": "results.writers",
     "action": "datasets written into the stage group; writer.end_stage closes it",
     "passes": "<HDF5 stage group>",
     "to": "femdata.h5"
    }
   ],
   "inputs": "none (consumes buffered stage state)",
   "outputs": "None (stage written into the native HDF5)",
   "reads": [
    "per-capturer buffers",
    "DomainCapture._current_stage/_stage_kind"
   ],
   "writes": [
    "native HDF5 stage group (via NativeWriter)",
    "DomainCapture._current_stage:=None"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.capture.DomainCapture.from_h5",
   "label": "from_h5",
   "composite": "results.capture",
   "signature": "DomainCapture.from_h5(model_path, *, spec: DomainCaptureSpec, fem: FEMData, output, ops=None) -> DomainCapture",
   "summary": "Construct a DomainCapture for the non-bridge path: reads ndm/ndf from a bridge-emitted model.h5 /meta, resolves a standalone spec against fem with those dims, returns a ready context manager writing to output.",
   "file": "src/apeGmsh/results/capture/_domain.py:377",
   "flow": [
    {
     "node": "results.capture",
     "action": "open model.h5 via opensees.emitter.h5_reader, read /meta ndm+ndf",
     "passes": "<model_path:Path>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "h5_reader returns meta dict with ndm/ndf",
     "passes": "<ndm:int, ndf:int>",
     "to": "results.capture"
    },
    {
     "node": "results.capture",
     "action": "spec._resolve_with_explicit_ndm_ndf(fem, ndm, ndf) -> ResolvedDomainCaptureSpec",
     "passes": "<DomainCaptureSpec, fem:FEMData, ndm, ndf>",
     "to": "results.spec"
    },
    {
     "node": "results.spec",
     "action": "return ResolvedDomainCaptureSpec; wrap in a new DomainCapture(resolved, output, fem)",
     "passes": "<ResolvedDomainCaptureSpec>",
     "to": "results.capture"
    }
   ],
   "inputs": "model.h5 path, standalone DomainCaptureSpec, fem, output path, optional ops",
   "outputs": "DomainCapture context manager (ndm/ndf from file /meta)",
   "reads": [
    "model.h5 /meta (ndm/ndf via h5_reader)",
    "DomainCaptureSpec._records",
    "fem.snapshot_id"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.capture.DomainCapture.step",
   "label": "step",
   "composite": "results.capture",
   "signature": "step(t: float) -> None",
   "summary": "Capture one snapshot at simulation time t — queries the live openseespy domain (ops.nodeDisp/nodeReaction/eleResponse/...) per category capturer and buffers values in RAM. Raises NotImplementedError for unsupported element-level categories.",
   "file": "src/apeGmsh/results/capture/_domain.py:472",
   "flow": [
    {
     "node": "results.capture",
     "action": "lazily resolve ops module; if any record needs reactions call ops.reactions()",
     "passes": "<ops module>",
     "to": "external"
    },
    {
     "node": "external",
     "action": "ops.nodeDisp/nodeVel/nodeReaction/eleResponse/eleType queried per capturer",
     "passes": "<t:float, ndarray per-component frames>",
     "to": "results.capture"
    },
    {
     "node": "results.capture",
     "action": "append per-component (N,) / per-class flat frames to in-RAM buffers",
     "passes": "<buffered frames>",
     "to": "results.capture"
    }
   ],
   "inputs": "t (current ops.getTime())",
   "outputs": "None (state appended to capturer buffers)",
   "reads": [
    "live openseespy domain",
    "DomainCapture._spec.records"
   ],
   "writes": [
    "per-capturer _times/_values buffers"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.capture.DomainCaptureSpec",
   "label": "DomainCaptureSpec",
   "composite": "results.capture",
   "signature": "DomainCaptureSpec(opensees: Any = None)",
   "summary": "Declarative spec builder for in-process DomainCapture. Standalone supports declaration+introspection; an attached OpenSees bridge (opensees=ops) is required for .resolve() so ndm/ndf are unambiguous (Phase 9 D8). Public entry of the capture/spec builder API.",
   "file": "src/apeGmsh/results/capture/spec.py:262",
   "flow": [
    {
     "node": "results.capture",
     "action": "construct an empty record list; retain optional opensees bridge handle for resolve-time ndm/ndf + class hints",
     "passes": "<DomainCaptureSpec>",
     "to": "results.spec"
    }
   ],
   "inputs": "optional opensees bridge (apeSees instance)",
   "outputs": "DomainCaptureSpec instance",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records",
    "DomainCaptureSpec._opensees"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.elements.available_components",
   "label": "elements.available_components",
   "composite": "results.elements",
   "signature": "available_components(*, stage: str | None = None) -> list[str]",
   "summary": "Canonical per-element-node component names present for a stage. Delegates to reader.available_components(sid, ResultLevel.ELEMENTS).",
   "file": "src/apeGmsh/results/_composites.py:913",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "reader.available_components(sid, ResultLevel.ELEMENTS)",
     "passes": "list[str]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "list[str] element component names",
   "reads": [
    "ResultsReader.available_components"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.fibers",
   "label": "elements.fibers",
   "composite": "results.elements",
   "signature": "attribute elements.fibers: FibersResultsComposite",
   "summary": "Fiber-level value sub-composite (fibers within fiber-section GPs). Same selection vocabulary + element-centroid geometric selectors; .get() adds gp_indices= and returns a FiberSlab with section-local y/z/area/material_tag location columns.",
   "file": "src/apeGmsh/results/_composites.py:834",
   "flow": [
    {
     "node": "results.elements",
     "action": "FibersResultsComposite(results) constructed in ElementResultsComposite.__init__",
     "passes": "FibersResultsComposite",
     "to": "results.elements"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "FibersResultsComposite",
   "reads": [
    "src/apeGmsh/results/_composites.py:FibersResultsComposite"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.fibers.available_components",
   "label": "elements.fibers.available_components",
   "composite": "results.elements",
   "signature": "available_components(*, stage: str | None = None) -> list[str]",
   "summary": "Canonical fiber-level component names present for a stage. Delegates to reader.available_components(sid, ResultLevel.FIBERS).",
   "file": "src/apeGmsh/results/_composites.py:987",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "reader.available_components(sid, ResultLevel.FIBERS)",
     "passes": "list[str]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "list[str] fiber component names",
   "reads": [
    "ResultsReader.available_components"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.fibers.get",
   "label": "elements.fibers.get",
   "composite": "results.elements",
   "signature": "get(*, pg=None, label=None, selection=None, ids=None, gp_indices=None, component: str, time: TimeSlice = None, stage: str | None = None) -> FiberSlab",
   "summary": "Read a fiber-level component. Resolves stage and element ids like elements.get; gp_indices= (optional) selects fiber-section GPs (cast to int64). Delegates to reader.read_fibers returning a FiberSlab (values (T,sum_F), element_index/gp_index/y/z/area/material_tag (sum_F,), time (T,)).",
   "file": "src/apeGmsh/results/_composites.py:962",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.elements",
     "action": "_resolve_element_ids; gp_indices → int64 ndarray|None",
     "passes": "selectors, gpi",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "fem.elements.* via Results._fem (None = all elements)",
     "passes": "eids:ndarray|None",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.read_fibers(sid, component, element_ids=, gp_indices=gpi, time_slice=time)",
     "passes": "FiberSlab(values:(T,sum_F))",
     "to": "results.slabs"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids=, optional gp_indices=, component (required), time, stage",
   "outputs": "FiberSlab",
   "reads": [
    "FEMData.elements.* (via Results._fem)",
    "ResultsReader.read_fibers"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.gauss",
   "label": "elements.gauss",
   "composite": "results.elements",
   "signature": "attribute elements.gauss: GaussResultsComposite",
   "summary": "Continuum Gauss-point value sub-composite. Same selection vocabulary + element-centroid geometric selectors; .get() returns a GaussSlab with parent-space natural_coords ([-1,+1]) and an optional per-element shell quaternion.",
   "file": "src/apeGmsh/results/_composites.py:833",
   "flow": [
    {
     "node": "results.elements",
     "action": "GaussResultsComposite(results) constructed in ElementResultsComposite.__init__",
     "passes": "GaussResultsComposite",
     "to": "results.elements"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "GaussResultsComposite",
   "reads": [
    "src/apeGmsh/results/_composites.py:GaussResultsComposite"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.gauss.available_components",
   "label": "elements.gauss.available_components",
   "composite": "results.elements",
   "signature": "available_components(*, stage: str | None = None) -> list[str]",
   "summary": "Canonical Gauss-level component names present for a stage. Delegates to reader.available_components(sid, ResultLevel.GAUSS).",
   "file": "src/apeGmsh/results/_composites.py:947",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "reader.available_components(sid, ResultLevel.GAUSS)",
     "passes": "list[str]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "list[str] Gauss component names",
   "reads": [
    "ResultsReader.available_components"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.gauss.get",
   "label": "elements.gauss.get",
   "composite": "results.elements",
   "signature": "get(*, pg=None, label=None, selection=None, ids=None, component: str, time: TimeSlice = None, stage: str | None = None) -> GaussSlab",
   "summary": "Read a continuum Gauss-point component. Resolves stage and element ids exactly like elements.get, then delegates to reader.read_gauss returning a GaussSlab (values (T,sum_GP), element_index (sum_GP,), natural_coords (sum_GP,dim) in [-1,+1], local_axes_quaternion (E,4)|None, time (T,)). GaussSlab.global_coords(fem) maps natural→world coords.",
   "file": "src/apeGmsh/results/_composites.py:928",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.elements",
     "action": "_resolve_element_ids(pg,label,selection,ids)",
     "passes": "selectors",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "fem.elements.physical/labels/mesh_selection.element_ids via Results._fem (None = all)",
     "passes": "eids:ndarray|None",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.read_gauss(sid, component, element_ids=, time_slice=time)",
     "passes": "GaussSlab(values:(T,sum_GP), natural_coords:(sum_GP,dim))",
     "to": "results.slabs"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids=, component (required), time, stage",
   "outputs": "GaussSlab",
   "reads": [
    "FEMData.elements.* (via Results._fem)",
    "ResultsReader.read_gauss"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.gauss.select",
   "label": "elements.gauss.select (+ fibers/layers/line_stations/springs)",
   "composite": "results.elements",
   "signature": "select(*, pg=None, label=None, selection=None, ids=None, element_type: str | None = None) -> ResultChain",
   "summary": "Daisy-chainable selection shared via _ElementGeometryMixin by every element sub-composite (gauss, fibers, layers, line_stations, springs). Seeds atoms via _combine_candidates (id-for-id identical to that sub-composite's .get); no selector seeds every domain element. Returns an element-level ResultChain whose terminal .get(component=, **extra) forwards the spawning sub-composite's extra kwargs (gp_indices= for fibers; gp_indices=/layer_indices= for layers) to that sub-composite's own .get.",
   "file": "src/apeGmsh/results/_composites.py:551",
   "flow": [
    {
     "node": "results.elements",
     "action": "_combine_candidates(pg,label,selection,ids,element_type) → seed (None ⇒ fem.elements.ids)",
     "passes": "seed:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_resolve_element_ids + optional element_type intersect against Results._fem",
     "passes": "atoms:list[int] element ids",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "engine_for(results, self, 'element'); ResultChain(atoms, _engine=) bound to this sub-composite as host",
     "passes": "ResultChain (point, element level)",
     "to": "external"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids= + optional element_type= (or none → all elements)",
   "outputs": "ResultChain (element level, host = the spawning sub-composite)",
   "reads": [
    "FEMData.elements.* (via Results._fem)",
    "src/apeGmsh/results/_result_chain.py:ResultChain, engine_for"
   ],
   "writes": [
    "_apegmsh_result_chain_engine memoised on the sub-composite"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.get",
   "label": "elements.get",
   "composite": "results.elements",
   "signature": "get(*, pg=None, label=None, selection=None, ids=None, component: str, time: TimeSlice = None, stage: str | None = None) -> ElementSlab",
   "summary": "Read a per-element-node component (globalForce / localForce). Resolves stage and resolves at most one of pg=/label=/selection=/ids= to an element-id ndarray via fem.elements.physical/labels/mesh_selection (None = all), then delegates to reader.read_elements returning an ElementSlab (values (T,E,npe), element_ids (E,), time (T,)).",
   "file": "src/apeGmsh/results/_composites.py:893",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.elements",
     "action": "_resolve_element_ids resolves one of pg/label/selection/ids",
     "passes": "selectors",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "via Results._fem: fem.elements.physical.element_ids / labels.element_ids / mesh_selection.element_ids (None = all)",
     "passes": "element_ids:ndarray|None",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "broker returns resolved element id arrays (np.unique-concatenated)",
     "passes": "eids:ndarray(E,)",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.read_elements(sid, component, element_ids=, time_slice=time)",
     "passes": "ElementSlab(values:(T,E,npe), element_ids:(E,), time:(T,))",
     "to": "results.slabs"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids=, component (required), time, stage",
   "outputs": "ElementSlab",
   "reads": [
    "FEMData.elements.physical/labels/mesh_selection (via Results._fem)",
    "ResultsReader.read_elements"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.in_box",
   "label": "elements.in_box",
   "composite": "results.elements",
   "signature": "in_box(box_min, box_max, *, component: str, pg=None, label=None, selection=None, ids=None, element_type=None, time=None, stage=None) -> ElementSlab",
   "summary": "Read component at every element whose centroid lies in the half-open AABB [box_min, box_max). _combine_candidates restricts first; _element_ids_in_box intersects (fails loud if no element geometry); read via self.get(ids=narrowed,...). Inherited by all element sub-composites.",
   "file": "src/apeGmsh/results/_composites.py:460",
   "flow": [
    {
     "node": "results.elements",
     "action": "_require_fem; _combine_candidates → cand",
     "passes": "cand:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_element_ids_in_box(fem, box_min, box_max) via centroids; intersect1d with cand",
     "passes": "narrowed:ndarray",
     "to": "results.elements"
    },
    {
     "node": "results.elements",
     "action": "self.get(ids=narrowed, component, time, stage)",
     "passes": "ElementSlab(values:(T,E,npe))",
     "to": "results.slabs"
    }
   ],
   "inputs": "box_min, box_max, component (required), optional selectors + element_type, time, stage",
   "outputs": "ElementSlab (elements in box)",
   "reads": [
    "FEMData.elements.types/resolve, nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_elements"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.in_sphere",
   "label": "elements.in_sphere",
   "composite": "results.elements",
   "signature": "in_sphere(center, radius: float, *, component: str, pg=None, label=None, selection=None, ids=None, element_type=None, time=None, stage=None) -> ElementSlab",
   "summary": "Read component at every element whose centroid is within radius of center (closed ball; ValueError on negative radius). _combine_candidates restricts first; _element_ids_in_sphere intersects (fails loud if no element geometry); read via self.get(ids=narrowed,...). Inherited by all element sub-composites.",
   "file": "src/apeGmsh/results/_composites.py:488",
   "flow": [
    {
     "node": "results.elements",
     "action": "_require_fem; _combine_candidates → cand",
     "passes": "cand:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_element_ids_in_sphere(fem, center, radius) via centroids; intersect1d with cand",
     "passes": "narrowed:ndarray",
     "to": "results.elements"
    },
    {
     "node": "results.elements",
     "action": "self.get(ids=narrowed, component, time, stage)",
     "passes": "ElementSlab(values:(T,E,npe))",
     "to": "results.slabs"
    }
   ],
   "inputs": "center, radius, component (required), optional selectors + element_type, time, stage",
   "outputs": "ElementSlab (elements in sphere)",
   "reads": [
    "FEMData.elements.types/resolve, nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_elements"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.layers",
   "label": "elements.layers",
   "composite": "results.elements",
   "signature": "attribute elements.layers: LayersResultsComposite",
   "summary": "Layered-shell layer value sub-composite. Same selection vocabulary + element-centroid geometric selectors; .get() adds gp_indices= and layer_indices= and returns a LayerSlab with surface-GP / layer / through-thickness-sub-GP indices and a per-row quaternion.",
   "file": "src/apeGmsh/results/_composites.py:835",
   "flow": [
    {
     "node": "results.elements",
     "action": "LayersResultsComposite(results) constructed in ElementResultsComposite.__init__",
     "passes": "LayersResultsComposite",
     "to": "results.elements"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "LayersResultsComposite",
   "reads": [
    "src/apeGmsh/results/_composites.py:LayersResultsComposite"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.layers.available_components",
   "label": "elements.layers.available_components",
   "composite": "results.elements",
   "signature": "available_components(*, stage: str | None = None) -> list[str]",
   "summary": "Canonical layer-level component names present for a stage. Delegates to reader.available_components(sid, ResultLevel.LAYERS).",
   "file": "src/apeGmsh/results/_composites.py:1033",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "reader.available_components(sid, ResultLevel.LAYERS)",
     "passes": "list[str]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "list[str] layer component names",
   "reads": [
    "ResultsReader.available_components"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.layers.get",
   "label": "elements.layers.get",
   "composite": "results.elements",
   "signature": "get(*, pg=None, label=None, selection=None, ids=None, gp_indices=None, layer_indices=None, component: str, time: TimeSlice = None, stage: str | None = None) -> LayerSlab",
   "summary": "Read a layered-shell layer component. Resolves stage and element ids like elements.get; gp_indices= (surface GPs) and layer_indices= (optional, cast to int64) narrow rows. Delegates to reader.read_layers returning a LayerSlab (values (T,sum_L); element_index/gp_index/layer_index/sub_gp_index/thickness (sum_L,); local_axes_quaternion (sum_L,4); time (T,)).",
   "file": "src/apeGmsh/results/_composites.py:1002",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.elements",
     "action": "_resolve_element_ids; gp_indices / layer_indices → int64 ndarray|None",
     "passes": "selectors, gpi, lyr",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "fem.elements.* via Results._fem (None = all elements)",
     "passes": "eids:ndarray|None",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.read_layers(sid, component, element_ids=, gp_indices=gpi, layer_indices=lyr, time_slice=time)",
     "passes": "LayerSlab(values:(T,sum_L))",
     "to": "results.slabs"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids=, optional gp_indices=/layer_indices=, component (required), time, stage",
   "outputs": "LayerSlab",
   "reads": [
    "FEMData.elements.* (via Results._fem)",
    "ResultsReader.read_layers"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.line_stations",
   "label": "elements.line_stations",
   "composite": "results.elements",
   "signature": "attribute elements.line_stations: LineStationsResultsComposite",
   "summary": "Beam section-force-along-length sub-composite. Same selection vocabulary + element-centroid geometric selectors; .get() returns a LineStationSlab with one row per integration station (station_natural_coord in [-1,+1]).",
   "file": "src/apeGmsh/results/_composites.py:836",
   "flow": [
    {
     "node": "results.elements",
     "action": "LineStationsResultsComposite(results) constructed in ElementResultsComposite.__init__",
     "passes": "LineStationsResultsComposite",
     "to": "results.elements"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "LineStationsResultsComposite",
   "reads": [
    "src/apeGmsh/results/_composites.py:LineStationsResultsComposite"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.line_stations.available_components",
   "label": "elements.line_stations.available_components",
   "composite": "results.elements",
   "signature": "available_components(*, stage: str | None = None) -> list[str]",
   "summary": "Canonical line-station component names present for a stage. Delegates to reader.available_components(sid, ResultLevel.LINE_STATIONS).",
   "file": "src/apeGmsh/results/_composites.py:1067",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "reader.available_components(sid, ResultLevel.LINE_STATIONS)",
     "passes": "list[str]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "list[str] line-station component names",
   "reads": [
    "ResultsReader.available_components"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.line_stations.get",
   "label": "elements.line_stations.get",
   "composite": "results.elements",
   "signature": "get(*, pg=None, label=None, selection=None, ids=None, component: str, time: TimeSlice = None, stage: str | None = None) -> LineStationSlab",
   "summary": "Read a beam line-diagram component (section forces along the length). Resolves stage and element ids like elements.get, then delegates to reader.read_line_stations returning a LineStationSlab (values (T,sum_S), element_index/station_natural_coord (sum_S,), time (T,)).",
   "file": "src/apeGmsh/results/_composites.py:1048",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.elements",
     "action": "_resolve_element_ids(pg,label,selection,ids)",
     "passes": "selectors",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "fem.elements.* via Results._fem (None = all elements)",
     "passes": "eids:ndarray|None",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.read_line_stations(sid, component, element_ids=, time_slice=time)",
     "passes": "LineStationSlab(values:(T,sum_S))",
     "to": "results.slabs"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids=, component (required), time, stage",
   "outputs": "LineStationSlab",
   "reads": [
    "FEMData.elements.* (via Results._fem)",
    "ResultsReader.read_line_stations"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.nearest_to",
   "label": "elements.nearest_to",
   "composite": "results.elements",
   "signature": "nearest_to(point, *, component: str, pg=None, label=None, selection=None, ids=None, element_type=None, time=None, stage=None) -> ElementSlab",
   "summary": "Read component at the single element whose centroid is nearest point (mean of node coords). _combine_candidates (named selectors + element_type) restricts first; _nearest_element_id picks the closest centroid (fails loud if the bound FEMData carries no resolvable element geometry); read via self.get(ids=[eid],...). Inherited by all element sub-composites.",
   "file": "src/apeGmsh/results/_composites.py:437",
   "flow": [
    {
     "node": "results.elements",
     "action": "_require_fem; _combine_candidates → cand",
     "passes": "cand:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_nearest_element_id(fem, point, candidate_ids=) via _element_centroids (fail-loud on missing element geometry)",
     "passes": "eid:int",
     "to": "results.elements"
    },
    {
     "node": "results.elements",
     "action": "self.get(ids=[eid], component, time, stage)",
     "passes": "ElementSlab(values:(T,1,npe))",
     "to": "results.slabs"
    }
   ],
   "inputs": "point, component (required), optional selectors + element_type, time, stage",
   "outputs": "ElementSlab (single element)",
   "reads": [
    "FEMData.elements.types/resolve, nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_elements"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.on_plane",
   "label": "elements.on_plane",
   "composite": "results.elements",
   "signature": "on_plane(point_on_plane, normal, tolerance: float, *, component: str, pg=None, label=None, selection=None, ids=None, element_type=None, time=None, stage=None) -> ElementSlab",
   "summary": "Read component at every element whose centroid is within tolerance of the plane (point + normal, normalised internally). _combine_candidates restricts first; _element_ids_on_plane intersects (fails loud if no element geometry); read via self.get(ids=narrowed,...). Inherited by all element sub-composites.",
   "file": "src/apeGmsh/results/_composites.py:516",
   "flow": [
    {
     "node": "results.elements",
     "action": "_require_fem; _combine_candidates → cand",
     "passes": "cand:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_element_ids_on_plane(fem, point, normal, tol) via centroids; intersect1d with cand",
     "passes": "narrowed:ndarray",
     "to": "results.elements"
    },
    {
     "node": "results.elements",
     "action": "self.get(ids=narrowed, component, time, stage)",
     "passes": "ElementSlab(values:(T,E,npe))",
     "to": "results.slabs"
    }
   ],
   "inputs": "point_on_plane, normal, tolerance, component (required), optional selectors + element_type, time, stage",
   "outputs": "ElementSlab (elements on plane)",
   "reads": [
    "FEMData.elements.types/resolve, nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_elements"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.select",
   "label": "elements.select",
   "composite": "results.elements",
   "signature": "select(*, pg=None, label=None, selection=None, ids=None, element_type: str | None = None) -> ResultChain",
   "summary": "Start a daisy-chainable element-result selection (point family, element level; spatial verbs operate on element centroids). Seeds atoms via _combine_candidates (additive pg/label/selection/ids + element_type, id-for-id identical to .get); no selector seeds every domain element (fem.elements.ids). Returns a ResultChain on a stable per-composite engine; centroids are computed fail-loud inside ResultChain.",
   "file": "src/apeGmsh/results/_composites.py:839",
   "flow": [
    {
     "node": "results.elements",
     "action": "_combine_candidates(pg,label,selection,ids,element_type) → seed (None ⇒ fem.elements.ids)",
     "passes": "seed:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_resolve_element_ids + optional _element_ids_of_type intersect against Results._fem",
     "passes": "atoms:list[int] element ids",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "engine_for(results, self, 'element') memoised engine; ResultChain(atoms, _engine=)",
     "passes": "ResultChain (point, element level)",
     "to": "external"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids= + optional element_type= (or none → all elements)",
   "outputs": "ResultChain (element level)",
   "reads": [
    "FEMData.elements.* (via Results._fem)",
    "src/apeGmsh/results/_result_chain.py:ResultChain, engine_for"
   ],
   "writes": [
    "_apegmsh_result_chain_engine memoised on the composite"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.springs",
   "label": "elements.springs",
   "composite": "results.elements",
   "signature": "attribute elements.springs: SpringsResultsComposite",
   "summary": "ZeroLength spring force/deformation sub-composite. Same selection vocabulary + element-centroid geometric selectors; .get() returns a SpringSlab for one spring direction with element_index carrying the raw OpenSees element tag.",
   "file": "src/apeGmsh/results/_composites.py:837",
   "flow": [
    {
     "node": "results.elements",
     "action": "SpringsResultsComposite(results) constructed in ElementResultsComposite.__init__",
     "passes": "SpringsResultsComposite",
     "to": "results.elements"
    }
   ],
   "inputs": "(accessor; no call)",
   "outputs": "SpringsResultsComposite",
   "reads": [
    "src/apeGmsh/results/_composites.py:SpringsResultsComposite"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.springs.available_components",
   "label": "elements.springs.available_components",
   "composite": "results.elements",
   "signature": "available_components(*, stage: str | None = None) -> list[str]",
   "summary": "Canonical spring component names present for a stage (e.g. spring_force_0, spring_deformation_2). Delegates to reader.available_components(sid, ResultLevel.SPRINGS).",
   "file": "src/apeGmsh/results/_composites.py:1114",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "reader.available_components(sid, ResultLevel.SPRINGS)",
     "passes": "list[str]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "list[str] spring component names",
   "reads": [
    "ResultsReader.available_components"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.elements.springs.get",
   "label": "elements.springs.get",
   "composite": "results.elements",
   "signature": "get(*, pg=None, label=None, selection=None, ids=None, component: str, time: TimeSlice = None, stage: str | None = None) -> SpringSlab",
   "summary": "Read one ZeroLength spring direction's force or deformation. component is a canonical name like 'spring_force_0' / 'spring_deformation_2' (use available_components()). Resolves stage and element ids like elements.get, then delegates to reader.read_springs returning a SpringSlab (values (T,E), element_index (E,) = raw OpenSees tags, time (T,)).",
   "file": "src/apeGmsh/results/_composites.py:1084",
   "flow": [
    {
     "node": "results.elements",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.elements",
     "action": "_resolve_element_ids(pg,label,selection,ids)",
     "passes": "selectors",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "fem.elements.* via Results._fem (None = all elements)",
     "passes": "eids:ndarray|None",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.read_springs(sid, component, element_ids=, time_slice=time)",
     "passes": "SpringSlab(values:(T,E), element_index:(E,))",
     "to": "results.slabs"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids=, component (required, e.g. spring_force_0), time, stage",
   "outputs": "SpringSlab",
   "reads": [
    "FEMData.elements.* (via Results._fem)",
    "ResultsReader.read_springs"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.from_mpco",
   "label": "from_mpco",
   "composite": "results",
   "signature": "Results.from_mpco(path, *, fem=None, merge_partitions=True) -> Results",
   "summary": "Open a STKO .mpco HDF5 results file (single-file or auto-discovered multi-partition) via MPCOReader/MPCOMultiPartitionReader; synthesizes a partial FEMData from /MODEL/ when fem is omitted.",
   "file": "src/apeGmsh/results/Results.py:210",
   "flow": [
    {
     "node": "results",
     "action": "discover_partition_files (if merge), pick MPCOReader or MPCOMultiPartitionReader, resolve_bound_fem",
     "passes": "<path(s), fem:FEMData|None>",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "MPCO reader synthesizes FEMData from /MODEL/; return bound Results",
     "passes": "<Results>",
     "to": "results"
    }
   ],
   "inputs": "mpco path or list, optional fem, merge_partitions flag",
   "outputs": "Results instance bound to a (synthesized) FEMData",
   "reads": [
    "STKO .mpco HDF5 file(s)"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.from_native",
   "label": "from_native",
   "composite": "results",
   "signature": "Results.from_native(path, *, fem=None) -> Results",
   "summary": "Open an apeGmsh native HDF5 results file via NativeReader; binds the embedded /model/ FEMData when fem is omitted (snapshot_id parity checked if both present).",
   "file": "src/apeGmsh/results/Results.py:112",
   "flow": [
    {
     "node": "results",
     "action": "construct NativeReader(path), resolve_bound_fem(reader, fem)",
     "passes": "<path, fem:FEMData|None>",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "NativeReader reconstructs FEMData from /model/; return bound Results",
     "passes": "<Results>",
     "to": "results"
    }
   ],
   "inputs": "native HDF5 path, optional fem",
   "outputs": "Results instance bound to a FEMData",
   "reads": [
    "native HDF5 file"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.from_recorders",
   "label": "from_recorders",
   "composite": "results",
   "signature": "Results.from_recorders(spec, output_dir, *, fem, cache_root=None, stage_name='analysis', stage_kind='transient', file_format='out', stage_id=None) -> Results",
   "summary": "Open the result of a Tcl/Py-recorder-driven OpenSees run: enumerate emitted files, hash them with PARSER_VERSION + fem.snapshot_id into a cache key, run RecorderTranscoder into a cached native HDF5 if absent, then open via from_native. This is the public driver of the transcode→writers→cache flow.",
   "file": "src/apeGmsh/results/Results.py:131",
   "flow": [
    {
     "node": "results",
     "action": "_cache.resolve_cache_root + list_source_files(spec) + compute_cache_key(parser_version, fem.snapshot_id)",
     "passes": "<source_files:list[Path], parser_version, snapshot_id>",
     "to": "results.writers"
    },
    {
     "node": "results.writers",
     "action": "_cache.cache_paths -> cached_h5; if missing instantiate RecorderTranscoder and run()",
     "passes": "<cached_h5:Path>",
     "to": "results.transcode"
    },
    {
     "node": "results.transcode",
     "action": "RecorderTranscoder.run parses .out files and writes the native HDF5 via NativeWriter",
     "passes": "<target_path:Path>",
     "to": "results.writers"
    },
    {
     "node": "results",
     "action": "Results.from_native(cached_h5, fem) opens it through NativeReader",
     "passes": "<cached_h5:Path, fem:FEMData>",
     "to": "results.readers"
    }
   ],
   "inputs": "ResolvedRecorderSpec, recorder output_dir, fem, cache/stage options",
   "outputs": "Results bound to the (cached) native HDF5",
   "reads": [
    "recorder .out files",
    "spec.records",
    "fem.snapshot_id",
    "cache dir"
   ],
   "writes": [
    "cached native HDF5 (on cache miss, via RecorderTranscoder/NativeWriter)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.inspect.components",
   "label": "inspect.components",
   "composite": "results.inspect",
   "signature": "components(*, stage: str | None = None) -> dict[str, list[str]]",
   "summary": "Available component names per topology level for one stage, as {level.value: [names]} over every ResultLevel (nodes/elements/line_stations/gauss/fibers/layers/springs). Stage resolved as elsewhere (sole stage default else raise).",
   "file": "src/apeGmsh/results/_inspect.py:54",
   "flow": [
    {
     "node": "results.inspect",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "for each ResultLevel: reader.available_components(sid, level)",
     "passes": "dict[str,list[str]]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "dict[str, list[str]] (level → component names)",
   "reads": [
    "ResultsReader.available_components",
    "ResultLevel enum"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.inspect.diagnose",
   "label": "inspect.diagnose",
   "composite": "results.inspect",
   "signature": "diagnose(component: str, *, stage: str | None = None) -> str",
   "summary": "Explain where a component lives (or doesn't) in a stage — the routing-side 'why is the slab empty?' report. Walks every ResultLevel calling reader.available_components, marks where component was FOUND, previews up to 6 names per level, lists per-level errors, and appends remediation tips when not found.",
   "file": "src/apeGmsh/results/_inspect.py:68",
   "flow": [
    {
     "node": "results.inspect",
     "action": "_r._resolve_stage(stage) (returns an explanatory string on failure rather than raising)",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "for each ResultLevel: reader.available_components(sid, level), test membership of component",
     "passes": "per-level [names] + match flags",
     "to": "results.inspect"
    },
    {
     "node": "results.inspect",
     "action": "format FOUND/NOT-FOUND + previews + errors + tips",
     "passes": "report:str",
     "to": "external"
    }
   ],
   "inputs": "component (required), stage (optional)",
   "outputs": "str multi-line diagnostic report",
   "reads": [
    "ResultsReader.available_components",
    "ResultLevel enum"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.inspect.summary",
   "label": "inspect.summary",
   "composite": "results.inspect",
   "signature": "summary() -> str",
   "summary": "Multi-line human-readable summary: reader path; bound-FEM node count + total element count + snapshot_id (or 'FEM: not bound'); and a per-stage listing (id, name, steps, kind, plus f/T/mode_index for modes). Also the repr of both Results and ResultsInspect.",
   "file": "src/apeGmsh/results/_inspect.py:22",
   "flow": [
    {
     "node": "results.inspect",
     "action": "read r._reader_path(), r.fem (node/element counts + snapshot_id), r.stages",
     "passes": "(none)",
     "to": "results"
    },
    {
     "node": "results",
     "action": "_all_stages() (cached reader.stages()) supplies the per-stage StageInfo list",
     "passes": "list[StageInfo]",
     "to": "results.inspect"
    },
    {
     "node": "results.inspect",
     "action": "join the formatted lines",
     "passes": "summary:str",
     "to": "external"
    }
   ],
   "inputs": "(none)",
   "outputs": "str multi-line summary",
   "reads": [
    "Results._reader_path()",
    "Results.fem (FEMData.nodes.ids/elements, snapshot_id)",
    "ResultsReader.stages()"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.live.LiveMPCO",
   "label": "LiveMPCO",
   "composite": "results.live",
   "signature": "LiveMPCO(spec: ResolvedRecorderSpec, path, *, ops=None)",
   "summary": "Public live-capture entry (via spec.emit_mpco) — context manager owning a single in-process MPCO recorder; __enter__ issues ops.recorder('mpco', ...), __exit__ removes it (flushing the .mpco HDF5). Raises with remediation if the MPCO recorder is unavailable.",
   "file": "src/apeGmsh/results/live/_mpco.py:53",
   "flow": [
    {
     "node": "results.live",
     "action": "__enter__ lazily imports openseespy, mkdirs parent, mpco_ops_args(spec.records) -> tuple, ops.recorder(*args)",
     "passes": "<ResolvedRecorderSpec.records, path>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "mpco_ops_args returns the ('mpco', path, -N..., -E..., -T...) tuple",
     "passes": "<mpco args tuple>",
     "to": "results.live"
    },
    {
     "node": "results.live",
     "action": "ops.recorder(*args); __exit__ ops.remove('recorder', tag) flushes the file",
     "passes": "<mpco recorder args>",
     "to": "external"
    }
   ],
   "inputs": "ResolvedRecorderSpec, .mpco path, optional ops",
   "outputs": "LiveMPCO context manager",
   "reads": [
    "ResolvedRecorderSpec.records"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.live.LiveRecorders",
   "label": "LiveRecorders",
   "composite": "results.live",
   "signature": "LiveRecorders(spec: ResolvedRecorderSpec, output_dir, *, file_format='out', ops=None)",
   "summary": "Public live-capture entry (via spec.emit_recorders) — context manager owning stage-scoped classic OpenSees recorders; begin_stage issues them with `<name>__` filename prefixes, end_stage removes them (flushing files). Modal records raise on __enter__.",
   "file": "src/apeGmsh/results/live/_recorders.py:90",
   "flow": [
    {
     "node": "results.live",
     "action": "__enter__ validates no modal records, lazily imports openseespy, mkdirs output_dir",
     "passes": "<ResolvedRecorderSpec, output_dir>",
     "to": "external"
    }
   ],
   "inputs": "ResolvedRecorderSpec, output_dir, file_format, optional ops",
   "outputs": "LiveRecorders context manager",
   "reads": [
    "ResolvedRecorderSpec.records"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.live.LiveRecorders.begin_stage",
   "label": "begin_stage",
   "composite": "results.live",
   "signature": "begin_stage(name: str, kind: str = 'transient') -> None",
   "summary": "Issue a fresh set of ops.recorder(...) calls for a new stage with `<name>__`-prefixed output files; supported categories emitted, fibers/layers warn-and-skip, modal skipped.",
   "file": "src/apeGmsh/results/live/_recorders.py:165",
   "flow": [
    {
     "node": "results.live",
     "action": "per record emit_logical(stage_id=name) -> LogicalRecorder, to_ops_args -> tuple",
     "passes": "<record, stage_id=name>",
     "to": "os.emit"
    },
    {
     "node": "os.emit",
     "action": "to_ops_args returns the ops.recorder argument tuple",
     "passes": "<ops args tuple>",
     "to": "results.live"
    },
    {
     "node": "results.live",
     "action": "ops.recorder(*args), retain returned tags",
     "passes": "<recorder args>",
     "to": "external"
    }
   ],
   "inputs": "stage name, kind",
   "outputs": "None (recorders issued into the running domain)",
   "reads": [
    "ResolvedRecorderSpec.records"
   ],
   "writes": [
    "LiveRecorders._current_stage/_current_tags"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.live.LiveRecorders.end_stage",
   "label": "end_stage",
   "composite": "results.live",
   "signature": "end_stage() -> None",
   "summary": "Remove the current stage's recorders (which flushes their .out/.xml files) and record the completed stage.",
   "file": "src/apeGmsh/results/live/_recorders.py:226",
   "flow": [
    {
     "node": "results.live",
     "action": "ops.remove('recorder', tag) for each current tag (best-effort), append a completed StageRecord",
     "passes": "<recorder tags>",
     "to": "external"
    }
   ],
   "inputs": "none",
   "outputs": "None (files flushed, stage closed)",
   "reads": [
    "LiveRecorders._current_tags/_current_stage"
   ],
   "writes": [
    "LiveRecorders._stages",
    "LiveRecorders._current_stage:=None"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.nodes.available_components",
   "label": "nodes.available_components",
   "composite": "results.nodes",
   "signature": "available_components(*, stage: str | None = None) -> list[str]",
   "summary": "Canonical node-level component names present for a stage (resolved as in .get). Delegates to reader.available_components(sid, ResultLevel.NODES).",
   "file": "src/apeGmsh/results/_composites.py:699",
   "flow": [
    {
     "node": "results.nodes",
     "action": "_r._resolve_stage(stage) → sid",
     "passes": "sid:str",
     "to": "results"
    },
    {
     "node": "results.readers",
     "action": "reader.available_components(sid, ResultLevel.NODES)",
     "passes": "list[str]",
     "to": "external"
    }
   ],
   "inputs": "stage (optional)",
   "outputs": "list[str] node component names",
   "reads": [
    "ResultsReader.available_components"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.nodes.get",
   "label": "nodes.get",
   "composite": "results.nodes",
   "signature": "get(*, pg=None, label=None, selection=None, ids=None, component: str, time: TimeSlice = None, stage: str | None = None) -> NodeSlab",
   "summary": "Read one component at a node selection. Resolves the stage (explicit stage= → owning Results._stage_id → the only stage → RuntimeError on ambiguity), resolves at most one of pg=/label=/selection=/ids= to a node-id ndarray by binding to the embedded FEMData (fem.nodes.physical/labels/mesh_selection node_ids; None = all nodes; ValueError if multiple selectors), then delegates to reader.read_nodes returning a NodeSlab (values (T,N), node_ids (N,), time (T,)).",
   "file": "src/apeGmsh/results/_composites.py:680",
   "flow": [
    {
     "node": "results.nodes",
     "action": "_r._resolve_stage(stage) → stage_id (explicit / scoped / sole / raise)",
     "passes": "sid:str (stage_id)",
     "to": "results"
    },
    {
     "node": "results.nodes",
     "action": "_resolve_node_ids resolves one of pg/label/selection/ids",
     "passes": "selectors",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "via Results._fem: fem.nodes.physical.node_ids / labels.node_ids / mesh_selection.node_ids (None = all)",
     "passes": "node_ids:ndarray(N,)|None",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "broker returns the resolved node id arrays (np.unique-concatenated)",
     "passes": "node_ids:ndarray(N,)",
     "to": "results.readers"
    },
    {
     "node": "results.readers",
     "action": "reader.read_nodes(sid, component, node_ids=, time_slice=time) reads the slab",
     "passes": "NodeSlab(values:(T,N), node_ids:(N,), time:(T,))",
     "to": "results.slabs"
    },
    {
     "node": "results.slabs",
     "action": "return the frozen NodeSlab dataclass",
     "passes": "NodeSlab",
     "to": "external"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids=, component (required), time, stage",
   "outputs": "NodeSlab",
   "reads": [
    "FEMData.nodes.physical/labels/mesh_selection (via Results._fem)",
    "ResultsReader.read_nodes"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.nodes.in_box",
   "label": "nodes.in_box",
   "composite": "results.nodes",
   "signature": "in_box(box_min, box_max, *, component: str, pg=None, label=None, selection=None, ids=None, time=None, stage=None) -> NodeSlab",
   "summary": "Read component at every node inside the half-open AABB [box_min, box_max) (upper bound exclusive so adjacent boxes don't double-count; use ±inf to relax an axis). Named selectors restrict candidates first; _node_ids_in_box intersects; read via self.get(ids=narrowed,...).",
   "file": "src/apeGmsh/results/_composites.py:729",
   "flow": [
    {
     "node": "results.nodes",
     "action": "_require_fem; _resolve_node_ids → candidate",
     "passes": "candidate:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_node_ids_in_box(fem, box_min, box_max) on fem.nodes.coords; intersect1d with candidate",
     "passes": "narrowed:ndarray",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "self.get(ids=narrowed, component, time, stage)",
     "passes": "NodeSlab(values:(T,N))",
     "to": "results.slabs"
    }
   ],
   "inputs": "box_min, box_max, component (required), optional selectors, time, stage",
   "outputs": "NodeSlab (nodes in box)",
   "reads": [
    "FEMData.nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_nodes"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.nodes.in_sphere",
   "label": "nodes.in_sphere",
   "composite": "results.nodes",
   "signature": "in_sphere(center, radius: float, *, component: str, pg=None, label=None, selection=None, ids=None, time=None, stage=None) -> NodeSlab",
   "summary": "Read component at every node within radius of center (closed ball; ValueError on negative radius). Named selectors restrict candidates first; _node_ids_in_sphere intersects; read via self.get(ids=narrowed,...).",
   "file": "src/apeGmsh/results/_composites.py:761",
   "flow": [
    {
     "node": "results.nodes",
     "action": "_require_fem; _resolve_node_ids → candidate",
     "passes": "candidate:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_node_ids_in_sphere(fem, center, radius) on fem.nodes.coords; intersect1d with candidate",
     "passes": "narrowed:ndarray",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "self.get(ids=narrowed, component, time, stage)",
     "passes": "NodeSlab(values:(T,N))",
     "to": "results.slabs"
    }
   ],
   "inputs": "center, radius, component (required), optional selectors, time, stage",
   "outputs": "NodeSlab (nodes in sphere)",
   "reads": [
    "FEMData.nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_nodes"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.nodes.nearest_to",
   "label": "nodes.nearest_to",
   "composite": "results.nodes",
   "signature": "nearest_to(point, *, component: str, pg=None, label=None, selection=None, ids=None, time=None, stage=None) -> NodeSlab",
   "summary": "Read component at the single node nearest point (3D Euclidean). Named selectors first restrict the candidate set (additive), then _nearest_node_id picks the closest from fem.nodes.coords; result is read via self.get(ids=[nid], ...). Requires a bound FEMData.",
   "file": "src/apeGmsh/results/_composites.py:707",
   "flow": [
    {
     "node": "results.nodes",
     "action": "_require_fem; _resolve_node_ids → candidate set",
     "passes": "candidate:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_nearest_node_id(fem, point, candidate_ids=) argmin over fem.nodes.coords",
     "passes": "nid:int",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "self.get(ids=[nid], component, time, stage)",
     "passes": "NodeSlab(values:(T,1))",
     "to": "results.slabs"
    }
   ],
   "inputs": "point, component (required), optional candidate selectors, time, stage",
   "outputs": "NodeSlab (single node)",
   "reads": [
    "FEMData.nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_nodes"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.nodes.on_plane",
   "label": "nodes.on_plane",
   "composite": "results.nodes",
   "signature": "on_plane(point_on_plane, normal, tolerance: float, *, component: str, pg=None, label=None, selection=None, ids=None, time=None, stage=None) -> NodeSlab",
   "summary": "Read component at every node within tolerance of the plane (point + normal, normal normalised internally; perpendicular distance via signed dot). Named selectors restrict candidates first; _node_ids_on_plane intersects; read via self.get(ids=narrowed,...). Useful for story-level / mid-span cuts.",
   "file": "src/apeGmsh/results/_composites.py:788",
   "flow": [
    {
     "node": "results.nodes",
     "action": "_require_fem; _resolve_node_ids → candidate",
     "passes": "candidate:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "_node_ids_on_plane(fem, point, normal, tol) on fem.nodes.coords; intersect1d with candidate",
     "passes": "narrowed:ndarray",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "self.get(ids=narrowed, component, time, stage)",
     "passes": "NodeSlab(values:(T,N))",
     "to": "results.slabs"
    }
   ],
   "inputs": "point_on_plane, normal, tolerance, component (required), optional selectors, time, stage",
   "outputs": "NodeSlab (nodes on plane)",
   "reads": [
    "FEMData.nodes.coords/ids (via Results._fem)",
    "ResultsReader.read_nodes"
   ],
   "writes": [],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.nodes.select",
   "label": "nodes.select",
   "composite": "results.nodes",
   "signature": "select(*, pg=None, label=None, selection=None, ids=None) -> ResultChain",
   "summary": "Start a daisy-chainable node-result selection (point family, node level). Seeds atoms from _resolve_node_ids (id-for-id identical to .get's resolution); no selector seeds every domain node (fem.nodes.ids). Returns a ResultChain bound to a stable per-composite engine adapter; refine with in_box/in_sphere/on_plane/nearest_to/where + set algebra, then terminal .get(component=).",
   "file": "src/apeGmsh/results/_composites.py:631",
   "flow": [
    {
     "node": "results.nodes",
     "action": "_resolve_node_ids(pg,label,selection,ids) → seed (None ⇒ fem.nodes.ids via _require_fem)",
     "passes": "seed:ndarray|None",
     "to": "results.bind"
    },
    {
     "node": "results.bind",
     "action": "resolve names against Results._fem (broker node_ids)",
     "passes": "atoms:list[int] node ids",
     "to": "results.chain"
    },
    {
     "node": "results.chain",
     "action": "engine_for(results, self, 'node') memoised _ResultChainEngine; ResultChain(atoms, _engine=)",
     "passes": "ResultChain (point, node level)",
     "to": "external"
    }
   ],
   "inputs": "one of pg=/label=/selection=/ids= (or none → all nodes)",
   "outputs": "ResultChain (node level)",
   "reads": [
    "FEMData.nodes.* (via Results._fem)",
    "src/apeGmsh/results/_result_chain.py:ResultChain, engine_for"
   ],
   "writes": [
    "_apegmsh_result_chain_engine memoised on the composite"
   ],
   "_cluster": "H_res_core"
  },
  {
   "id": "results.plot",
   "label": "plot",
   "composite": "results",
   "signature": "Results.plot -> ResultsPlot",
   "summary": "Property accessor on Results that lazily constructs and returns the ResultsPlot composite — the static matplotlib renderer mirroring the interactive viewer's diagram catalog for headless/publication figures.",
   "file": "src/apeGmsh/results/Results.py:376",
   "flow": [
    {
     "node": "results",
     "action": "lazily instantiate ResultsPlot bound to self on first access, cache it",
     "passes": "<ResultsPlot>",
     "to": "results.plot"
    }
   ],
   "inputs": "none (property)",
   "outputs": "ResultsPlot instance (cached on Results._plot)",
   "reads": [
    "Results._plot"
   ],
   "writes": [
    "Results._plot"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.contour",
   "label": "contour",
   "composite": "results.plot",
   "signature": "contour(component, *, step=-1, stage=None, ax=None, cmap='viridis', clim=None, edge_color='k', linewidth=0.1, alpha=1.0, scalar_bar=True, deformed=False, scale=1.0, wireframe=False, wireframe_color='k', title=None) -> Axes3D",
   "summary": "Paint a nodal scalar component on the mesh surface (and 1-D elements) at one step, per-face flat shaded by the mean of vertex scalars; optional displacement warp and undeformed ghost wireframe.",
   "file": "src/apeGmsh/results/plot/_plot.py:151",
   "flow": [
    {
     "node": "results.plot",
     "action": "read the nodal scalar at one step via _read_node_scalars -> Results.nodes.get(component, time=step, stage)",
     "passes": "<component:str, step:int, stage:str|None>",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "return a NodeSlab; scatter slab.values[0] into an (N,) array parallel to fem.nodes.ids (NaN fill)",
     "passes": "<NodeSlab>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "pull cached facet tables; if deformed=True, _deformed_coords reads displacement_x/y/z slabs and warps",
     "passes": "<node_values:ndarray(N,), coords:ndarray(N,3)>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "build Poly3DCollection/Line3DCollection with face means, set cmap+Normalize, add colorbar, autoscale",
     "passes": "<Axes3D>",
     "to": "viz.plot"
    }
   ],
   "inputs": "component (canonical nodal name), step, stage, styling; bound FEMData",
   "outputs": "matplotlib Axes3D with scalar-painted surface + scalar bar",
   "reads": [
    "Results.nodes (NodeSlab)",
    "Results._fem",
    "ResultsPlot._facet_cache",
    "fem.nodes.ids"
   ],
   "writes": [
    "ResultsPlot._facet_cache (first call)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.deformed",
   "label": "deformed",
   "composite": "results.plot",
   "signature": "deformed(*, step=-1, scale=1.0, stage=None, ax=None, component=None, cmap='viridis', clim=None, deformed_color=..., edge_color='k', linewidth=0.3, ghost=True) -> Axes3D",
   "summary": "Plot the displacement-warped deformed shape with an optional undeformed ghost overlay; when component= is given it delegates to contour(deformed=True) with a wireframe ghost.",
   "file": "src/apeGmsh/results/plot/_plot.py:301",
   "flow": [
    {
     "node": "results.plot",
     "action": "if component is not None delegate to self.contour(component, deformed=True, scale, wireframe=ghost)",
     "passes": "<component:str, scale:float, step:int>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "else pull cached facets and _deformed_coords (reads displacement_x/y/z slabs via Results.nodes.get)",
     "passes": "<component:'displacement_*', step:int, stage:str|None>",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "return displacement NodeSlabs; compute coords + scale*warp",
     "passes": "<NodeSlab x3>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "draw ghost (undeformed) + deformed Poly3D/Line3D collections, autoscale to union bbox",
     "passes": "<Axes3D>",
     "to": "viz.plot"
    }
   ],
   "inputs": "step, scale, stage, optional component (scalar overlay); bound FEMData",
   "outputs": "matplotlib Axes3D with deformed shape (optionally scalar-painted)",
   "reads": [
    "Results.nodes (displacement NodeSlabs)",
    "Results._fem",
    "ResultsPlot._facet_cache"
   ],
   "writes": [
    "ResultsPlot._facet_cache (first call)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.figsize",
   "label": "figsize",
   "composite": "results.plot",
   "signature": "figsize(size: tuple[float, float]) -> ResultsPlot",
   "summary": "Set the default figure size for axes this composite creates when no ax= is passed; returns self for chaining.",
   "file": "src/apeGmsh/results/plot/_plot.py:107",
   "flow": [
    {
     "node": "results.plot",
     "action": "store (w,h) as the default figsize for subsequently created figures",
     "passes": "<figsize:tuple[float,float]>",
     "to": "results.plot"
    }
   ],
   "inputs": "size (w, h) in inches",
   "outputs": "self (ResultsPlot) for chaining",
   "reads": [],
   "writes": [
    "ResultsPlot._figsize"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.history",
   "label": "history",
   "composite": "results.plot",
   "signature": "history(node=None, *, component, point=None, stage=None, ax=None, label=None, **plot_kwargs) -> Axes",
   "summary": "Plot one node's component vs time on a 2-D axes; selects the node by id or by snapping a point= xyz to the nearest node via Results.nodes.nearest_to.",
   "file": "src/apeGmsh/results/plot/_plot.py:381",
   "flow": [
    {
     "node": "results.plot",
     "action": "require exactly one of node=/point=; for point= call Results.nodes.nearest_to(point, component, stage)",
     "passes": "<point:tuple[float,float,float], component:str>",
     "to": "results.nodes"
    },
    {
     "node": "results.plot",
     "action": "for node= call Results.nodes.get(ids=[node], component, stage)",
     "passes": "<ids:list[int], component:str>",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "return a single-node NodeSlab (values (T,1), time (T,), node_ids)",
     "passes": "<NodeSlab>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "plot series vs slab.time on a 2-D Axes with legend/grid",
     "passes": "<Axes>",
     "to": "viz.plot"
    }
   ],
   "inputs": "node id OR point xyz, component, stage, matplotlib plot kwargs",
   "outputs": "2-D matplotlib Axes with the time history line",
   "reads": [
    "Results.nodes (NodeSlab via get / nearest_to)"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.line_force",
   "label": "line_force",
   "composite": "results.plot",
   "signature": "line_force(component, *, step=-1, stage=None, ax=None, ids=None, pg=None, label=None, scale=None, target_frac=0.10, axis=None, color=..., fill_alpha=0.45, edge_linewidth=1.2, with_mesh=True) -> Axes3D",
   "summary": "Beam internal-force / strain diagram along element length — pulls line-station data via Results.elements.line_stations.get and renders the classic envelope-plus-filled-trapezoid diagram oriented in each beam's local frame.",
   "file": "src/apeGmsh/results/plot/_plot.py:718",
   "flow": [
    {
     "node": "results.plot",
     "action": "require bound FEMData; select beams and read stations via Results.elements.line_stations.get(component, time=step, stage, ids/pg/label)",
     "passes": "<component:str, step:int, ids|pg|label>",
     "to": "results.elements"
    },
    {
     "node": "results.elements",
     "action": "return a LineStationSlab (values, element_index, station_natural_coord)",
     "passes": "<LineStationSlab>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "build per-beam local axes from FEM end-node coords, compute station positions + scaled offsets",
     "passes": "<fem:FEMData>",
     "to": "femdata"
    },
    {
     "node": "results.plot",
     "action": "add Line3DCollection envelope + Poly3DCollection fill, autoscale to mesh+tips",
     "passes": "<Axes3D>",
     "to": "viz.plot"
    }
   ],
   "inputs": "component (line-station canonical), step, stage, beam selector (ids/pg/label); bound FEMData",
   "outputs": "matplotlib Axes3D with beam force/strain envelope diagram",
   "reads": [
    "Results.elements.line_stations (LineStationSlab)",
    "Results._fem",
    "fem.nodes.coords/ids"
   ],
   "writes": [
    "ResultsPlot._facet_cache (first call via ghost mesh)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.loads",
   "label": "loads",
   "composite": "results.plot",
   "signature": "loads(*, pattern=None, ax=None, color=..., scale=None, target_frac=0.07, arrow_length_ratio=0.3, linewidth=1.5, with_mesh=True) -> Axes3D",
   "summary": "Applied nodal load arrows read from the broker-resolved fem.nodes.loads composite (reference magnitudes, no time evolution); one color per pattern, or filter by pattern name. Moments skipped.",
   "file": "src/apeGmsh/results/plot/_plot.py:594",
   "flow": [
    {
     "node": "results.plot",
     "action": "require bound FEMData with a nodes.loads composite; list patterns()",
     "passes": "<fem.nodes.loads>",
     "to": "femdata"
    },
    {
     "node": "results.plot",
     "action": "for each pattern pull broker load records via loads_composite.by_pattern(pat) (force_xyz only)",
     "passes": "<pattern:str>",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "return nodal load records; map node_id->coords, build per-pattern anchor/vec arrays",
     "passes": "<load records (node_id, force_xyz)>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "draw ghost mesh + per-pattern ax.quiver, autoscale to mesh+tips",
     "passes": "<Axes3D>",
     "to": "viz.plot"
    }
   ],
   "inputs": "optional pattern filter, arrow kwargs; bound session-side FEMData with nodes.loads",
   "outputs": "matplotlib Axes3D with applied-load arrows",
   "reads": [
    "Results._fem",
    "fem.nodes.loads (patterns/by_pattern)",
    "fem.nodes.coords",
    "fem.nodes.ids"
   ],
   "writes": [
    "ResultsPlot._facet_cache (first call via ghost mesh)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.mesh",
   "label": "mesh",
   "composite": "results.plot",
   "signature": "mesh(*, ax=None, color=..., edge_color='white', alpha=0.7, linewidth=0.3) -> Axes3D",
   "summary": "Render the undeformed mesh as a flat-colored 3-D surface (Poly3DCollection for facets + Line3DCollection for 1-D segments); no scalar field. Walks the cached facet tables off the bound FEMData.",
   "file": "src/apeGmsh/results/plot/_plot.py:116",
   "flow": [
    {
     "node": "results.plot",
     "action": "ensure a 3-D Axes, then pull cached (tris, segs, lookup, coords) via _facets()",
     "passes": "<facets:(ndarray tris,ndarray segs,ndarray lookup,ndarray coords)>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "on first call extract_facets/coords_lookup off the bound FEMData",
     "passes": "<fem:FEMData>",
     "to": "femdata"
    },
    {
     "node": "results.plot",
     "action": "add Poly3DCollection(coords[lookup[tris]]) + Line3DCollection, autoscale to coords bbox",
     "passes": "<Axes3D>",
     "to": "viz.plot"
    }
   ],
   "inputs": "optional ax + styling kwargs; bound FEMData (Results._fem)",
   "outputs": "matplotlib Axes3D with mesh surface drawn",
   "reads": [
    "Results._fem",
    "ResultsPlot._facet_cache",
    "fem.nodes.coords"
   ],
   "writes": [
    "ResultsPlot._facet_cache (first call)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.reactions",
   "label": "reactions",
   "composite": "results.plot",
   "signature": "reactions(*, step=-1, stage=None, ax=None, color=..., scale=None, target_frac=0.07, zero_tol=1e-3, arrow_length_ratio=0.3, linewidth=1.5, with_mesh=True, deformed=False, deform_scale=1.0) -> Axes3D",
   "summary": "Force arrows at constrained nodes — a thin wrapper over vector_glyph('reaction_force'). Reaction moments are intentionally not drawn (matplotlib has no curved-arrow primitive).",
   "file": "src/apeGmsh/results/plot/_plot.py:557",
   "flow": [
    {
     "node": "results.plot",
     "action": "delegate to self.vector_glyph('reaction_force', ...) with a reactions label",
     "passes": "<prefix:'reaction_force', step:int, stage:str|None>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "(via vector_glyph) read reaction_force_x/y/z NodeSlabs at step",
     "passes": "<NodeSlab per axis>",
     "to": "results.nodes"
    }
   ],
   "inputs": "step, stage, arrow scale/filter kwargs; bound FEMData",
   "outputs": "matplotlib Axes3D with reaction-force arrows",
   "reads": [
    "Results.nodes (reaction_force_* NodeSlabs)",
    "Results._fem"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.plot.vector_glyph",
   "label": "vector_glyph",
   "composite": "results.plot",
   "signature": "vector_glyph(prefix, *, step=-1, stage=None, ax=None, color=..., scale=None, target_frac=0.07, zero_tol=1e-3, arrow_length_ratio=0.3, linewidth=1.0, with_mesh=True, deformed=False, deform_scale=1.0, label=None) -> Axes3D",
   "summary": "Draw 3-D quiver arrows for a generic vector field read as {prefix}_x/_y/_z nodal components; auto-scales the longest arrow to a fraction of the bbox diagonal and filters near-zero rows.",
   "file": "src/apeGmsh/results/plot/_plot.py:442",
   "flow": [
    {
     "node": "results.plot",
     "action": "require bound FEMData; list available components via Results.nodes.available_components(stage)",
     "passes": "<stage:str|None>",
     "to": "results.nodes"
    },
    {
     "node": "results.plot",
     "action": "read each present {prefix}_x/_y/_z scalar at step via _read_node_scalars -> Results.nodes.get",
     "passes": "<component:str, step:int, stage:str|None>",
     "to": "results.nodes"
    },
    {
     "node": "results.nodes",
     "action": "return NodeSlabs; assemble (N,3) vector, filter significant, optional deformed anchors",
     "passes": "<NodeSlab per axis>",
     "to": "results.plot"
    },
    {
     "node": "results.plot",
     "action": "draw ghost mesh + ax.quiver(anchors, scaled vectors), autoscale to mesh+tips",
     "passes": "<Axes3D>",
     "to": "viz.plot"
    }
   ],
   "inputs": "prefix (e.g. 'displacement','reaction_force'), step, stage, scale/filter kwargs; bound FEMData",
   "outputs": "matplotlib Axes3D with vector arrows",
   "reads": [
    "Results.nodes (available_components + per-axis NodeSlabs)",
    "Results._fem",
    "fem.nodes.coords"
   ],
   "writes": [
    "ResultsPlot._facet_cache (first call via ghost mesh)"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.readers.MPCOReader",
   "label": "MPCOReader",
   "composite": "results.readers",
   "signature": "MPCOReader(path) [implements ResultsReader protocol]",
   "summary": "Public reader entry point — reads STKO MPCO HDF5 (Phase 3) and synthesizes a partial FEMData from /MODEL/. Reached indirectly via Results.from_mpco; conforms to the ResultsReader protocol.",
   "file": "src/apeGmsh/results/readers/_protocol.py:75",
   "flow": [
    {
     "node": "results.readers",
     "action": "Results.from_mpco constructs MPCOReader (or MPCOMultiPartitionReader); resolve_bound_fem synthesizes FEMData from /MODEL/",
     "passes": "<path>",
     "to": "results"
    },
    {
     "node": "results",
     "action": "composite layer calls protocol read_* returning typed Slabs",
     "passes": "<NodeSlab|GaussSlab|...>",
     "to": "results.nodes"
    }
   ],
   "inputs": "MPCO HDF5 path(s)",
   "outputs": "MPCOReader (ResultsReader-conformant) — Slabs on read",
   "reads": [
    "STKO MPCO HDF5 file (/RESULTS, /MODEL)"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.readers.NativeReader",
   "label": "NativeReader",
   "composite": "results.readers",
   "signature": "NativeReader(path) [implements ResultsReader protocol]",
   "summary": "Public reader entry point — reads apeGmsh native HDF5 (Phase 1) and reconstructs the embedded /model/ FEMData. Reached indirectly via Results.from_native / from_recorders; conforms to the ResultsReader protocol the composite layer talks to.",
   "file": "src/apeGmsh/results/readers/_protocol.py:75",
   "flow": [
    {
     "node": "results.readers",
     "action": "Results.from_native constructs NativeReader(path); resolve_bound_fem reconstructs FEMData from /model/",
     "passes": "<path>",
     "to": "results"
    },
    {
     "node": "results",
     "action": "composite layer calls protocol read_nodes/read_elements/... returning typed Slabs",
     "passes": "<NodeSlab|ElementSlab|GaussSlab|...>",
     "to": "results.nodes"
    }
   ],
   "inputs": "native HDF5 path",
   "outputs": "NativeReader (ResultsReader-conformant) — Slabs on read",
   "reads": [
    "native HDF5 file (/stages, /model)"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.schema.PARSER_VERSION",
   "label": "PARSER_VERSION",
   "composite": "results.schema",
   "signature": "schema._versions.PARSER_VERSION: str = '1.0'",
   "summary": "Public recorder-transcoder parser version constant; bumped when transcode logic changes in a way that must invalidate cached transcoded HDF5 files. Mixed into the from_recorders cache key.",
   "file": "src/apeGmsh/results/schema/_versions.py:11",
   "flow": [
    {
     "node": "results.schema",
     "action": "from_recorders feeds it into _cache.compute_cache_key so logic changes invalidate the cache",
     "passes": "<PARSER_VERSION:str>",
     "to": "results.writers"
    }
   ],
   "inputs": "none (module constant)",
   "outputs": "'1.0'",
   "reads": [],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.schema.SCHEMA_VERSION",
   "label": "SCHEMA_VERSION",
   "composite": "results.schema",
   "signature": "schema._versions.SCHEMA_VERSION: str = '1.0'",
   "summary": "Public on-disk native-HDF5 schema version constant (minor bump = additive, major = breaking). Stamped into every file by NativeWriter.open and read by NativeReader for compatibility.",
   "file": "src/apeGmsh/results/schema/_versions.py:10",
   "flow": [
    {
     "node": "results.schema",
     "action": "NativeWriter.open stamps it as the file's schema-version root attr",
     "passes": "<SCHEMA_VERSION:str>",
     "to": "results.writers"
    }
   ],
   "inputs": "none (module constant)",
   "outputs": "'1.0'",
   "reads": [],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.categories",
   "label": "categories",
   "composite": "results.spec",
   "signature": "categories() -> tuple[str, ...]",
   "summary": "Static introspection: the seven categories declarable on a DomainCaptureSpec (nodes/elements/line_stations/gauss/fibers/layers/modal).",
   "file": "src/apeGmsh/results/capture/spec.py:474",
   "flow": [
    {
     "node": "results.spec",
     "action": "return the ALL_CATEGORIES constant tuple",
     "passes": "<categories:tuple[str,...]>",
     "to": "chain"
    }
   ],
   "inputs": "none (staticmethod)",
   "outputs": "tuple of the 7 category names",
   "reads": [
    "ALL_CATEGORIES"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.components_for",
   "label": "components_for",
   "composite": "results.spec",
   "signature": "components_for(category: str) -> tuple[str, ...]",
   "summary": "Static introspection: sorted canonical component names allowed in a category (empty for 'modal'); raises KeyError for an unknown category.",
   "file": "src/apeGmsh/results/capture/spec.py:479",
   "flow": [
    {
     "node": "results.spec",
     "action": "look up _CATEGORY_COMPONENTS[category] and return sorted tuple",
     "passes": "<components:tuple[str,...]>",
     "to": "chain"
    }
   ],
   "inputs": "category name",
   "outputs": "sorted tuple of canonical component names",
   "reads": [
    "_CATEGORY_COMPONENTS"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.elements",
   "label": "elements",
   "composite": "results.spec",
   "signature": "elements(*, components, pg=None, label=None, selection=None, ids=None, dt=None, n_steps=None, name=None, element_class_name=None) -> DomainCaptureSpec",
   "summary": "Declare a per-element-node force capture record (globalForce/localForce closed-form line elements).",
   "file": "src/apeGmsh/results/capture/spec.py:330",
   "flow": [
    {
     "node": "results.spec",
     "action": "_declare appends a DomainCaptureRecord(category='elements') with optional element_class_name hint",
     "passes": "<DomainCaptureRecord(category='elements')>",
     "to": "results.spec"
    }
   ],
   "inputs": "components, selector, cadence, name, optional element_class_name",
   "outputs": "self (DomainCaptureSpec)",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.fibers",
   "label": "fibers",
   "composite": "results.spec",
   "signature": "fibers(*, components, pg=None, label=None, selection=None, ids=None, dt=None, n_steps=None, name=None, element_class_name=None) -> DomainCaptureSpec",
   "summary": "Declare a uniaxial fiber-section capture record (per-fiber stress/strain on beam-column FiberSection2d/3d).",
   "file": "src/apeGmsh/results/capture/spec.py:396",
   "flow": [
    {
     "node": "results.spec",
     "action": "_declare appends a DomainCaptureRecord(category='fibers')",
     "passes": "<DomainCaptureRecord(category='fibers')>",
     "to": "results.spec"
    }
   ],
   "inputs": "components, selector, cadence, name, optional element_class_name",
   "outputs": "self (DomainCaptureSpec)",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.gauss",
   "label": "gauss",
   "composite": "results.spec",
   "signature": "gauss(*, components, pg=None, label=None, selection=None, ids=None, dt=None, n_steps=None, name=None, element_class_name=None) -> DomainCaptureSpec",
   "summary": "Declare a continuum Gauss-point capture record (stress/strain/derived-scalar/material-state at integration points).",
   "file": "src/apeGmsh/results/capture/spec.py:374",
   "flow": [
    {
     "node": "results.spec",
     "action": "_declare appends a DomainCaptureRecord(category='gauss')",
     "passes": "<DomainCaptureRecord(category='gauss')>",
     "to": "results.spec"
    }
   ],
   "inputs": "components, selector, cadence, name, optional element_class_name",
   "outputs": "self (DomainCaptureSpec)",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.layers",
   "label": "layers",
   "composite": "results.spec",
   "signature": "layers(*, components, pg=None, label=None, selection=None, ids=None, dt=None, n_steps=None, name=None, element_class_name=None) -> DomainCaptureSpec",
   "summary": "Declare a layered-shell capture record (per-layer stress/strain on LayeredShellFiberSection shells).",
   "file": "src/apeGmsh/results/capture/spec.py:418",
   "flow": [
    {
     "node": "results.spec",
     "action": "_declare appends a DomainCaptureRecord(category='layers'); resolve later builds LayerSectionMetadata",
     "passes": "<DomainCaptureRecord(category='layers')>",
     "to": "results.spec"
    }
   ],
   "inputs": "components, selector, cadence, name, optional element_class_name",
   "outputs": "self (DomainCaptureSpec)",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.line_stations",
   "label": "line_stations",
   "composite": "results.spec",
   "signature": "line_stations(*, components, pg=None, label=None, selection=None, ids=None, dt=None, n_steps=None, name=None, element_class_name=None) -> DomainCaptureSpec",
   "summary": "Declare a beam line-station capture record (section forces along beam-column length).",
   "file": "src/apeGmsh/results/capture/spec.py:352",
   "flow": [
    {
     "node": "results.spec",
     "action": "_declare appends a DomainCaptureRecord(category='line_stations')",
     "passes": "<DomainCaptureRecord(category='line_stations')>",
     "to": "results.spec"
    }
   ],
   "inputs": "components, selector, cadence, name, optional element_class_name",
   "outputs": "self (DomainCaptureSpec)",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.modal",
   "label": "modal",
   "composite": "results.spec",
   "signature": "modal(n_modes: int, *, dt=None, n_steps=None, name=None) -> DomainCaptureSpec",
   "summary": "Declare a modal-shape capture record; at capture time DomainCapture.capture_modes runs ops.eigen(n_modes) and writes one kind='mode' stage per mode.",
   "file": "src/apeGmsh/results/capture/spec.py:440",
   "flow": [
    {
     "node": "results.spec",
     "action": "validate n_modes>0 + cadence, append a DomainCaptureRecord(category='modal', n_modes)",
     "passes": "<DomainCaptureRecord(category='modal')>",
     "to": "results.spec"
    }
   ],
   "inputs": "n_modes (positive int), cadence, name",
   "outputs": "self (DomainCaptureSpec)",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records",
    "DomainCaptureSpec._auto_id"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.nodes",
   "label": "nodes",
   "composite": "results.spec",
   "signature": "nodes(*, components, pg=None, label=None, selection=None, ids=None, dt=None, n_steps=None, name=None) -> DomainCaptureSpec",
   "summary": "Declare a nodal capture record (displacement/reaction/etc.) against PG/label/selection names or explicit ids, with optional dt/n_steps cadence; returns self for chaining.",
   "file": "src/apeGmsh/results/capture/spec.py:295",
   "flow": [
    {
     "node": "results.spec",
     "action": "_declare normalizes components, validates cadence+selector exclusivity, appends a DomainCaptureRecord(category='nodes')",
     "passes": "<DomainCaptureRecord(category='nodes')>",
     "to": "results.spec"
    }
   ],
   "inputs": "components (str/iterable canonical or shorthand), one selector family, cadence, name",
   "outputs": "self (DomainCaptureSpec) for chaining",
   "reads": [],
   "writes": [
    "DomainCaptureSpec._records",
    "DomainCaptureSpec._auto_id"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.resolve",
   "label": "resolve",
   "composite": "results.spec",
   "signature": "resolve(fem: FEMData) -> ResolvedDomainCaptureSpec",
   "summary": "Resolve every declared record's selectors to concrete node/element ID arrays against a FEMData snapshot, sourcing ndm/ndf from the attached OpenSees bridge (Phase 9 D8); raises if no bridge or ndm/ndf unset.",
   "file": "src/apeGmsh/results/capture/spec.py:530",
   "flow": [
    {
     "node": "results.spec",
     "action": "read _ndm/_ndf off the attached opensees bridge (raise if absent)",
     "passes": "<ndm:int, ndf:int>",
     "to": "apesees"
    },
    {
     "node": "results.spec",
     "action": "for each record expand_many components, resolve selectors via fem.nodes/elements.physical/labels + fem.mesh_selection",
     "passes": "<DomainCaptureRecord, fem:FEMData>",
     "to": "femdata"
    },
    {
     "node": "femdata",
     "action": "physical/labels/mesh_selection return concrete id arrays",
     "passes": "<node_ids|element_ids:ndarray int64>",
     "to": "results.spec"
    },
    {
     "node": "results.spec",
     "action": "for layers records build LayerSectionMetadata from the bridge section registry",
     "passes": "<element_ids:ndarray>",
     "to": "apesees"
    },
    {
     "node": "results.spec",
     "action": "assemble ResolvedDomainCaptureSpec(fem_snapshot_id, records, ndm, ndf)",
     "passes": "<ResolvedDomainCaptureSpec>",
     "to": "results.capture"
    }
   ],
   "inputs": "fem (FEMData snapshot); attached opensees bridge (for ndm/ndf + section/class hints)",
   "outputs": "ResolvedDomainCaptureSpec (concrete ID arrays + ndm/ndf + snapshot_id)",
   "reads": [
    "DomainCaptureSpec._records",
    "opensees bridge (_ndm/_ndf/_sections/_elem_assignments/_sec_tags)",
    "fem.nodes.physical/labels",
    "fem.elements.physical/labels",
    "fem.mesh_selection",
    "fem.snapshot_id"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.DomainCaptureSpec.shorthands_for",
   "label": "shorthands_for",
   "composite": "results.spec",
   "signature": "shorthands_for(category: str) -> dict[str, tuple[str, ...]]",
   "summary": "Static introspection: shorthand→canonical-expansion mapping valid for a category (e.g. 'displacement'→x/y/z, plus 'reaction' for nodes), filtered to components allowed in that category.",
   "file": "src/apeGmsh/results/capture/spec.py:497",
   "flow": [
    {
     "node": "results.spec",
     "action": "walk vocabulary shorthand tables, keep entries whose expansion is fully allowed in the category",
     "passes": "<shorthands:dict[str,tuple[str,...]]>",
     "to": "vocabulary"
    }
   ],
   "inputs": "category name",
   "outputs": "dict mapping shorthand to canonical expansion tuple",
   "reads": [
    "apeGmsh._vocabulary shorthand tables",
    "_CATEGORY_COMPONENTS"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec",
   "label": "ResolvedRecorderSpec",
   "composite": "results.spec",
   "signature": "ResolvedRecorderSpec(fem_snapshot_id: str, records: tuple[ResolvedRecorderRecord, ...] = ())",
   "summary": "Public resolved recorder-spec container — the bridge between declarative ops.recorder.* and any execution strategy (Tcl/Py emit, live recorders, MPCO, transcode). Carries the fem snapshot_id and resolved per-record ID arrays.",
   "file": "src/apeGmsh/results/spec/_resolved.py:173",
   "flow": [
    {
     "node": "results.spec",
     "action": "hold fem_snapshot_id + tuple of ResolvedRecorderRecord; expose iteration / by_category / emit helpers",
     "passes": "<ResolvedRecorderSpec>",
     "to": "results.spec"
    }
   ],
   "inputs": "fem_snapshot_id, tuple of ResolvedRecorderRecord",
   "outputs": "ResolvedRecorderSpec dataclass",
   "reads": [],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.emit_mpco",
   "label": "emit_mpco",
   "composite": "results.spec",
   "signature": "emit_mpco(path, *, ops=None) -> LiveMPCO",
   "summary": "Return a LiveMPCO context manager that issues a single in-process ops.recorder('mpco', path, ...) covering the whole analysis; read back with Results.from_mpco(path). Requires an MPCO-capable openseespy build.",
   "file": "src/apeGmsh/results/spec/_resolved.py:321",
   "flow": [
    {
     "node": "results.spec",
     "action": "construct LiveMPCO(self, path, ops)",
     "passes": "<ResolvedRecorderSpec, path>",
     "to": "results.live"
    }
   ],
   "inputs": "path (.mpco), optional ops",
   "outputs": "LiveMPCO context manager",
   "reads": [],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.emit_recorders",
   "label": "emit_recorders",
   "composite": "results.spec",
   "signature": "emit_recorders(output_dir, *, file_format='out', ops=None) -> LiveRecorders",
   "summary": "Return a LiveRecorders context manager that pushes classic ops.recorder('Node'/'Element', ...) calls into the running domain on per-stage boundaries; read back with Results.from_recorders(stage_id=...).",
   "file": "src/apeGmsh/results/spec/_resolved.py:276",
   "flow": [
    {
     "node": "results.spec",
     "action": "construct LiveRecorders(self, output_dir, file_format, ops)",
     "passes": "<ResolvedRecorderSpec, output_dir, file_format>",
     "to": "results.live"
    }
   ],
   "inputs": "output_dir, file_format, optional ops",
   "outputs": "LiveRecorders context manager",
   "reads": [],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.from_manifest_h5",
   "label": "from_manifest_h5",
   "composite": "results.spec",
   "signature": "ResolvedRecorderSpec.from_manifest_h5(path) -> ResolvedRecorderSpec",
   "summary": "Reconstruct a ResolvedRecorderSpec from a previously written HDF5 manifest (no active FEMData required).",
   "file": "src/apeGmsh/results/spec/_resolved.py:411",
   "flow": [
    {
     "node": "results.spec",
     "action": "open the manifest h5py.File, rebuild ResolvedRecorderRecord per group and the spec",
     "passes": "<path>",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "datasets/attrs read back into records",
     "passes": "<ResolvedRecorderSpec>",
     "to": "results.spec"
    }
   ],
   "inputs": "manifest HDF5 path",
   "outputs": "reconstructed ResolvedRecorderSpec",
   "reads": [
    "manifest HDF5 file"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.to_manifest_h5",
   "label": "to_manifest_h5",
   "composite": "results.spec",
   "signature": "to_manifest_h5(path) -> None",
   "summary": "Serialize the resolved spec to a small self-contained HDF5 sidecar manifest (categories, components, cadence, resolved id arrays) reloadable without an active FEMData.",
   "file": "src/apeGmsh/results/spec/_resolved.py:234",
   "flow": [
    {
     "node": "results.spec",
     "action": "write schema_version+snapshot_id attrs and per-record group (components/node_ids/element_ids datasets) to an h5py.File",
     "passes": "<path, records>",
     "to": "femdata.h5"
    }
   ],
   "inputs": "path for the manifest HDF5",
   "outputs": "None (manifest .h5 written)",
   "reads": [
    "ResolvedRecorderSpec.fem_snapshot_id/records"
   ],
   "writes": [
    "manifest HDF5 file"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.to_mpco_python_command",
   "label": "to_mpco_python_command",
   "composite": "results.spec",
   "signature": "to_mpco_python_command(*, output_dir='', filename='run.mpco') -> str",
   "summary": "Emit the single `ops.recorder('mpco', ...)` Python call covering the whole spec.",
   "file": "src/apeGmsh/results/spec/_resolved.py:399",
   "flow": [
    {
     "node": "results.spec",
     "action": "delegate to _emit.emit_mpco_python(records, output_dir, filename)",
     "passes": "<records, filename>",
     "to": "os.emit"
    }
   ],
   "inputs": "output_dir, filename",
   "outputs": "single Python ops.recorder('mpco', ...) string",
   "reads": [
    "ResolvedRecorderSpec.records"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.to_mpco_tcl_command",
   "label": "to_mpco_tcl_command",
   "composite": "results.spec",
   "signature": "to_mpco_tcl_command(*, output_dir='', filename='run.mpco') -> str",
   "summary": "Emit the single `recorder mpco ...` Tcl command aggregating unique MPCO -N/-E tokens across all records, with an aggregated -T cadence.",
   "file": "src/apeGmsh/results/spec/_resolved.py:379",
   "flow": [
    {
     "node": "results.spec",
     "action": "delegate to _emit.emit_mpco_tcl(records, output_dir, filename)",
     "passes": "<records, filename>",
     "to": "results.spec"
    },
    {
     "node": "results.spec",
     "action": "collect_mpco_tokens + aggregate_cadence build one mpco line",
     "passes": "<node_tokens, elem_tokens>",
     "to": "os.emit"
    }
   ],
   "inputs": "output_dir, filename",
   "outputs": "single Tcl `recorder mpco ...` string",
   "reads": [
    "ResolvedRecorderSpec.records"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.to_python_commands",
   "label": "to_python_commands",
   "composite": "results.spec",
   "signature": "to_python_commands(*, output_dir='', file_format='out') -> list[str]",
   "summary": "Emit the list of `ops.recorder(...)` Python call source lines for this spec.",
   "file": "src/apeGmsh/results/spec/_resolved.py:368",
   "flow": [
    {
     "node": "results.spec",
     "action": "delegate to _emit.emit_spec_python(records, output_dir, file_format)",
     "passes": "<records, output_dir, file_format>",
     "to": "results.spec"
    },
    {
     "node": "results.spec",
     "action": "per record emit_logical -> LogicalRecorder, format_python each",
     "passes": "<LogicalRecorder list>",
     "to": "os.emit"
    }
   ],
   "inputs": "output_dir, file_format",
   "outputs": "list of Python ops.recorder(...) source lines",
   "reads": [
    "ResolvedRecorderSpec.records"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.spec.ResolvedRecorderSpec.to_tcl_commands",
   "label": "to_tcl_commands",
   "composite": "results.spec",
   "signature": "to_tcl_commands(*, output_dir='', file_format='out') -> list[str]",
   "summary": "Emit the list of `recorder ...` Tcl command lines for this spec (nodes/elements/gauss/line_stations; fibers/layers/modal emit TODO comments).",
   "file": "src/apeGmsh/results/spec/_resolved.py:349",
   "flow": [
    {
     "node": "results.spec",
     "action": "delegate to _emit.emit_spec_tcl(records, output_dir, file_format)",
     "passes": "<records, output_dir, file_format>",
     "to": "results.spec"
    },
    {
     "node": "results.spec",
     "action": "per record emit_logical -> LogicalRecorder, format_tcl each",
     "passes": "<LogicalRecorder list>",
     "to": "os.emit"
    }
   ],
   "inputs": "output_dir, file_format",
   "outputs": "list of Tcl recorder command strings",
   "reads": [
    "ResolvedRecorderSpec.records"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.transcode.RecorderTranscoder",
   "label": "RecorderTranscoder",
   "composite": "results.transcode",
   "signature": "RecorderTranscoder(spec, output_dir, target_path, fem, *, stage_name='analysis', stage_kind='transient', file_format='out', stage_id=None)",
   "summary": "Public transcoder entry used by Results.from_recorders — parses emitted OpenSees .out files into a native HDF5 file matching the apeGmsh schema, reusing the spec emitter to recompute file paths/column layouts.",
   "file": "src/apeGmsh/results/transcoders/_recorder.py:77",
   "flow": [
    {
     "node": "results.transcode",
     "action": "store spec, output_dir, target_path, fem, stage metadata; defer parsing to run()",
     "passes": "<ResolvedRecorderSpec, output_dir, target_path, fem>",
     "to": "results.transcode"
    }
   ],
   "inputs": "ResolvedRecorderSpec, output_dir, target HDF5 path, FEMData, stage metadata",
   "outputs": "RecorderTranscoder instance",
   "reads": [],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.transcode.RecorderTranscoder.run",
   "label": "run",
   "composite": "results.transcode",
   "signature": "run() -> Path",
   "summary": "Parse all emitted recorder files and write the native HDF5 target. Per record: re-derive file paths via emit_logical, parse .out via _txt, unflatten element-level data via the response catalog, merge nodes (union+NaN), then NativeWriter writes one stage. fibers/layers/modal skipped (surfaced in self.unsupported).",
   "file": "src/apeGmsh/results/transcoders/_recorder.py:104",
   "flow": [
    {
     "node": "results.transcode",
     "action": "per record emit_logical -> LogicalRecorder paths/layout",
     "passes": "<ResolvedRecorderRecord>",
     "to": "os.emit"
    },
    {
     "node": "results.transcode",
     "action": "parse each .out via transcoders._txt.parse_node_file / parse_element_file",
     "passes": "<file_path, LogicalRecorder, flat_size>",
     "to": "results.transcode"
    },
    {
     "node": "results.transcode",
     "action": "unflatten element-level flats via opensees._response_catalog (RESPONSE_CATALOG/unflatten)",
     "passes": "<flat:ndarray(T,E,K), ResponseLayout>",
     "to": "os.emit"
    },
    {
     "node": "results.transcode",
     "action": "NativeWriter.open(fem) + begin_stage + write_merged_nodes/write_gauss_group/write_line_stations_group/write_nodal_forces_group + end_stage",
     "passes": "<node_ids/element_index, components:dict[str,ndarray]>",
     "to": "results.writers"
    },
    {
     "node": "results.writers",
     "action": "native HDF5 target written and closed",
     "passes": "<target_path:Path>",
     "to": "femdata.h5"
    }
   ],
   "inputs": "none (consumes constructor state + on-disk .out files + bound FEMData)",
   "outputs": "Path of the written native HDF5 file",
   "reads": [
    "on-disk recorder .out files",
    "ResolvedRecorderSpec.records",
    "opensees._response_catalog",
    "fem (element lengths/connectivity)"
   ],
   "writes": [
    "native HDF5 target file (via NativeWriter)",
    "RecorderTranscoder.unsupported"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.viewer",
   "label": "viewer",
   "composite": "results",
   "signature": "Results.viewer(*, blocking=True, title=None, restore_session='prompt', save_session=True, cuts=None, model_h5=None) -> ResultsViewer | subprocess.Popen | None",
   "summary": "Public ResultsViewer open entry. Blocking: constructs ResultsViewer(self, ...).show() in-process and runs the Qt loop until close. Non-blocking: spawns `python -m apeGmsh.viewers <path>` and closes the parent reader. Returns None under APEGMSH_SKIP_VIEWER.",
   "file": "src/apeGmsh/results/Results.py:397",
   "flow": [
    {
     "node": "results",
     "action": "if APEGMSH_SKIP_VIEWER return None; if not blocking spawn subprocess and self.close()",
     "passes": "<results path>",
     "to": "viewer.results"
    },
    {
     "node": "results",
     "action": "blocking: construct ResultsViewer(self, title, restore_session, save_session, cuts, model_h5).show()",
     "passes": "<Results, cuts, model_h5>",
     "to": "viewer.results"
    },
    {
     "node": "viewer.results",
     "action": "ResultsViewer.show() pins a live ref, logs session.open, calls _show_impl",
     "passes": "<ResultsViewer>",
     "to": "viewer.results"
    }
   ],
   "inputs": "blocking/title/session/cuts/model_h5 options; Results with a bound FEMData",
   "outputs": "ResultsViewer (blocking) | subprocess.Popen (non-blocking) | None (skip)",
   "reads": [
    "Results._path",
    "APEGMSH_SKIP_VIEWER env"
   ],
   "writes": [],
   "_cluster": "I_res_io"
  },
  {
   "id": "results.writers.NativeWriter",
   "label": "NativeWriter",
   "composite": "results.writers",
   "signature": "NativeWriter(path) [bulk-write API: open/begin_stage/write_*/end_stage/close]",
   "summary": "Public bulk writer for apeGmsh native HDF5 result files. Used internally by RecorderTranscoder and DomainCapture: open() writes root attrs + embeds FEMData, begin_stage/end_stage scope stages, write_nodes/write_*_group write per-level slabs at SCHEMA_VERSION.",
   "file": "src/apeGmsh/results/writers/_native.py:57",
   "flow": [
    {
     "node": "results.writers",
     "action": "open() creates the h5py.File, stamps SCHEMA_VERSION/source attrs, write_model(fem) embeds the /model snapshot",
     "passes": "<path, fem:FEMData, source_type>",
     "to": "results.schema"
    },
    {
     "node": "results.writers",
     "action": "begin_stage/write_nodes/write_gauss_group/.../end_stage write slab datasets per stage",
     "passes": "<node_ids/element_index:ndarray, components:dict[str,ndarray]>",
     "to": "femdata.h5"
    },
    {
     "node": "femdata.h5",
     "action": "datasets persisted; close() finalises the file",
     "passes": "<native HDF5 file>",
     "to": "results.readers"
    }
   ],
   "inputs": "output path; then fem + per-stage slab arrays via the write methods",
   "outputs": "native HDF5 results file on disk",
   "reads": [
    "FEMData (for /model embed)",
    "schema._native/_versions constants"
   ],
   "writes": [
    "native HDF5 results file"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "selection.ElementChain.__bool__",
   "label": "bool(ElementChain)",
   "composite": "selection",
   "signature": "__bool__() -> bool",
   "summary": "True if the chain has at least one accumulated atom.",
   "file": "src/apeGmsh/_chain.py:222",
   "flow": [
    {
     "node": "selection",
     "action": "return bool(self._items)",
     "passes": "bool",
     "to": "selection"
    }
   ],
   "inputs": "none",
   "outputs": "bool",
   "reads": [
    "chain._items"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.__iter__",
   "label": "iter(ElementChain)",
   "composite": "selection",
   "signature": "__iter__() -> Iterator[int]",
   "summary": "Iterate the chain's accumulated element-id atoms (pre-materialization).",
   "file": "src/apeGmsh/_chain.py:216",
   "flow": [
    {
     "node": "selection",
     "action": "return iter(self._items)",
     "passes": "Iterator[int]",
     "to": "selection"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of element-id atoms",
   "reads": [
    "chain._items"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.__len__",
   "label": "len(ElementChain)",
   "composite": "selection",
   "signature": "__len__() -> int",
   "summary": "Number of accumulated atoms in the chain.",
   "file": "src/apeGmsh/_chain.py:219",
   "flow": [
    {
     "node": "selection",
     "action": "return len(self._items)",
     "passes": "int",
     "to": "selection"
    }
   ],
   "inputs": "none",
   "outputs": "atom count int",
   "reads": [
    "chain._items"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.difference",
   "label": "difference / -",
   "composite": "selection",
   "signature": "difference(other: SelectionChain) -> ElementChain  (alias __sub__)",
   "summary": "Set difference (self minus other) of two same-type, same-engine ElementChains.",
   "file": "src/apeGmsh/_chain.py:192",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); drop atoms present in other._items",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another ElementChain",
   "outputs": "new ElementChain (difference)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.in_box",
   "label": "in_box",
   "composite": "selection",
   "signature": "in_box(lo, hi, *, inclusive: bool = False) -> ElementChain",
   "summary": "Refine the element chain to elements whose centroid is inside the box; default half-open [lo,hi), inclusive=True for closed.",
   "file": "src/apeGmsh/_chain.py:135",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.in_box → ElementChain._spatial_box on _centroid_map",
     "passes": "atoms tuple, lo/hi float64(3,)",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "build centroids from sibling NodeComposite (fail-loud on unknown node), numpy mask, _wrap",
     "passes": "centroid float64(N,3), filtered atoms",
     "to": "selection"
    }
   ],
   "inputs": "lo, hi corner points, inclusive flag",
   "outputs": "new ElementChain",
   "reads": [
    "ElementComposite._groups",
    "ElementComposite._apegmsh_nodes_ref (NodeComposite ids/coords)"
   ],
   "writes": [
    "engine._apegmsh_elem_centroid cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.in_sphere",
   "label": "in_sphere",
   "composite": "selection",
   "signature": "in_sphere(center, radius: float) -> ElementChain",
   "summary": "Refine the element chain to elements whose centroid is inside the closed ball; ValueError if radius<0.",
   "file": "src/apeGmsh/_chain.py:140",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.in_sphere → ElementChain._spatial_sphere on centroids",
     "passes": "atoms tuple, center float64(3,), radius float",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "np.linalg.norm mask on centroids, _wrap",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "center point, radius",
   "outputs": "new ElementChain",
   "reads": [
    "ElementComposite._groups",
    "_apegmsh_nodes_ref"
   ],
   "writes": [
    "engine._apegmsh_elem_centroid cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.intersect",
   "label": "intersect / &",
   "composite": "selection",
   "signature": "intersect(other: SelectionChain) -> ElementChain  (alias __and__)",
   "summary": "Set intersection of two same-type, same-engine ElementChains.",
   "file": "src/apeGmsh/_chain.py:187",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); keep atoms also in other._items",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another ElementChain",
   "outputs": "new ElementChain (intersection)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.nearest_to",
   "label": "nearest_to",
   "composite": "selection",
   "signature": "nearest_to(point, *, count: int = 1) -> ElementChain",
   "summary": "Keep the count elements whose centroids are closest to point (squared-distance sort, lowest-index tie-break).",
   "file": "src/apeGmsh/_chain.py:146",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.nearest_to → _nearest sorts atoms by sq-dist on centroids",
     "passes": "atoms tuple, point, count int",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "_coords_of via _centroid_map, sorted order[:count], _wrap",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "reference point, count",
   "outputs": "new ElementChain (≤count atoms)",
   "reads": [
    "ElementComposite._groups",
    "_apegmsh_nodes_ref"
   ],
   "writes": [
    "engine._apegmsh_elem_centroid cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.on_plane",
   "label": "on_plane",
   "composite": "selection",
   "signature": "on_plane(point, normal, *, tol: float) -> ElementChain",
   "summary": "Refine the element chain to elements whose centroid is within tol of the plane; ValueError on zero normal or negative tol.",
   "file": "src/apeGmsh/_chain.py:143",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.on_plane → ElementChain._spatial_plane signed-distance on centroids",
     "passes": "atoms tuple, point/normal float64(3,), tol float",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "abs distance mask on centroids, _wrap",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "plane point, normal, tol",
   "outputs": "new ElementChain",
   "reads": [
    "ElementComposite._groups",
    "_apegmsh_nodes_ref"
   ],
   "writes": [
    "engine._apegmsh_elem_centroid cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.result",
   "label": "result",
   "composite": "selection",
   "signature": "result() -> GroupResult",
   "summary": "Terminal: materialize the accumulated element-id atoms into a GroupResult of per-type ElementGroup blocks (np.isin block-masking) — same family as fem.elements.get().",
   "file": "src/apeGmsh/mesh/_elem_chain.py:163",
   "flow": [
    {
     "node": "selection",
     "action": "ElementChain._materialize: per group np.isin(g.ids, keep) mask",
     "passes": "atoms set, np.isin mask per group",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "deferred import ElementGroup/GroupResult, build masked blocks",
     "passes": "list[ElementGroup] → GroupResult",
     "to": "selection"
    }
   ],
   "inputs": "none (uses accumulated atoms)",
   "outputs": "GroupResult",
   "reads": [
    "ElementComposite._groups"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.symmetric_difference",
   "label": "symmetric_difference / ^",
   "composite": "selection",
   "signature": "symmetric_difference(other: SelectionChain) -> ElementChain  (alias __xor__)",
   "summary": "Symmetric difference of two same-type, same-engine ElementChains, order preserved across self then other.",
   "file": "src/apeGmsh/_chain.py:197",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); keep atoms in exactly one set",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another ElementChain",
   "outputs": "new ElementChain (symmetric difference)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.union",
   "label": "union / |",
   "composite": "selection",
   "signature": "union(other: SelectionChain) -> ElementChain  (alias __or__)",
   "summary": "Set union of two same-type, same-engine ElementChains, insertion-order-preserving; TypeError on cross-type/cross-engine.",
   "file": "src/apeGmsh/_chain.py:183",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other) then _wrap(self._items + other._items) deduped",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another ElementChain",
   "outputs": "new ElementChain (union)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.ElementChain.where",
   "label": "where",
   "composite": "selection",
   "signature": "where(predicate) -> ElementChain",
   "summary": "Keep elements whose centroid satisfies the user predicate(xyz) -> bool.",
   "file": "src/apeGmsh/_chain.py:149",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.where calls _coords_of (centroids) then filters atoms by predicate",
     "passes": "atoms tuple, centroids float64(N,3)",
     "to": "fem.elements"
    },
    {
     "node": "fem.elements",
     "action": "_wrap kept atoms into new ElementChain",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "callable predicate(xyz)",
   "outputs": "new ElementChain",
   "reads": [
    "ElementComposite._groups",
    "_apegmsh_nodes_ref"
   ],
   "writes": [
    "engine._apegmsh_elem_centroid cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.__bool__",
   "label": "bool(NodeChain)",
   "composite": "selection",
   "signature": "__bool__() -> bool",
   "summary": "True if the chain has at least one accumulated atom.",
   "file": "src/apeGmsh/_chain.py:222",
   "flow": [
    {
     "node": "selection",
     "action": "return bool(self._items)",
     "passes": "bool",
     "to": "selection"
    }
   ],
   "inputs": "none",
   "outputs": "bool",
   "reads": [
    "chain._items"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.__iter__",
   "label": "iter(NodeChain)",
   "composite": "selection",
   "signature": "__iter__() -> Iterator[int]",
   "summary": "Iterate the chain's accumulated node-id atoms (pre-materialization).",
   "file": "src/apeGmsh/_chain.py:216",
   "flow": [
    {
     "node": "selection",
     "action": "return iter(self._items)",
     "passes": "Iterator[int]",
     "to": "selection"
    }
   ],
   "inputs": "none",
   "outputs": "iterator of node-id atoms",
   "reads": [
    "chain._items"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.__len__",
   "label": "len(NodeChain)",
   "composite": "selection",
   "signature": "__len__() -> int",
   "summary": "Number of accumulated atoms in the chain.",
   "file": "src/apeGmsh/_chain.py:219",
   "flow": [
    {
     "node": "selection",
     "action": "return len(self._items)",
     "passes": "int",
     "to": "selection"
    }
   ],
   "inputs": "none",
   "outputs": "atom count int",
   "reads": [
    "chain._items"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.difference",
   "label": "difference / -",
   "composite": "selection",
   "signature": "difference(other: SelectionChain) -> NodeChain  (alias __sub__)",
   "summary": "Set difference (self minus other) of two same-type, same-engine NodeChains.",
   "file": "src/apeGmsh/_chain.py:192",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); drop atoms present in other._items",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another NodeChain",
   "outputs": "new NodeChain (difference)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.in_box",
   "label": "in_box",
   "composite": "selection",
   "signature": "in_box(lo, hi, *, inclusive: bool = False) -> NodeChain",
   "summary": "Refine the node chain to atoms whose coordinate is inside the box; default half-open [lo,hi), inclusive=True for closed [lo,hi].",
   "file": "src/apeGmsh/_chain.py:135",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.in_box → NodeChain._spatial_box maps atoms→engine coord rows via _row_map",
     "passes": "atoms tuple, lo/hi float64(3,)",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "numpy mask on NodeComposite.coords, _wrap survivors into new NodeChain",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "lo, hi corner points, inclusive flag",
   "outputs": "new NodeChain (covariant)",
   "reads": [
    "NodeComposite.coords",
    "NodeComposite.ids (_row_map cache)"
   ],
   "writes": [
    "engine._apegmsh_chain_idrow cache"
   ],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.in_sphere",
   "label": "in_sphere",
   "composite": "selection",
   "signature": "in_sphere(center, radius: float) -> NodeChain",
   "summary": "Refine the node chain to atoms inside the closed ball of given center/radius (numpy norm on coords); ValueError if radius<0.",
   "file": "src/apeGmsh/_chain.py:140",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.in_sphere → NodeChain._spatial_sphere on engine coords",
     "passes": "atoms tuple, center float64(3,), radius float",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "np.linalg.norm mask, _wrap survivors",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "center point, radius",
   "outputs": "new NodeChain",
   "reads": [
    "NodeComposite.coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.intersect",
   "label": "intersect / &",
   "composite": "selection",
   "signature": "intersect(other: SelectionChain) -> NodeChain  (alias __and__)",
   "summary": "Set intersection of two same-type, same-engine NodeChains.",
   "file": "src/apeGmsh/_chain.py:187",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); keep atoms also in other._items",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another NodeChain",
   "outputs": "new NodeChain (intersection)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.nearest_to",
   "label": "nearest_to",
   "composite": "selection",
   "signature": "nearest_to(point, *, count: int = 1) -> NodeChain",
   "summary": "Keep the count atoms closest to point (squared-distance sort with lowest-index tie-break).",
   "file": "src/apeGmsh/_chain.py:146",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.nearest_to → _nearest sorts atoms by fsum sq-dist on engine coords",
     "passes": "atoms tuple, point, count int",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "_coords_of via _row_map, sorted order[:count], _wrap",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "reference point, count",
   "outputs": "new NodeChain (≤count atoms)",
   "reads": [
    "NodeComposite.coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.on_plane",
   "label": "on_plane",
   "composite": "selection",
   "signature": "on_plane(point, normal, *, tol: float) -> NodeChain",
   "summary": "Refine the node chain to atoms within tol of the plane (point, normal); ValueError on zero normal or negative tol.",
   "file": "src/apeGmsh/_chain.py:143",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.on_plane → NodeChain._spatial_plane signed-distance on coords",
     "passes": "atoms tuple, point/normal float64(3,), tol float",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "abs((c-p)·n̂)<=tol mask, _wrap survivors",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "plane point, normal, tol",
   "outputs": "new NodeChain",
   "reads": [
    "NodeComposite.coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.result",
   "label": "result",
   "composite": "selection",
   "signature": "result() -> NodeResult",
   "summary": "Terminal: materialize the accumulated atoms into a NodeResult (object-dtype ids + (N,3) float64 coords) — id-for-id identical to fem.nodes.get().",
   "file": "src/apeGmsh/mesh/_node_chain.py:87",
   "flow": [
    {
     "node": "selection",
     "action": "NodeChain._materialize: atoms→int64 ids, _coords_of maps atoms→engine coord rows",
     "passes": "atoms tuple → ids int64, coords float64(N,3)",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "deferred import NodeResult, construct and return",
     "passes": "NodeResult",
     "to": "selection"
    }
   ],
   "inputs": "none (uses accumulated atoms)",
   "outputs": "NodeResult",
   "reads": [
    "NodeComposite.coords (via _row_map)"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.symmetric_difference",
   "label": "symmetric_difference / ^",
   "composite": "selection",
   "signature": "symmetric_difference(other: SelectionChain) -> NodeChain  (alias __xor__)",
   "summary": "Symmetric difference of two same-type, same-engine NodeChains, order preserved across self then other.",
   "file": "src/apeGmsh/_chain.py:197",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other); keep atoms in exactly one set",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another NodeChain",
   "outputs": "new NodeChain (symmetric difference)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.union",
   "label": "union / |",
   "composite": "selection",
   "signature": "union(other: SelectionChain) -> NodeChain  (alias __or__)",
   "summary": "Set union of two same-type, same-engine NodeChains, insertion-order-preserving dedup; TypeError on cross-type/cross-engine.",
   "file": "src/apeGmsh/_chain.py:183",
   "flow": [
    {
     "node": "selection",
     "action": "_compatible(other) then _wrap(self._items + other._items) deduped",
     "passes": "two atom tuples",
     "to": "selection"
    }
   ],
   "inputs": "another NodeChain",
   "outputs": "new NodeChain (union)",
   "reads": [
    "chain._items",
    "chain._engine"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "selection.NodeChain.where",
   "label": "where",
   "composite": "selection",
   "signature": "where(predicate) -> NodeChain",
   "summary": "Keep atoms whose coordinate row satisfies the user predicate(xyz) -> bool.",
   "file": "src/apeGmsh/_chain.py:149",
   "flow": [
    {
     "node": "selection",
     "action": "SelectionChain.where calls _coords_of then filters atoms by predicate(xyz)",
     "passes": "atoms tuple, coords float64(N,3)",
     "to": "fem.nodes"
    },
    {
     "node": "fem.nodes",
     "action": "_wrap kept atoms into new NodeChain",
     "passes": "filtered atoms tuple",
     "to": "selection"
    }
   ],
   "inputs": "callable predicate(xyz)",
   "outputs": "new NodeChain",
   "reads": [
    "NodeComposite.coords"
   ],
   "writes": [],
   "_cluster": "E_fem"
  },
  {
   "id": "viewer.results.export_animation",
   "label": "export_animation",
   "composite": "viewer.results",
   "signature": "export_animation(path, *, fps=30, step_stride=1) -> Any",
   "summary": "Export the viewer's time history as an animated MP4/GIF (format from the path suffix); requires show() to have wired the plotter+director. Delegates to viewers.animation.export_animation.",
   "file": "src/apeGmsh/viewers/results_viewer.py:154",
   "flow": [
    {
     "node": "viewer.results",
     "action": "require plotter+director (show() must have run); delegate to viewers.animation.export_animation(plotter, director, path, fps, step_stride)",
     "passes": "<plotter, ResultsDirector, path, fps, step_stride>",
     "to": "viewer.animation"
    }
   ],
   "inputs": "output path (.mp4/.gif), fps, step_stride; a shown ResultsViewer",
   "outputs": "animation export result (path/handle)",
   "reads": [
    "ResultsViewer._plotter",
    "ResultsViewer._director"
   ],
   "writes": [
    "animation file on disk"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "viewer.results.show",
   "label": "show",
   "composite": "viewer.results",
   "signature": "ResultsViewer.show(*, maximized=True) -> ResultsViewer",
   "summary": "Open the viewer window and run the Qt event loop until close. Pins a strong ref so the window survives an already-running Qt loop, initialises the session action logger, then delegates to _show_impl (traps and logs any escaping exception). Returns self.",
   "file": "src/apeGmsh/viewers/results_viewer.py:183",
   "flow": [
    {
     "node": "viewer.results",
     "action": "add self to _LIVE_VIEWERS, log session/open with the results path",
     "passes": "<ResultsViewer>",
     "to": "viewer.results"
    },
    {
     "node": "viewer.results",
     "action": "call _show_impl(maximized) inside a log_error trap",
     "passes": "<maximized:bool>",
     "to": "viewer.results"
    },
    {
     "node": "viewer.results",
     "action": "_show_impl builds ResultsDirector(results) + build_fem_scene(ViewerData.from_fem(fem)) + ResultsWindow",
     "passes": "<Results, FEMSceneData>",
     "to": "viewer"
    }
   ],
   "inputs": "maximized flag; constructed ResultsViewer (Results with bound FEMData)",
   "outputs": "self (ResultsViewer) after window closes",
   "reads": [
    "Results._fem",
    "Results._path"
   ],
   "writes": [
    "_LIVE_VIEWERS",
    "ResultsViewer._director/_scene/_win/_plotter"
   ],
   "_cluster": "I_res_io"
  },
  {
   "id": "viz.inspect.get_geometry_info",
   "label": "get_geometry_info",
   "composite": "viz.inspect",
   "signature": "Inspect.get_geometry_info() -> tuple[dict, pd.DataFrame]",
   "summary": "g.inspect.get_geometry_info — pure BRep introspection. Walks gmsh.model entities per dim (points/curves/surfaces/volumes), sampling type/boundary/length/area/volume/center/normal/curvature/inertia into per-dim flat DataFrames + type summaries + a global summary. Prints when parent._verbose.",
   "file": "src/apeGmsh/viz/Inspect.py:33",
   "flow": [
    {
     "node": "viz.inspect",
     "action": "gmsh.model.getEntities/getType/getValue/occ.getMass per dim",
     "passes": "raw entity geometry (coords, mass, normals)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return BRep entity data",
     "passes": "ndarray coords/scalars",
     "to": "viz.inspect"
    },
    {
     "node": "viz.inspect",
     "action": "assemble per-dim DataFrames + global summary; print if verbose",
     "passes": "dict + pd.DataFrame",
     "to": "session"
    }
   ],
   "inputs": "live Gmsh model state (synced geometry)",
   "outputs": "(mapping dict of df/summary/entities, global_summary DataFrame)",
   "reads": [
    "gmsh.model BRep entities"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.inspect.get_mesh_info",
   "label": "get_mesh_info",
   "composite": "viz.inspect",
   "signature": "Inspect.get_mesh_info() -> tuple[dict, pd.DataFrame]",
   "summary": "g.inspect.get_mesh_info — introspects the generated mesh: node coords DataFrame, per-(dim,tag,elem_type) element table + element-type count summary + SICN quality summary (dim>=2). Returns mapping + dim-indexed global summary; prints if verbose.",
   "file": "src/apeGmsh/viz/Inspect.py:249",
   "flow": [
    {
     "node": "viz.inspect",
     "action": "gmsh.model.mesh.getNodes/getElements/getElementProperties/getElementQualities",
     "passes": "node tags+xyz, element types+tags",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return mesh node/element/quality arrays",
     "passes": "ndarray tags/coords, SICN",
     "to": "viz.inspect"
    },
    {
     "node": "viz.inspect",
     "action": "build nodes_df/elem_df/summary/quality + global summary",
     "passes": "dict + pd.DataFrame",
     "to": "session"
    }
   ],
   "inputs": "generated Gmsh mesh state",
   "outputs": "(mapping with nodes/elements/quality, global_summary DataFrame)",
   "reads": [
    "gmsh.model.mesh nodes/elements/qualities"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.inspect.print_summary",
   "label": "print_summary",
   "composite": "viz.inspect",
   "signature": "Inspect.print_summary() -> str",
   "summary": "g.inspect.print_summary — comprehensive 5-section text report (geometry counts/bbox, physical groups + unassigned, mesh options via gmsh.option, apeGmsh-tracked mesh directives, mesh statistics/quality). Distinguishes [gmsh] ground-truth vs [tracked] write-only directives. Prints and returns the text.",
   "file": "src/apeGmsh/viz/Inspect.py:373",
   "flow": [
    {
     "node": "viz.inspect",
     "action": "gmsh.model.getEntities/getPhysicalGroups/option.getNumber/mesh stats",
     "passes": "geometry+PG+option+mesh scalars",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return live model/mesh/option values",
     "passes": "counts, bbox, qualities",
     "to": "viz.inspect"
    },
    {
     "node": "viz.inspect",
     "action": "read parent.mesh._directives for tracked write-only settings",
     "passes": "directive dict list",
     "to": "session"
    },
    {
     "node": "viz.inspect",
     "action": "format 5-section text, print, return",
     "passes": "str report",
     "to": "session"
    }
   ],
   "inputs": "live Gmsh session + parent.mesh tracked directives",
   "outputs": "formatted multi-section summary string (also printed)",
   "reads": [
    "gmsh.model/option/mesh",
    "parent.mesh._directives"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.clear",
   "label": "clear",
   "composite": "viz.plot",
   "signature": "Plot.clear() -> Plot",
   "summary": "g.plot.clear — plt.close the current figure and discard handles so the next drawing call starts a fresh figure. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:1051",
   "flow": [
    {
     "node": "viz.plot",
     "action": "plt.close(_fig), reset _fig/_ax to None",
     "passes": "—",
     "to": "viz.plot"
    }
   ],
   "inputs": "current figure",
   "outputs": "self (figure discarded)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.figsize",
   "label": "figsize",
   "composite": "viz.plot",
   "signature": "Plot.figsize(size: tuple[float, float]) -> Plot",
   "summary": "g.plot.figsize — set the matplotlib figure size in inches before drawing; resizes in place if a figure already exists. Chainable (returns self).",
   "file": "src/apeGmsh/viz/Plot.py:318",
   "flow": [
    {
     "node": "viz.plot",
     "action": "store _figsize, resize existing _fig if present",
     "passes": "(w,h) inches",
     "to": "viz.plot"
    }
   ],
   "inputs": "(width, height) tuple",
   "outputs": "self (Plot, chainable)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.geometry",
   "label": "geometry",
   "composite": "viz.plot",
   "signature": "Plot.geometry(*, n_curve_samples=60, show_points=True, show_curves=True, show_surfaces=True, label_tags=False, color_points=..., color_curves=..., color_surfaces=..., surface_alpha=0.25, show=False) -> Plot",
   "summary": "g.plot.geometry — render BRep by parametric sampling (vertex scatter, curve polylines, SVD-fitted hole-aware surface triangulation via Poly3DCollection). No mesh required. Autoscales to equal aspect. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:499",
   "flow": [
    {
     "node": "viz.plot",
     "action": "gmsh.model.getEntities/getValue/getParametrizationBounds sample BRep",
     "passes": "vertex/curve/surface-loop ndarrays",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return parametric geometry samples",
     "passes": "ndarray points",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "scatter/plot/add_collection3d onto mpl axes, autoscale",
     "passes": "matplotlib Figure/Axes3D",
     "to": "external"
    }
   ],
   "inputs": "live Gmsh BRep, styling kwargs",
   "outputs": "self (figure populated; plt.show if show=True)",
   "reads": [
    "gmsh.model BRep entities"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.label_elements",
   "label": "label_elements",
   "composite": "viz.plot",
   "signature": "Plot.label_elements(*, dim=-1, tag=-1, stride=1, fontsize=5, color='darkred', prefix='e', show=False) -> Plot",
   "summary": "g.plot.label_elements — annotate mesh elements with their tag ids at element centroids (strided, optional dim/tag restriction). Builds node lookup for centroid computation. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:935",
   "flow": [
    {
     "node": "viz.plot",
     "action": "gmsh.model.mesh.getNodes + getElements/getElementProperties",
     "passes": "node lookup, element node lists",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return mesh nodes/elements",
     "passes": "ndarray tags/coords",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "compute centroids, ax.text per strided element",
     "passes": "matplotlib Axes3D text",
     "to": "external"
    }
   ],
   "inputs": "generated mesh, dim/tag/stride",
   "outputs": "self (element tags annotated)",
   "reads": [
    "gmsh.model.mesh nodes/elements"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.label_entities",
   "label": "label_entities",
   "composite": "viz.plot",
   "signature": "Plot.label_entities(*, dims=None, show_dim=True, fontsize=7, show=False) -> Plot",
   "summary": "g.plot.label_entities — annotate the current axes with BRep entity tags at each entity's parametric/centroid position (P/C/S/V prefixes), per requested dims. No mesh required. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:808",
   "flow": [
    {
     "node": "viz.plot",
     "action": "gmsh.model.getParametrizationBounds/getValue per entity",
     "passes": "entity centroid xyz",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return parametric/center coords",
     "passes": "ndarray xyz",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "ax.text label at each position",
     "passes": "matplotlib Axes3D text",
     "to": "external"
    }
   ],
   "inputs": "live BRep, dims filter",
   "outputs": "self (axes annotated)",
   "reads": [
    "gmsh.model BRep entities"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.label_nodes",
   "label": "label_nodes",
   "composite": "viz.plot",
   "signature": "Plot.label_nodes(*, dim=-1, tag=-1, stride=1, fontsize=5, color='black', prefix='n', offset=None, show=False) -> Plot",
   "summary": "g.plot.label_nodes — annotate mesh nodes with their tag ids (every Nth via stride, optional dim/tag restriction and text offset). Autoscales. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:878",
   "flow": [
    {
     "node": "viz.plot",
     "action": "gmsh.model.mesh.getNodes(dim,tag)",
     "passes": "node tags + flat coords",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return mesh nodes",
     "passes": "ndarray tags/xyz",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "ax.text per strided node, autoscale",
     "passes": "matplotlib Axes3D text",
     "to": "external"
    }
   ],
   "inputs": "generated mesh, dim/tag/stride",
   "outputs": "self (node tags annotated)",
   "reads": [
    "gmsh.model.mesh nodes"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.mesh",
   "label": "mesh",
   "composite": "viz.plot",
   "signature": "Plot.mesh(*, color=..., edge_color='white', alpha=0.70, linewidth=0.30, show=False) -> Plot",
   "summary": "g.plot.mesh — draw the surface mesh as filled Poly3DCollection polygons (dim=2) plus dim=1 line segments; for volume meshes only surface elements are shown. Builds a node-tag→xyz lookup, iterates element corner verts. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:609",
   "flow": [
    {
     "node": "viz.plot",
     "action": "gmsh.model.mesh.getNodes/getElements build node lookup + corner verts",
     "passes": "node xyz dict, element vert arrays",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return mesh nodes/elements",
     "passes": "ndarray tags/coords",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "add Poly3D/Line3D collections, autoscale",
     "passes": "matplotlib Figure/Axes3D",
     "to": "external"
    }
   ],
   "inputs": "generated Gmsh mesh, styling kwargs",
   "outputs": "self (figure populated)",
   "reads": [
    "gmsh.model.mesh nodes/elements"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.physical_groups",
   "label": "physical_groups",
   "composite": "viz.plot",
   "signature": "Plot.physical_groups(*, dims=None, names=None, cmap='tab20', n_curve_samples=40, point_size=60, linewidth=2.5, surface_alpha=0.35, label_groups=True, show_legend=True, show=False) -> Plot",
   "summary": "g.plot.physical_groups — colour BRep entities by physical group (geometry level, no mesh). Synchronizes OCC, iterates getPhysicalGroups, draws each member per dim with a distinct colormap colour + legend. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:1063",
   "flow": [
    {
     "node": "viz.plot",
     "action": "gmsh.model.occ.synchronize + getPhysicalGroups/getEntitiesForPhysicalGroup",
     "passes": "PG dimtags + member entities",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return PG membership + BRep samples",
     "passes": "ndarray coords per group",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "per-group colored scatter/Line3D/Poly3D + legend",
     "passes": "matplotlib Figure/Axes3D",
     "to": "external"
    }
   ],
   "inputs": "live BRep + physical groups, dims/names filter",
   "outputs": "self (PG-coloured geometry figure)",
   "reads": [
    "gmsh.model physical groups + BRep entities"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.physical_groups_mesh",
   "label": "physical_groups_mesh",
   "composite": "viz.plot",
   "signature": "Plot.physical_groups_mesh(*, dims=None, names=None, cmap='tab20', alpha=0.80, linewidth=0.30, edge_color='white', point_size=50, seg_width=2.5, show_legend=True, show=False) -> Plot",
   "summary": "g.plot.physical_groups_mesh — colour mesh nodes/elements by physical group; per group collects mesh entities on every member and renders in the group colour with a legend. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:1219",
   "flow": [
    {
     "node": "viz.plot",
     "action": "getPhysicalGroups + node lookup + per-member mesh nodes/elements",
     "passes": "PG members, mesh verts",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return PG mesh nodes/elements",
     "passes": "ndarray tags/coords",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "per-group colored scatter/Line3D/Poly3D + legend",
     "passes": "matplotlib Figure/Axes3D",
     "to": "external"
    }
   ],
   "inputs": "generated mesh + physical groups, dims/names filter",
   "outputs": "self (PG-coloured mesh figure)",
   "reads": [
    "gmsh.model physical groups + mesh nodes/elements"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.quality",
   "label": "quality",
   "composite": "viz.plot",
   "signature": "Plot.quality(*, quality_name='minSICN', cmap='RdYlGn', vmin=None, vmax=None, alpha=0.85, linewidth=0.20, show_colorbar=True, show=False) -> Plot",
   "summary": "g.plot.quality — draw dim=2 surface elements coloured by a Gmsh quality metric (minSICN/minSIGE/gamma/...). Builds Normalize+colormap, adds Poly3DCollection with per-face colors and an optional colorbar. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:709",
   "flow": [
    {
     "node": "viz.plot",
     "action": "build node lookup, collect dim=2 elem verts + tags",
     "passes": "element vert arrays, elem tags",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "getElementQualities(tags, qualityName)",
     "passes": "ndarray quality values",
     "to": "viz.plot"
    },
    {
     "node": "viz.plot",
     "action": "normalize→cmap face colors, Poly3DCollection + colorbar",
     "passes": "matplotlib Figure/Axes3D",
     "to": "external"
    }
   ],
   "inputs": "generated mesh, quality metric name, colormap/range",
   "outputs": "self (quality-coloured figure)",
   "reads": [
    "gmsh.model.mesh elements + getElementQualities"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.savefig",
   "label": "savefig",
   "composite": "viz.plot",
   "signature": "Plot.savefig(path, **kwargs) -> Plot",
   "summary": "g.plot.savefig — write the current figure to path (raises if no figure drawn yet); kwargs forwarded to Figure.savefig, bbox_inches defaults to 'tight'. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:1026",
   "flow": [
    {
     "node": "viz.plot",
     "action": "fig.savefig(path, **kwargs)",
     "passes": "image file bytes",
     "to": "external"
    }
   ],
   "inputs": "output path, savefig kwargs",
   "outputs": "self (file written to disk)",
   "reads": [],
   "writes": [
    "figure image file at path"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.show",
   "label": "show",
   "composite": "viz.plot",
   "signature": "Plot.show() -> Plot",
   "summary": "g.plot.show — tight_layout + plt.show() the current figure; handles not reset so chained savefig/draw still target it. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:1011",
   "flow": [
    {
     "node": "viz.plot",
     "action": "fig.tight_layout + plt.show",
     "passes": "matplotlib Figure",
     "to": "external"
    }
   ],
   "inputs": "current figure",
   "outputs": "self (figure displayed)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.plot.use_axes",
   "label": "use_axes",
   "composite": "viz.plot",
   "signature": "Plot.use_axes(ax: Axes3D) -> Plot",
   "summary": "g.plot.use_axes — install an externally-owned 3D Axes as the drawing target (for embedding apeGmsh plots into a larger matplotlib layout). Requires matplotlib. Chainable.",
   "file": "src/apeGmsh/viz/Plot.py:370",
   "flow": [
    {
     "node": "viz.plot",
     "action": "adopt ax.figure and ax as _fig/_ax",
     "passes": "matplotlib Axes3D",
     "to": "viz.plot"
    }
   ],
   "inputs": "external Axes3D",
   "outputs": "self (Plot, chainable)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.preview.preview",
   "label": "preview",
   "composite": "viz.preview",
   "signature": "preview(session=None, *, mode='mesh', dims=None, show_nodes=True, browser=False, return_fig=False) -> plotly.Figure|None",
   "summary": "Top-level apeGmsh.preview (also g.model.preview / g.mesh.preview) unified entry — routes to preview_model or preview_mesh by mode. Builds an interactive WebGL plotly figure from the shared PyVista scene builders.",
   "file": "src/apeGmsh/viz/NotebookPreview.py:677",
   "flow": [
    {
     "node": "viz.preview",
     "action": "dispatch by mode to preview_model/preview_mesh",
     "passes": "mode+dims+flags",
     "to": "viz.preview"
    },
    {
     "node": "viz.preview",
     "action": "delegate scene build + figure",
     "passes": "plotly Figure or None",
     "to": "session"
    }
   ],
   "inputs": "optional session, mode {'model','mesh'}, dims, show_nodes, browser, return_fig",
   "outputs": "plotly Figure (return_fig) else None (inline/browser display)",
   "reads": [
    "gmsh globals via scene builders"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.preview.preview_mesh",
   "label": "preview_mesh",
   "composite": "viz.preview",
   "signature": "preview_mesh(session=None, *, dims=None, show_nodes=True, browser=False, return_fig=False) -> plotly.Figure|None",
   "summary": "Interactive WebGL preview of the mesh — builds an off-screen PyVista mesh_scene, converts per-dim grids to plotly traces plus an optional 'Mesh nodes' scatter cloud; legend doubles as a visibility filter. Requires plotly.",
   "file": "src/apeGmsh/viz/NotebookPreview.py:645",
   "flow": [
    {
     "node": "viz.preview",
     "action": "build_mesh_scene on off-screen pv.Plotter",
     "passes": "PyVista per-dim grids + node arrays",
     "to": "viewer.mesh"
    },
    {
     "node": "viewer.mesh",
     "action": "return scene (registry, node_coords, node_tags)",
     "passes": "PyVista grids + node ndarrays",
     "to": "viz.preview"
    },
    {
     "node": "viz.preview",
     "action": "un-shift, convert grids+nodes to plotly traces",
     "passes": "plotly Mesh3d/Scatter3d traces",
     "to": "viz.preview"
    },
    {
     "node": "viz.preview",
     "action": "make Figure, display inline / browser / return",
     "passes": "plotly Figure",
     "to": "session"
    }
   ],
   "inputs": "optional session, mesh dims, show_nodes, browser/return_fig flags",
   "outputs": "plotly Figure or None (HTML/inline display)",
   "reads": [
    "gmsh.model.mesh via build_mesh_scene",
    "physical groups + labels"
   ],
   "writes": [
    "temp HTML file when browser=True"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.preview.preview_model",
   "label": "preview_model",
   "composite": "viz.preview",
   "signature": "preview_model(session=None, *, dims=None, browser=False, return_fig=False) -> plotly.Figure|None",
   "summary": "Interactive WebGL preview of BRep geometry — builds an off-screen PyVista brep_scene, un-shifts the origin, converts per-dim grids to plotly Mesh3d/Scatter3d traces with PG+label hover tooltips, displays inline / in browser / returns the Figure. Requires plotly.",
   "file": "src/apeGmsh/viz/NotebookPreview.py:597",
   "flow": [
    {
     "node": "viz.preview",
     "action": "build_brep_scene on off-screen pv.Plotter",
     "passes": "PyVista per-dim UnstructuredGrids",
     "to": "viewer.model"
    },
    {
     "node": "viewer.model",
     "action": "return scene registry (dim_meshes, origin_shift)",
     "passes": "PyVista grids + shift ndarray",
     "to": "viz.preview"
    },
    {
     "node": "viz.preview",
     "action": "un-shift grids, build PG/label lookup, convert to plotly traces",
     "passes": "plotly Mesh3d/Scatter3d traces",
     "to": "viz.preview"
    },
    {
     "node": "viz.preview",
     "action": "make Figure, display inline / browser / return",
     "passes": "plotly Figure",
     "to": "session"
    }
   ],
   "inputs": "optional session, BRep dims, browser/return_fig flags",
   "outputs": "plotly Figure or None (HTML/inline display)",
   "reads": [
    "gmsh.model BRep via build_brep_scene",
    "physical groups + labels"
   ],
   "writes": [
    "temp HTML file when browser=True"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.bbox",
   "label": "Selection.bbox",
   "composite": "viz.selection",
   "signature": "Selection.bbox() -> tuple[float,...]",
   "summary": "Axis-aligned bounding box (x0,y0,z0,x1,y1,z1) of the union of all entities in the Selection.",
   "file": "src/apeGmsh/viz/Selection.py:298",
   "flow": [
    {
     "node": "viz.selection",
     "action": "gmsh.model.getBoundingBox per dimtag, min/max reduce",
     "passes": "per-entity bbox",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return bounding boxes",
     "passes": "6-tuple floats",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "reduce to union bbox",
     "passes": "BBox 6-tuple",
     "to": "session"
    }
   ],
   "inputs": "Selection (raises if empty)",
   "outputs": "BBox 6-tuple",
   "reads": [
    "gmsh.model.getBoundingBox"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.centers",
   "label": "Selection.centers",
   "composite": "viz.selection",
   "signature": "Selection.centers() -> np.ndarray",
   "summary": "(N,3) array of entity centroids (parametric midpoint for dim 1/2, getValue for points, occ.getCenterOfMass for volumes; bbox-center fallback).",
   "file": "src/apeGmsh/viz/Selection.py:315",
   "flow": [
    {
     "node": "viz.selection",
     "action": "_entity_center per dimtag via gmsh.model getValue/occ",
     "passes": "centroid xyz",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return entity coords",
     "passes": "ndarray (N,3)",
     "to": "session"
    }
   ],
   "inputs": "Selection",
   "outputs": "ndarray (N,3) centroids",
   "reads": [
    "gmsh.model getValue/occ.getCenterOfMass/getBoundingBox"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.filter",
   "label": "Selection.filter",
   "composite": "viz.selection",
   "signature": "Selection.filter(*, tags=None, exclude_tags=None, labels=None, kinds=None, physical=None, in_box=None, in_sphere=None, on_plane=None, on_axis=None, at_point=None, length_range=None, area_range=None, volume_range=None, aligned=None, horizontal=None, vertical=None, predicate=None) -> Selection",
   "summary": "Re-apply the AND filter battery to an existing (homogeneous-dim) Selection, returning a new refined Selection. Chainable on the snapshot result.",
   "file": "src/apeGmsh/viz/Selection.py:227",
   "flow": [
    {
     "node": "viz.selection",
     "action": "_apply_filters over self.dimtags at self.dim",
     "passes": "filtered DimTags",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "wrap in new Selection",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "filter kwargs (raises on mixed-dim selection)",
   "outputs": "refined Selection",
   "reads": [
    "gmsh.model entity bbox/mass + parent.labels/_metadata as filters require"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.limit",
   "label": "Selection.limit",
   "composite": "viz.selection",
   "signature": "Selection.limit(n: int) -> Selection",
   "summary": "Keep at most n entries of the Selection (head slice). Returns a new Selection.",
   "file": "src/apeGmsh/viz/Selection.py:264",
   "flow": [
    {
     "node": "viz.selection",
     "action": "slice dimtags[:n], wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "n limit",
   "outputs": "truncated Selection",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.masses",
   "label": "Selection.masses",
   "composite": "viz.selection",
   "signature": "Selection.masses() -> np.ndarray",
   "summary": "(N,) array of length/area/volume per entity via occ.getMass (0.0 for points, NaN on failure).",
   "file": "src/apeGmsh/viz/Selection.py:322",
   "flow": [
    {
     "node": "viz.selection",
     "action": "gmsh.model.occ.getMass per dimtag",
     "passes": "mass scalar",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return mass values",
     "passes": "ndarray (N,)",
     "to": "session"
    }
   ],
   "inputs": "Selection",
   "outputs": "ndarray (N,) measures",
   "reads": [
    "gmsh.model.occ.getMass"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.set_algebra",
   "label": "Selection set ops",
   "composite": "viz.selection",
   "signature": "Selection.__or__/__and__/__sub__/__xor__(other: Selection) -> Selection",
   "summary": "Set algebra on frozen Selections — union/intersection/difference/symmetric-difference; result preserves self-then-other order, may become mixed-dim.",
   "file": "src/apeGmsh/viz/Selection.py:211",
   "flow": [
    {
     "node": "viz.selection",
     "action": "set-combine self/other dimtags, re-order, wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "two Selection objects",
   "outputs": "combined Selection",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.sorted_by",
   "label": "Selection.sorted_by",
   "composite": "viz.selection",
   "signature": "Selection.sorted_by(key='x') -> Selection",
   "summary": "Sort the Selection by a coordinate ('x'/'y'/'z'), a metric ('length'/'area'/'volume'/'mass'), or a callable (dim,tag)->float. Returns a new ordered Selection.",
   "file": "src/apeGmsh/viz/Selection.py:268",
   "flow": [
    {
     "node": "viz.selection",
     "action": "compute centers()/masses() or call key per dimtag",
     "passes": "score ndarray",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return centers/masses",
     "passes": "ndarray scores",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "sort by score, wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "sort key (coord/metric/callable)",
   "outputs": "ordered Selection",
   "reads": [
    "gmsh.model entity centers/masses (for coord/metric keys)"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.to_dataframe",
   "label": "Selection.to_dataframe",
   "composite": "viz.selection",
   "signature": "Selection.to_dataframe() -> pd.DataFrame",
   "summary": "DataFrame with columns dim,tag,kind,label,x,y,z,mass — kind from Model._metadata, label from g.labels reverse map, centers/masses from Gmsh.",
   "file": "src/apeGmsh/viz/Selection.py:494",
   "flow": [
    {
     "node": "viz.selection",
     "action": "read model._metadata + labels.reverse_map + centers()/masses()",
     "passes": "kind/label maps + ndarray geom",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return entity centers/masses",
     "passes": "ndarray",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "assemble per-entity rows into DataFrame",
     "passes": "pd.DataFrame",
     "to": "session"
    }
   ],
   "inputs": "Selection",
   "outputs": "pd.DataFrame (dim,tag,kind,label,x,y,z,mass)",
   "reads": [
    "model._metadata",
    "g.labels reverse map",
    "gmsh.model centers/masses"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.to_list",
   "label": "Selection.to_list",
   "composite": "viz.selection",
   "signature": "Selection.to_list() -> list[DimTag]",
   "summary": "Return the Selection as a plain list of (dim, tag) tuples.",
   "file": "src/apeGmsh/viz/Selection.py:339",
   "flow": [
    {
     "node": "viz.selection",
     "action": "list(self._dimtags)",
     "passes": "list[DimTag]",
     "to": "session"
    }
   ],
   "inputs": "Selection",
   "outputs": "list of (dim,tag)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.to_mesh_elements",
   "label": "Selection.to_mesh_elements",
   "composite": "viz.selection",
   "signature": "Selection.to_mesh_elements() -> dict",
   "summary": "Return mesh element data ({'element_ids','connectivity'}) for all entities at the Selection's dim (homogeneous, dim>=1; pads mixed npe with -1). Requires the mesh generated.",
   "file": "src/apeGmsh/viz/Selection.py:428",
   "flow": [
    {
     "node": "viz.selection",
     "action": "gmsh.model.mesh.getElements per dimtag, reshape, pad",
     "passes": "element ids + connectivity",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return mesh elements",
     "passes": "ndarray ids/conn",
     "to": "session"
    }
   ],
   "inputs": "Selection (homogeneous dim>=1, mesh generated)",
   "outputs": "dict {'element_ids': ndarray(E), 'connectivity': ndarray(E,npe)}",
   "reads": [
    "gmsh.model.mesh.getElements"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.to_mesh_nodes",
   "label": "Selection.to_mesh_nodes",
   "composite": "viz.selection",
   "signature": "Selection.to_mesh_nodes() -> dict",
   "summary": "Return mesh node data ({'tags','coords'}) for all entities in the Selection, deduplicated and sorted by tag. Requires the mesh generated (RuntimeError otherwise).",
   "file": "src/apeGmsh/viz/Selection.py:374",
   "flow": [
    {
     "node": "viz.selection",
     "action": "gmsh.model.mesh.getNodes per dimtag, dedupe, sort",
     "passes": "node tags + coords",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return mesh nodes",
     "passes": "ndarray tags/coords",
     "to": "session"
    }
   ],
   "inputs": "Selection (mesh must be generated)",
   "outputs": "dict {'tags': ndarray(N), 'coords': ndarray(N,3)}",
   "reads": [
    "gmsh.model.mesh.getNodes"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.to_physical",
   "label": "Selection.to_physical",
   "composite": "viz.selection",
   "signature": "Selection.to_physical(name: str, *, tag: Tag=-1) -> Tag",
   "summary": "Promote the Selection to a physical group via parent.physical.add (homogeneous-dim, non-empty required). Returns the assigned physical-group tag.",
   "file": "src/apeGmsh/viz/Selection.py:347",
   "flow": [
    {
     "node": "viz.selection",
     "action": "parent.physical.add(dim, tags, name, tag)",
     "passes": "dim + tag list + name",
     "to": "physical"
    },
    {
     "node": "physical",
     "action": "register physical group in Gmsh",
     "passes": "pg_tag int",
     "to": "session"
    }
   ],
   "inputs": "physical-group name, requested tag (-1=auto)",
   "outputs": "assigned physical-group tag",
   "reads": [],
   "writes": [
    "physical group in gmsh.model"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.Selection.to_tags",
   "label": "Selection.to_tags",
   "composite": "viz.selection",
   "signature": "Selection.to_tags() -> list[Tag]",
   "summary": "Return a plain list of entity tags (requires homogeneous dim, else ValueError).",
   "file": "src/apeGmsh/viz/Selection.py:343",
   "flow": [
    {
     "node": "viz.selection",
     "action": "list(self.tags) (raises if mixed dim)",
     "passes": "list[Tag]",
     "to": "session"
    }
   ],
   "inputs": "Selection (homogeneous dim)",
   "outputs": "list of tags",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.adjacent_to",
   "label": "adjacent_to",
   "composite": "viz.selection",
   "signature": "SelectionComposite.adjacent_to(sel: Selection, dim_target: int) -> Selection",
   "summary": "g.model.selection.adjacent_to — return all dim_target entities whose boundary touches any entity in sel (e.g. surfaces bounded by these curves). Returns a new Selection.",
   "file": "src/apeGmsh/viz/Selection.py:874",
   "flow": [
    {
     "node": "viz.selection",
     "action": "occ.synchronize + getEntities(dim_target) + per-entity getBoundary",
     "passes": "candidate DimTags + boundaries",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return entities + boundary sets",
     "passes": "list[DimTag]",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "keep entities whose boundary intersects src set, wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "source Selection, target dimension",
   "outputs": "Selection (adjacent entities at dim_target)",
   "reads": [
    "gmsh.model entities + getBoundary"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.boundary_of",
   "label": "boundary_of",
   "composite": "viz.selection",
   "signature": "SelectionComposite.boundary_of(sel: Selection, *, combined=False) -> Selection",
   "summary": "g.model.selection.boundary_of — wraps gmsh.model.getBoundary(oriented=False) over a Selection's dimtags, returning the boundary as a new Selection.",
   "file": "src/apeGmsh/viz/Selection.py:860",
   "flow": [
    {
     "node": "viz.selection",
     "action": "occ.synchronize + getBoundary(sel.dimtags, oriented=False)",
     "passes": "boundary DimTags",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return boundary entities",
     "passes": "list[DimTag]",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "wrap in Selection",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "source Selection, combined flag",
   "outputs": "Selection (boundary entities)",
   "reads": [
    "gmsh.model.getBoundary"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.closest_to",
   "label": "closest_to",
   "composite": "viz.selection",
   "signature": "SelectionComposite.closest_to(x, y, z, *, dim=0, n=1) -> Selection",
   "summary": "g.model.selection.closest_to — return the n entities of dimension dim whose centroid is nearest to point (x,y,z), sorted by distance. Returns a new Selection.",
   "file": "src/apeGmsh/viz/Selection.py:896",
   "flow": [
    {
     "node": "viz.selection",
     "action": "occ.synchronize + getEntities(dim), compute centroid distances",
     "passes": "entity centroids",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return entities + center coords",
     "passes": "ndarray centroids",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "sort by ‖center−q‖, take n, wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "query point (x,y,z), dim, n",
   "outputs": "Selection (n nearest entities)",
   "reads": [
    "gmsh.model entities + centers"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.picker",
   "label": "picker",
   "composite": "viz.selection",
   "signature": "SelectionComposite.picker(physical_group=None, *, dims=None, **kwargs) -> ModelViewer",
   "summary": "g.model.selection.picker — open the interactive Qt (pyvistaqt+VTK) ModelViewer to create/rename/delete/populate physical groups from the UI; optional physical_group pre-loads an existing group's members. Returns the viewer with .selection/.tags/.to_physical after close.",
   "file": "src/apeGmsh/viz/Selection.py:559",
   "flow": [
    {
     "node": "viz.selection",
     "action": "construct ModelViewer(parent, model, physical_group, dims, **kwargs)",
     "passes": "ModelViewer instance",
     "to": "viewer.model"
    },
    {
     "node": "viewer.model",
     "action": "p.show() interactive Qt window; stage PGs",
     "passes": "user-picked DimTags",
     "to": "physical"
    },
    {
     "node": "viz.selection",
     "action": "return viewer for post-close inspection",
     "passes": "ModelViewer (.selection/.tags)",
     "to": "session"
    }
   ],
   "inputs": "optional physical_group name, dims, viewer kwargs",
   "outputs": "ModelViewer instance (selection/tags available after close)",
   "reads": [
    "gmsh.model BRep + physical groups"
   ],
   "writes": [
    "physical groups (on viewer close)"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.select_all",
   "label": "select_all",
   "composite": "viz.selection",
   "signature": "SelectionComposite.select_all(dim=-1, *, <same filter kwargs>) -> Selection",
   "summary": "g.model.selection.select_all — select entities of a given dim, or with dim=-1 across all dims (filters applied per-dim-slice then concatenated). Returns a frozen Selection (may be mixed-dim).",
   "file": "src/apeGmsh/viz/Selection.py:807",
   "flow": [
    {
     "node": "viz.selection",
     "action": "dim>=0 → _query(dim); dim=-1 → getEntities per dim 0..3",
     "passes": "DimTag universe(s)",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return entities per dim",
     "passes": "list[DimTag]",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "_apply_filters per slice, concatenate, wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "dim (-1=all), filter kwargs",
   "outputs": "Selection (single or mixed-dim snapshot)",
   "reads": [
    "gmsh.model entities all dims"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.select_curves",
   "label": "select_curves",
   "composite": "viz.selection",
   "signature": "SelectionComposite.select_curves(*, tags=None, exclude_tags=None, labels=None, kinds=None, physical=None, in_box=None, in_sphere=None, on_plane=None, on_axis=None, at_point=None, length_range=None, area_range=None, volume_range=None, aligned=None, horizontal=None, vertical=None, predicate=None) -> Selection",
   "summary": "g.model.selection.select_curves — same filter battery applied to dim=1 BRep curves (length_range/horizontal/vertical/aligned are curve-specific); returns a frozen Selection.",
   "file": "src/apeGmsh/viz/Selection.py:705",
   "flow": [
    {
     "node": "viz.selection",
     "action": "occ.synchronize + getEntities(dim=1)",
     "passes": "DimTag universe",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim=1 entities",
     "passes": "list[DimTag]",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "_apply_filters then wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "filter kwargs",
   "outputs": "Selection (dim=1 snapshot)",
   "reads": [
    "gmsh.model dim=1 entities + bbox/mass for metric filters"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.select_points",
   "label": "select_points",
   "composite": "viz.selection",
   "signature": "SelectionComposite.select_points(*, tags=None, exclude_tags=None, labels=None, kinds=None, physical=None, in_box=None, in_sphere=None, on_plane=None, on_axis=None, at_point=None, length_range=None, area_range=None, volume_range=None, aligned=None, horizontal=None, vertical=None, predicate=None) -> Selection",
   "summary": "g.model.selection.select_points — synchronize OCC, take all dim=0 entities, AND-apply the identity/spatial/metric/predicate filter battery, return a frozen Selection snapshot.",
   "file": "src/apeGmsh/viz/Selection.py:671",
   "flow": [
    {
     "node": "viz.selection",
     "action": "occ.synchronize + getEntities(dim=0)",
     "passes": "DimTag universe",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim=0 entities",
     "passes": "list[DimTag]",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "_apply_filters (identity/spatial/metric/predicate)",
     "passes": "filtered DimTags",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "wrap in frozen Selection",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "filter kwargs (tags/labels/kinds/physical/spatial/metric/predicate)",
   "outputs": "Selection (dim=0 snapshot)",
   "reads": [
    "gmsh.model dim=0 entities",
    "parent.labels/model._metadata for identity filters"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.select_surfaces",
   "label": "select_surfaces",
   "composite": "viz.selection",
   "signature": "SelectionComposite.select_surfaces(*, tags=None, exclude_tags=None, labels=None, kinds=None, physical=None, in_box=None, in_sphere=None, on_plane=None, on_axis=None, at_point=None, length_range=None, area_range=None, volume_range=None, aligned=None, horizontal=None, vertical=None, predicate=None) -> Selection",
   "summary": "g.model.selection.select_surfaces — filter battery applied to dim=2 BRep surfaces (area_range is surface-specific); returns a frozen Selection.",
   "file": "src/apeGmsh/viz/Selection.py:739",
   "flow": [
    {
     "node": "viz.selection",
     "action": "occ.synchronize + getEntities(dim=2)",
     "passes": "DimTag universe",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim=2 entities",
     "passes": "list[DimTag]",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "_apply_filters then wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "filter kwargs",
   "outputs": "Selection (dim=2 snapshot)",
   "reads": [
    "gmsh.model dim=2 entities + bbox/mass"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.selection.SelectionComposite.select_volumes",
   "label": "select_volumes",
   "composite": "viz.selection",
   "signature": "SelectionComposite.select_volumes(*, tags=None, exclude_tags=None, labels=None, kinds=None, physical=None, in_box=None, in_sphere=None, on_plane=None, on_axis=None, at_point=None, length_range=None, area_range=None, volume_range=None, aligned=None, horizontal=None, vertical=None, predicate=None) -> Selection",
   "summary": "g.model.selection.select_volumes — filter battery applied to dim=3 BRep volumes (volume_range is volume-specific); returns a frozen Selection.",
   "file": "src/apeGmsh/viz/Selection.py:773",
   "flow": [
    {
     "node": "viz.selection",
     "action": "occ.synchronize + getEntities(dim=3)",
     "passes": "DimTag universe",
     "to": "gmsh"
    },
    {
     "node": "gmsh",
     "action": "return dim=3 entities",
     "passes": "list[DimTag]",
     "to": "viz.selection"
    },
    {
     "node": "viz.selection",
     "action": "_apply_filters then wrap",
     "passes": "Selection snapshot",
     "to": "session"
    }
   ],
   "inputs": "filter kwargs",
   "outputs": "Selection (dim=3 snapshot)",
   "reads": [
    "gmsh.model dim=3 entities + occ.getCenterOfMass/getMass"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.VTKExport",
   "label": "VTKExport",
   "composite": "viz.vtkexport",
   "signature": "VTKExport(ctx: apeGmsh, dim: int|None=2)",
   "summary": "High-level VTU writer bound to an apeGmsh instance — pulls FEMData via ctx.mesh.queries.get_fem_data(dim), builds node-id→index map and per-group 0-based connectivity with VTK cell types, ready to accumulate fields.",
   "file": "src/apeGmsh/viz/VTKExport.py:291",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "ctx.mesh.queries.get_fem_data(dim) to obtain FEMData",
     "passes": "FEMData (nodes/elements)",
     "to": "fem.extract"
    },
    {
     "node": "fem.extract",
     "action": "return broker (coords/ids/connectivity groups)",
     "passes": "FEMData arrays",
     "to": "viz.vtkexport"
    },
    {
     "node": "viz.vtkexport",
     "action": "build 0-based cell blocks + VTK type per group",
     "passes": "conn ndarray + vtk_type",
     "to": "viz.vtkexport"
    }
   ],
   "inputs": "apeGmsh ctx, dim",
   "outputs": "VTKExport instance with cell blocks ready",
   "reads": [
    "FEMData via ctx.mesh.queries.get_fem_data"
   ],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.VTKExport.add_elem_scalar",
   "label": "add_elem_scalar",
   "composite": "viz.vtkexport",
   "signature": "VTKExport.add_elem_scalar(name: str, data: np.ndarray) -> None",
   "summary": "Accumulate an element scalar field (one value per element) into cell_data for the next write().",
   "file": "src/apeGmsh/viz/VTKExport.py:347",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "ravel float64 into _cell_data[name]",
     "passes": "ndarray (M,)",
     "to": "viz.vtkexport"
    }
   ],
   "inputs": "field name, (M,) array",
   "outputs": "None (field staged)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.VTKExport.add_elem_vector",
   "label": "add_elem_vector",
   "composite": "viz.vtkexport",
   "signature": "VTKExport.add_elem_vector(name: str, data: np.ndarray) -> None",
   "summary": "Accumulate an element vector field (3 components/element; (M,2) auto-padded with z=0) into cell_data.",
   "file": "src/apeGmsh/viz/VTKExport.py:351",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "pad (M,2)→(M,3), store in _cell_data",
     "passes": "ndarray (M,3)",
     "to": "viz.vtkexport"
    }
   ],
   "inputs": "field name, (M,3)/(M,2) array",
   "outputs": "None (field staged)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.VTKExport.add_node_scalar",
   "label": "add_node_scalar",
   "composite": "viz.vtkexport",
   "signature": "VTKExport.add_node_scalar(name: str, data: np.ndarray) -> None",
   "summary": "Accumulate a nodal scalar field (one float64 value per node) into point_data for the next write().",
   "file": "src/apeGmsh/viz/VTKExport.py:331",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "ravel float64 into _point_data[name]",
     "passes": "ndarray (N,)",
     "to": "viz.vtkexport"
    }
   ],
   "inputs": "field name, (N,) array",
   "outputs": "None (field staged)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.VTKExport.add_node_vector",
   "label": "add_node_vector",
   "composite": "viz.vtkexport",
   "signature": "VTKExport.add_node_vector(name: str, data: np.ndarray) -> None",
   "summary": "Accumulate a nodal vector field (3 components/node; (N,2) auto-padded with z=0) into point_data.",
   "file": "src/apeGmsh/viz/VTKExport.py:335",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "validate 2-D, pad (N,2)→(N,3), store",
     "passes": "ndarray (N,3)",
     "to": "viz.vtkexport"
    }
   ],
   "inputs": "field name, (N,3)/(N,2) array",
   "outputs": "None (field staged)",
   "reads": [],
   "writes": [],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.VTKExport.write",
   "label": "VTKExport.write",
   "composite": "viz.vtkexport",
   "signature": "VTKExport.write(filename: str|Path='results.vtu') -> Path",
   "summary": "Write accumulated point/cell fields + the primary connectivity block to a single .vtu file (delegates to write_vtu).",
   "file": "src/apeGmsh/viz/VTKExport.py:371",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "select primary conn+type, call write_vtu",
     "passes": "points/cells/point_data/cell_data",
     "to": "viz.vtkexport"
    },
    {
     "node": "viz.vtkexport",
     "action": "serialize VTU XML to disk",
     "passes": ".vtu file",
     "to": "external"
    }
   ],
   "inputs": "output filename",
   "outputs": "Path to written .vtu",
   "reads": [],
   "writes": [
    ".vtu file"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.VTKExport.write_mode_series",
   "label": "write_mode_series",
   "composite": "viz.vtkexport",
   "signature": "VTKExport.write_mode_series(base_name: str, mode_shapes: list[np.ndarray], frequencies: list[float]) -> list[Path]",
   "summary": "Write mode shapes (+magnitude) as a time-series of .vtu files plus a .pvd collection (frequency = timestep) for ParaView animation.",
   "file": "src/apeGmsh/viz/VTKExport.py:383",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "build per-mode steps {time,point_data}, call write_vtu_series",
     "passes": "phi3 ndarray + magnitude",
     "to": "viz.vtkexport"
    },
    {
     "node": "viz.vtkexport",
     "action": "write N .vtu + .pvd collection",
     "passes": ".vtu/.pvd files",
     "to": "external"
    }
   ],
   "inputs": "base name, mode_shapes list, frequencies list",
   "outputs": "list of written Paths (.vtu + .pvd)",
   "reads": [],
   "writes": [
    ".vtu series + .pvd file"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.write_vtu",
   "label": "write_vtu",
   "composite": "viz.vtkexport",
   "signature": "write_vtu(filename, points, cells, *, vtk_cell_type=VTK_QUAD, point_data=None, cell_data=None, binary=True) -> Path",
   "summary": "Standalone (no-apeGmsh) core writer — emit a VTK UnstructuredGrid .vtu from raw points/0-based connectivity with optional point/cell data, base64-binary or ASCII. Pure numpy + stdlib.",
   "file": "src/apeGmsh/viz/VTKExport.py:86",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "build ElementTree (Points/Cells/PointData/CellData), encode arrays",
     "passes": "ndarray points/cells/fields",
     "to": "viz.vtkexport"
    },
    {
     "node": "viz.vtkexport",
     "action": "ElementTree.write to file",
     "passes": ".vtu XML file",
     "to": "external"
    }
   ],
   "inputs": "filename, points (N,3), cells (M,npe) 0-based, vtk_cell_type, point/cell data, binary",
   "outputs": "Path to written .vtu",
   "reads": [],
   "writes": [
    ".vtu file"
   ],
   "_cluster": "J2_misc"
  },
  {
   "id": "viz.vtkexport.write_vtu_series",
   "label": "write_vtu_series",
   "composite": "viz.vtkexport",
   "signature": "write_vtu_series(base_name, points, cells, *, vtk_cell_type=VTK_QUAD, steps: list[dict]) -> list[Path]",
   "summary": "Standalone time-series writer — emit one .vtu per step plus a .pvd Collection (timestep from each step's 'time') for ParaView animation. Pure numpy + stdlib.",
   "file": "src/apeGmsh/viz/VTKExport.py:232",
   "flow": [
    {
     "node": "viz.vtkexport",
     "action": "loop steps → write_vtu each + build .pvd Collection XML",
     "passes": "ndarray + per-step data",
     "to": "viz.vtkexport"
    },
    {
     "node": "viz.vtkexport",
     "action": "write .vtu files + .pvd",
     "passes": ".vtu/.pvd files",
     "to": "external"
    }
   ],
   "inputs": "base name, points, cells, steps list of {time,point_data,cell_data}",
   "outputs": "list of Paths (.vtu series + .pvd)",
   "reads": [],
   "writes": [
    ".vtu series + .pvd file"
   ],
   "_cluster": "J2_misc"
  }
 ]
}