chemunited-core
Core data models for orchestration, execution, and simulation of automated chemistry platforms.
chemunited-core is the shared schema and runtime-model layer for the ChemUnited stack. It gives downstream packages a consistent way to describe equipment, inter-component connections, internal topology, and unit-aware physical quantities.
Installation
Requires Python >=3.11.
pip install chemunited-core
For local development:
pip install -e ".[dev]"
Architecture
The editable architecture diagram lives at docs/chemunited-core-architecture.drawio.
The package follows one core pattern everywhere:
Pydantic *Mode -> Element.from_mode(...) -> dataclass *Data -> consumer package
|
+-> Element.update(...) -> sync_internal_state()
*Modeclasses validate config, UI, or protocol input.*Dataclasses hold the runtime representation used by orchestration, visualization, or simulation layers.Element.from_mode(mode)builds fully initialized dataclass instances.Element.update(mode)applies only explicitly provided fields, then refreshes derived runtime state throughsync_internal_state().
Public API
Import public symbols from subpackages such as chemunited_core.components and chemunited_core.connections. There is currently no single top-level export module intended to be the main public surface.
| Module | Purpose | Main public objects |
|---|---|---|
chemunited_core.components |
Process equipment models | ComponentMode, ComponentData, FlowSourceMode, FlowSourceData, JunctionMode, JunctionData, PlugFlowMode, PlugFlowComponentData, PressureControlMode, PressureControlData, BackPressureRegulatorMode, BackPressureRegulatorData, ValveMode, ValveComponentData, VesselMode, VesselComponentData |
chemunited_core.connections |
Inter-component edges | EdgeMode, EdgeData, ConnectionType |
chemunited_core.common |
Shared enums and mode-to-data bridge | Element, ConnectionType, GroupParameterCategory |
chemunited_quantities |
Unit-aware physical quantities | ChemUnitQuantity, ChemQuantityValidator, units_for_dimension, ureg |
chemunited_core.compounds |
Inventory payload objects | VolumeContentBase |
Component Catalog
| Model pair | Role | Runtime topology |
|---|---|---|
ComponentMode / ComponentData |
Base component contract | Two hydraulic ports by default, no inventory |
FlowSourceMode / FlowSourceData |
Fixed-flow boundary | One hydraulic port with a FLOW boundary condition |
PressureControlMode / PressureControlData |
Fixed-pressure boundary | One hydraulic port with a PRESSURE boundary condition |
PlugFlowMode / PlugFlowComponentData |
Tube or reactor channel | Two hydraulic ports joined by one transport edge |
JunctionMode / JunctionData |
Splitter or combiner | N external ports connected to hub port 0 through junction edges |
Gantry3DMode / Gantry3DData |
Autosampler head and tray selector | Head port 1 is atmospheric when idle and connects only to the selected movement port when inserted |
ValveMode / ValveComponentData |
Rotary switching element | All possible internal routes are compiled; only active routes stay open |
BackPressureRegulatorMode / BackPressureRegulatorData |
Pressure-controlled inline valve | Two ports joined by a normally closed internal edge |
VesselMode / VesselComponentData |
Storage and phase inventory | Top and bottom hydraulic ports plus one heat port and one inventory node |
Consumer Contract
Another package can safely rely on the following behavior:
Element.from_mode(mode)accepts a Pydantic model and returns the matching dataclass with derived runtime fields already built.Element.update(mode)patches only fields explicitly set on the incoming mode object and then callssync_internal_state().- Every
ComponentDatainstance exposesname,figure,position,angle,component_type,port_pairs,ports_by_number,internal_edges, andinternal_inventory. ports_by_numbercontainsPortobjects with stable fields such asnumber,component,category,relative_position,access,closure, and optionalboundary.Gantry3DDatauses port1as the autosampler head. When the head is not inserted into a valid tray position, that port carries an atmosphericPRESSUREboundary; when inserted, only the selected head-to-tray internal edge is open.- Vial-array wells remain
MOVEMENTports for tray topology, but whenpressure_access=Falseeach well port carries an atmosphericPRESSUREboundary for autosampler consumers. internal_edgescontainsInternalEdgeobjects keyed by(origin_port, destination_port)or(origin_port, "Inventory").internal_inventoryis eitherNoneor anInventoryNodethat storesliq_contentandgas_content.EdgeDataexposesorigin,destination,origin_port,destination_port,classification,length,diameter,straight_path, andair_pressure_line.EdgeData.nameis a stable identifier in the form<origin>_<origin_port>_<destination>_<destination_port>.ChemUnitQuantitystores unit-aware values. Use.to_base_units().magnitudewhen your consumer needs SI floats.- Non-hydraulic
EdgeModeclassifications normalizelengthanddiameterto0 mm. EdgeModeacceptsdestinyanddestiny_portas aliases for backward compatibility.
Using It From Another Package
Create validated *Mode objects, compile them to *Data, then read the runtime topology.
from chemunited_core.components import (
FlowSourceData,
FlowSourceMode,
PlugFlowComponentData,
PlugFlowMode,
VesselComponentData,
VesselMode,
)
from chemunited_core.connections import EdgeData, EdgeMode
source = FlowSourceData.from_mode(
FlowSourceMode(
name="FeedPump",
figure="PumpFigure",
position=(0.0, 0.0),
angle=0,
flow_rate="5 ml/min",
)
)
reactor = PlugFlowComponentData.from_mode(
PlugFlowMode(
name="ReactorTube",
figure="TubeFigure",
position=(2.0, 0.0),
angle=0,
length="500 mm",
diameter="2 mm",
)
)
receiver = VesselComponentData.from_mode(
VesselMode(
name="Receiver",
figure="FlaskFigure",
position=(4.0, 0.0),
angle=0,
capacity="250 ml",
top_access=3,
bottom_access=2,
)
)
edge_a = EdgeData.from_mode(
EdgeMode(
origin=source.name,
destination=reactor.name,
origin_port=1,
destination_port=1,
length="100 mm",
diameter="1.6 mm",
)
)
edge_b = EdgeData.from_mode(
EdgeMode(
origin=reactor.name,
destination=receiver.name,
origin_port=2,
destination_port=1,
length="150 mm",
diameter="1.6 mm",
)
)
components = [source, reactor, receiver]
connections = [edge_a, edge_b]
Inspect the compiled topology through public runtime fields:
for component in components:
print(component.name, sorted(component.ports_by_number))
print(component.port_pairs)
print(component.internal_inventory)
for edge_key, internal_edge in reactor.internal_edges.items():
print(edge_key, internal_edge.length, internal_edge.diameter)
Apply updates through update() so derived state stays in sync:
source.update(FlowSourceMode(flow_rate="8 ml/min"))
reactor.update(PlugFlowMode(length="750 mm", diameter="1.0 mm"))
That patch-style update behavior is especially useful when another package stores partial UI edits, protocol commands, or configuration diffs.
Ports, Internal Edges, and Inventory
For downstream packages, the most important compiled objects are:
Port: the externally connectable point on a component. GUI or graph packages usually readrelative_position,category,access,closure, andboundary.InternalEdge: the directed edge inside a component. Simulation packages usually readlength,diameter,role, andresistance_override.InventoryNode: the lumped storage node for vessels and similar components.
These objects live in chemunited_core.components.internals and are populated by each component's internal_structure() implementation.
Units and Quantities
chemunited-core uses Pint-backed quantities from the
chemunited-quantities package.
- Mode fields accept strings such as
"5 ml/min","250 ml","1.2 bar", or"500 mm". - Runtime helpers such as
flow_rate_si,setpoint_pa,length_value,diameter_value, andcapacity_valueexpose SI magnitudes where needed. - Import
ChemUnitQuantity,ChemQuantityValidator,units_for_dimension, and the shareduregdirectly fromchemunited_quantities.
Examples
See examples/build_valve_graph.py for a runnable example that builds a small hydraulic setup and inspects component ports, internal edges, and process connections.
Run it from the repository root:
python examples/build_valve_graph.py
If pyvis is installed, the example also writes an HTML graph to examples/output/valve_flow_graph.html.
Development
The current verified quality gate is:
pre-commit run --all-files
Useful local checks:
python -c "import sys; from pathlib import Path; sys.path.insert(0, str(Path('src').resolve())); import chemunited_core.components"
python examples/build_valve_graph.py
A minimal import smoke test lives in tests/test_imports.py.
License
See LICENSE.
Metadata
Release files for chemunited-core 0.0.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| chemunited_core-0.0.8.tar.gz | 90.7 kB | With provenance | Details for chemunited_core-0.0.8.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| chemunited_core-0.0.8-py3-none-any.whl | Python 3 | none | any | Details for chemunited_core-0.0.8-py3-none-any.whl |
Total release size: 205.0 kB
Release files / chemunited_core-0.0.8.tar.gz
| Download URL | chemunited_core-0.0.8.tar.gz |
|---|---|
| Size | 90.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7fd73a5d1bdf169b5b074d4a82e51eaf73514335e1820bd5876aa25425a27b18
|
|
BLAKE2b-256 checksum How to use checksums |
9a49c5065a5537dc3a41ff22eeea4314c1ed597c2c9e7ea333f5d27f6064a5d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / chemunited_core-0.0.8-py3-none-any.whl
| Download URL | chemunited_core-0.0.8-py3-none-any.whl |
|---|---|
| Size | 114.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
36f7d6102ffc0fe42bd0cfaa95d86d0f4cfd1b666a6cfba154826c843f153dbf
|
|
BLAKE2b-256 checksum How to use checksums |
564d81fac5dc8705168a690c5f3856785eafef3d3c3051753a6395133c4d0ad5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for chemunited-core 0.0.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for chemunited_core-0.0.8.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.