Services Layer

7.1 Using OrbitronServices (Rust API)

use orbitron_services::{OrbitronServices, ServicesConfig, Backend};

fn main() -> anyhow::Result<()> {
    // Configure headless services (suitable for CLI tools or batch jobs).
    let config = ServicesConfig {
        enable_gpu: false,
        max_file_size: 2 * 1024 * 1024 * 1024, // 2 GiB cap
        max_atoms: 0,                          // 0 = unlimited
        max_bonds: 0,                          // 0 = unlimited
        max_memory: 0,                         // 0 = unlimited
        verbose: true,
        render_backend: Backend::Headless,
        data_root: Some(std::env::current_dir()?),
    };

    let services = OrbitronServices::new(config)?;
    let scene = services.load("fixtures/benzene.xyz")?;

    // Measurements and derived data
    // Atom ids are 1-based, matching each format's native numbering.
    let distance = services.distance(&scene, 1, 2)?;
    let bbox = services.analyzer().bounding_box(&scene)?;

    // Export / render
    services.export(&scene, "benzene.pdb")?;
    services
        .renderer()
        .render_to_file(&scene, "benzene.png", 1920, 1080, None)?;

    Ok(())
}
  • Prefer the façade methods (load, distance, export) for convenience. The sub-services stay accessible via getters if you need finer control (services.loader().load_frequencies).
  • Swap in an alternative data source via OrbitronServices::with_data_source and a custom DataSource; the CLI and Python bridge follow this pattern.
  • Persistent configuration lives in config.rs. OrbitronConfig resolves the platform-specific config directory via directories::ProjectDirs; both the CLI and GUI load it on start and merge it with per-run flags or environment overrides.
  • Use renderer_mut() when you need to mutate renderer options (e.g., toggling lighting presets before drawing headless renders).
  • streaming::TrajectoryStream integrates with async runtimes—feed frames into custom analysis loops without blocking the UI. The viewer uses it to stream long trajectories while rendering.

7.2 Resource reports

capacity::ResourceReport keeps source parsing and decoded scene costs as separate stages. Frontends should preserve that split. A raw NWChem output can be expensive to parse but produce a small scene, while a compact bundle can decode into a large trajectory or grid.

The report uses these types:

Type Contract
FileOpenEstimate Source bytes, format multiplier, optional count-derived decoded-memory minimum, projected incremental host memory, available-memory target, and separate format/hint confidence
SceneResourceInputs Primary scene counts, a nested render request, total frames, resident records, packed positions, grid points, and requested features
SceneRenderInputs Visible atom and bond instances, an optional viewport, and prepared mesh counts
SceneRenderEstimate Exact instance, mesh, and readback buffers plus logical color and depth attachment bytes
SceneResourceEstimate Approximate scene records, the render estimate, exact packed coordinate and grid payloads, and costs that remain unestimated
ByteEstimate Tagged exact, projected, or range JSON so a frontend cannot mistake a heuristic for a measurement
UnestimatedCost Requested work such as labels, bond inference, surface meshes, electronic structure, and undo history that lacks a defensible byte model
ReliefOption A cheaper path the frontend may offer explicitly, such as disabling labels or retaining bounded trajectory storage

frame_count means frames available. It does not mean frames resident. Use resident_atom_records and resident_bond_records for eager snapshots and frame caches, and packed_position_count for a packed f32 coordinate attachment. This distinction is required for indexed and packed trajectories: 10,000 available frames with a three-frame cache must not be estimated as 10,000 expanded SceneSnapshot values.

FileKind::estimate_with_hints keeps the calibrated source-size estimate as a floor. Header or bounded-scan counts can raise that projection through atom records, additional-frame positions for sources that advertise a trajectory, and raw volumetric values, but they never lower it. Passing None returns the same value as FileKind::estimate. A prefix_lower_bound hint therefore catches an already-large prefix without pretending it describes the unscanned tail. prefix_observed has a different meaning: an application printed that count in the prefix, but a later job stage may describe another system. Frontends must not present it as a lower bound for the complete file.

Bundle inspection also reports packed_trajectory_bytes separately from total uncompressed attachment bytes. The bundle-derived retained-scene floor adds atom records, resident trajectory positions, selected grid values, and the packed coordinate attachment. Bundle opening applies the bundle multiplier to the larger of stored bytes and that floor because manifest decoding and scene reconstruction create transient copies. Do not apply the same rule to every attachment: a bundled raw source is retained for provenance and import but is not decoded during an ordinary scene open.

Current writers serialize the same fixed-shape facts to resources.json before manifest.json. inspect_resources_path caps the actual uncompressed record at 64 KiB and returns without inflating the manifest or attachments. The measured million-atom record is 352 bytes beside a 114.8 MB manifest. Legacy bundles without the entry retain bounded manifest inspection.

Treat the compact record only as preflight evidence. container::open rederives it from the parsed canonical payload and refuses a mismatch before exposing the bundle. Code that sizes an allocation or consumes an attachment must use the validated full-open path, not the resource-only inspection result.

capacity::estimate_file_open(path, mode) is the shared local-file entry point. It combines file metadata, FileKind classification, and the bounded source inspector, then retains the size multiplier as the floor. The desktop, CLI, Python bridge, and Rust SDK gates all pass that returned FileOpenEstimate to Operation::EstimatedFileOpen. Do not rebuild a file-open operation from byte length alone in a frontend; doing so discards the counts that can expose a small file declaring a very large scene.

The render estimate gets instance and mesh sizes from Rust’s active layouts. Those source, staging, vertex, index, uniform, and readback buffer payloads are exact before allocator or device padding. Viewport color and depth attachments are marked layout_derived: Orbitron knows their logical pixel and sample counts, but Metal, Vulkan, DX12, and WebGPU may allocate more. The largest-buffer field covers buffers only, not textures.

Prepared meshes have exact counts. A requested surface that has not been extracted does not. It remains UnestimatedCost::SurfaceMeshes until the marching-cubes or cartoon result exists, which prevents a grid-size heuristic from masquerading as a known triangle count. Scene record storage uses the backbone’s conservative per-record figures and stays projected. Variable-size properties and other unmeasured work also stay in unestimated_costs; do not fold them into an apparently complete total.

capacity::bundle_preparation_command returns a shell-safe canonical export command only when bounded inspection recognizes a raw source. It returns None for unknown inputs and existing .orbpack files. Resource gates attach that command to ResourceReport::bundle_preparation_command only when the source reaches a warning band. The same report also adds prepare_bundle_near_source to relief_options. CLI and TUI warnings print the command directly. The desktop warning displays it before opening, and Scene Info retains it with a copy control after the scene loads.