📜 Abstract & Introduction
Despite exponential growth in CPU & GPU bandwidth, many computer-aided engineering (CAE) software tools still cannot fully leverage hardware across teams and systems. To solve this deficiency, we propose an efficient universal computational pipeline for geometry preparation, physics definition, condition assignment, solver numerics, and other useful data-processing functions common to computational fluid dynamics (CFD) and finite element analysis (FEA).
The objects and interfaces of the XCOMPUTE pipeline are detailed here: an executable instruction comprises an algorithm/verb bound to one or more argument(s)/noun(s) and sequenced into physical models and solvers to define high-level workflow procedures. Instructions and sequences are dynamically assembled from human-facing building blocks to allow a variety of customizations and optimizations. Algorithms can be defined by static host bytecode and/or implemented as a device kernel as part of a compiled OpenCL program, callable from a solver program. Property-key inputs, outputs, and constants permit algorithms to connect in an executable sequence or be invoked individually as part of a polymorphic compute framework.
⚠️ Problem Statement
Hardware vs Software Performance Gap
High-performance computing performance has roughly doubled every year over the past few decades. For example, the Rmax metric for LINPACK used by the Top500 survey shows a steady, exponential progression from 59.7 gigaflops in June 1993 to 1,102 petaflops (1.102 exaflops) in June 2022 [4] — yielding an 18 million-fold increase over 29 years. This dramatic increase is the result of a spectrum of technical innovations in circuit fabrication technology, CPU architecture, clustering, vectorization, GPU-based accelerators, etc.
Software for computer-aided engineering and simulation benefits from some of these innovations, but often cannot keep pace with the broad spectrum of progress. As engineering becomes increasingly dependent on computation, the capabilities of software limit engineering productivity and responsiveness of designs to new insights. Allowing engineers to take greater advantage of these innovations increases the number of available design iterations or the fidelity of simulations.
Engineering Inefficiencies & Cost Escalation
A 2014 survey of 248 manufacturers found that engineers report spending a third of their time on non-value added work, and 20% of their time working with outdated information, often resulting in wasted effort and rework [5]. An analysis of survey responses determined that the largest contributor to non-value added time is related to trying to find information, indicating that data management practices have a significant impact on engineering efficiency.
Furthermore, a 2004 NASA study found that the cost of fixing errors at later stages in a project can become over 1,000 times more expensive than earlier stages in the product development lifecycle, revealing that costs escalate exponentially [6]. Increased emphasis on finding errors early in the project lifecycle means spending more time and a larger percentage of project costs in the definition phases of a project — more than is usually allocated to the early phases.
Core Philosophy & Design Goals
In our view, it is a grand challenge to develop a unified framework that implements powerful, disparate numerical algorithms such as these and executes them concurrently across heterogeneous, distributed computing resources. It is our intent to deploy this framework in a sustainable way that benefits a broad community of engineers.
The goal of this paper is to help developers build well-encapsulated and interoperable modules using simple building blocks. We believe clean inheritance models and abstracted code provide the foundation for design, development, and improvement of integrated CAE systems. The primary characteristic of an orthogonal base set of systems and algorithms is that every concept is unique and presents its own characteristic benefits and challenges with surrounding code and machinery. The agglomeration and arrangement of these smaller units constitutes a complex numerical process or workflow; such a standardization is the only way for a performant unification to minimize error and maximize return.
📦 Software Stack & Abstraction Layers
XCOMPUTE's design converges on approximately 400 translation units (orthogonal class concepts) and four fundamental software layers at the intersection of human and machine requirements. The code leverages object-oriented template meta-programming, functional programming, and runtime code-generation techniques to maximize ease of development and user modularity, improve pattern and execution regularity, and organize meaningful object hierarchies.
1. Messages (Schema)
Protobuf-based serialization (BSD-3-Clause) [2, 3]. Cross-platform, language-agnostic data exchange. vector.proto, spatial.proto, concept.proto, meta.proto.
2. Common (Protocol)
XCTP transport over TCP/IP. Self-describing headers, revision tracking, permission-aware I/O. Manages client-server state synchronization.
3. Server/Client (Application)
Real-time state synchronization, recursive system management, GUI/CLI interfaces. Leverages hardware-acceleration at coarse (CPU) and fine (GPU) levels.
4. OpenCL/OpenGL (SIMD Runtime)
JIT kernel generation, heterogeneous CPU/GPU dispatch, dynamic polymorphic execution. Emulates C++ dynamic-dispatch across available devices.
🧩 Core Building Blocks
Property-Key (PK)
A property-key (PK) is the composite of a property tag, followed by optional modifier tags, enabling:
- Human and machine readable data tagging and retrieval
- Ideal lexicographical sorting & searching (in-session)
- Compatibility comparison by string name (out-of-session)
- Dynamic I/O for algorithms using modular substitution (in-code)
Using property-keys, applications can dynamically allocate memory for data scalars, spatial vectors, or tensors by referencing a particular geometry's cardinality and dimensionality. Therefore, memory allocation for varying objects is automatically handled by the bound instruction and/or sequence as part of the larger program. Additionally, dimensional analysis of physical units is easily summed between property and modifier stages, enabling physical unit calculation and implementation of Buckingham π theorem [7].
In numeric applications, all property and modifier objects are constructed with a static singleton-like pattern; semi-unique PKs are built from these. Map ordering is determined using an overloaded less-than (<) operator, comparing homonumerical and heteronumerical PK stages: {Null} < {Property} < {Property, Modifier} < {Property, Modifier, Modifier}, ...
Given P properties and M modifiers, we can compute the number of unique property-keys C_k at modifier stage k: C_k = P × M^k. The total number of uniques PK's is C_total, computed as the sum of C_k across all used modifiers to arbitrary depth K — effectively infinite when using unlimited number of modifier stages: C_total = Σ(P × M^k) for k=0 to K.
At the time of publication, xcompute has defined P ~ 90 properties, M ~ 40 modifiers, roughly half with intrinsic attributes. More will be added as applications prove necessary.
Data Containers
A Data container maps PK to vectors. Members include: Name, Series, Revision, Iteration (basic identifying information), and map<PropertyKey, DeviceVector> Record (associative container mapping property-key entries to corresponding values). Useful functions include contains(), get(), set(), stream operators for Messages::Vector, and save()/load() for file I/O.
A DeviceVector is a template container for contiguous host and device data entries, utilizing revision numbers for synchronization. DeviceVector inherits from row major Eigen::Matrix [8]. Its members include: Buffer (optional contiguous device memory), Revision (major and minor increment to track memory and value updates), and bool sync() (synchronization data between host and device).
Algorithm & Objects
An algorithm is an intrinsic operator (verb) — a reusable global callable function-class defining a procedure to achieve a desired numeric operation. An algorithm is defined by its procedure (as code) and the argument types required for processing. Objects are not directly bound to an algorithm, since algorithms have a singleton-like pattern; they are referenced and invoked as needed within other algorithms, models, and solvers bound to instruction and argument objects.
Requirements: A set of object types expected as bound arguments. Types are specified upfront by the developer within an algorithm constructor.
Inputs/Outputs/Constants: PKs expected as provided data, produced as resultant data, or expected as non-varying constants. Input data is assumed to be pre-allocated by an upstream process. If an output entry does not exist, it is automatically pre-allocated by the solver.
Functions: init(), bind(), prepare(), operator(), clCode() (generates OpenCL kernel source programmatically).
Arguments contain bound objects to be operated upon, often passed into an algorithm's callable function. Arguments can be of arbitrary type, but often contain a pointer to a system, and sometimes references to data and geometry or other bound objects. A standard overridden function signature with a type map is required to permit variadic argument patterns while maintaining a standard function interface.
Instruction, Sequence & Solver
An instruction is an executable instance comprising an algorithm bound to arguments as part of a compute program. It performs the operation of binding objects together in a temporary callable instance that can be invoked with a managed sequence. Inputs and outputs can be functionally queried, returning underlying PK inputs and outputs with relevant AnyProperty and AnyModifier substitutions.
A sequence is an executable container of instructions that assists in object binding and solver preparation. Algorithms are hardcoded and immutable in static bytecode, but with the help of runtime containers, unique high-level behavior can be achieved via custom sequences to comprise physical models and numerical methods (aka. "solvers"). Together, the physical model and solver transform the PDE into a discrete ODE, typically integrating in spatial and temporal dimensions, respectively.
A solver is an executable sequence and heterogeneous compute program manager. The solver base class is useful to assemble custom complex scripts at runtime. Derived variations override the build function to populate its sequences given a numerical method. Most compute programs are heterogeneous, comprising a mixture of host- and device-capable algorithms managed by each system's solver. Device-capable algorithms are processed using specialized mutable C-like code (for a single thread within a SIMD work group), compiled by and targeting an OpenCL ICD runtime driver, enabling compute programs to emulate C++ dynamic-dispatch across available devices.
⚙️ Execution Modes & Configuration
Configuration Files
To set default application runtime parameters (such as favored device, max iterations, convergence criteria, input/output preferences) each xcompute session can load a simple human-editable *.cfg file at launch and/or throughout runtime. Different applications (e.g. server vs client) have one or two configuration files that serve characteristic purposes. In all cases, the application expects at least one config file in the local execution or install path. If one is not provided, warnings are generated and defaults may lead to unexpected or undefined behavior. Configurations can be serialized using Messages::Variables.
Server-side config contains compute resource defaults and service parameters such as port numbers. Client-side config contains interface library locations, stylesheet locations, and user-facing defaults.
Shell Interpreter
A command line interface for XCOMPUTE is under development. While an interactive client facilitates more exploration of a problem, repetitive analysis of a problem is also required, where a simple scripted interface may be preferable to the interactive client for batched studies. The shell interpreter would allow a problem to be expressed on the command line. Geometries would be inputted from external files; boundary conditions can then be applied to surfaces. The described system could then be submitted for execution.
Several external file formats are expected as potential inputs to the command: CSV for data and conditions, VTU for external visualization tools, MSH for gmsh import and export, STL for discrete surface representation, SDF for signed distance field (compressed).
Protobuf-Generated I/O
To share numerical information across computing sessions, Xplicit Computing created the Messages™ file and wire schema, based on Google's Protocol Buffer mechanism, known commonly as Protobuf [10]. Its purpose is to allow engineers and scientists to efficiently and seamlessly share numerical computing data across computing platforms and programming languages. Messages language bindings provide an easy way to utilize existing workflow tools to generate case files for use in the xcompute environment.
Messages provides flexible encoding/decoding similar to XML and JSON but faster and denser. A machine-generated Messages library contains utilities to flatten and reconstruct object-oriented and vectorized data structures encountered in numerical simulation setup, expression, and results (e.g. systems engineering, CFD, FEA, EDA, and geometry processing).
The Messages schema is defined by the .proto files: vector.proto (XCO numeric data object arrays using packed arena allocation), spatial.proto (XCG geometry/topology of elements and regions discretization), concept.proto (XCS system case setup, models, parameters, associations), meta.proto (XCM metadata and user-graphics media for a specific system).
// C++ Binding Example
#include "vector.pb.h"
Messages::Vector64 msg;
msg.set_name("Position|Value");
msg.set_components(3);
msg.add_values(pos.x);
msg.add_values(pos.y);
msg.add_values(pos.z);
# Python Binding Example
import vector_pb2 as vector
msg = vector.Vector64()
msg.name = "Position|Value"
msg.components = 3
msg.values.append(pos.x)
msg.values.append(pos.y)
msg.values.append(pos.z)
🌐 Server-Client Protocol (XCTP)
Transport & State Synchronization
Numerical throughput is typically limited by available processing power, local working memory, or communication bandwidth between devices or hosts, and cache-coherency considerations. Although processing power continues to climb, transport between host and devices remains a primary expense moving towards heterogeneous architectures. In a large-scale distributed environment, this data locality impediment is exacerbated due to limited network bandwidth (as compared to local RAM or PCIe). In order to approach theoretical throughput within the hardware and compatibility across CAE software contexts, an effective systems-of-systems conceptualization (or decomposition) is required for efficient message construction and transport on top of an open schema.
In conjunction with the Messages library, the xcompute protocol provides for efficient sharing of structured numerical data between servers and clients or between servers and servers. Upon changes to a numerical system, a server application pushes a Messages::Meta manifest (defined in meta.proto) to each connected client as an outline of the numerical domain and data available to be requested for each system. Connected clients receive these meta messages into the respective metaobject's buffer, and if it doesn't exist, creates a new metaobject for a corresponding system's global unique id. Within the following client refresh cycle, each relevant metaobject iterates through its members comparing revision numbers against those in the meta-buffer, and as required, updating said members by calling blocking get/pull requests from the server by id.
Client-side user events invoke any number of non-blocking do/command functions on the server to manipulate the simulation state machine, and affected systems push meta messages to post changes. To minimize unintended exposure of work product and intellectual property, the governing numerical system setup is expressly not defined in meta messages, but rather in respective server-side messages defined in Messages Protobuf concept.proto and spatial.proto files. Attributes are requested and fulfilled a la carte from the meta manifest, and numerical data is mostly transmitted over networks for requested attributes as encoded arrays of single-precision floats.
XCTP Message & Packet Structure
To facilitate network communication, the xcompute transport protocol (XCTP) over TCP/IP is introduced, with the service name "xcompute" on IANA-reserved port 11235 [11]. Either server- or client-side, a message is either ingress or egress, and contains the necessary metadata to describe its payload. The message payload may be a Protobuf message or a data primitive (e.g., std::string, int, and IEEE-754 float and double).
XCTP version 1.0 has two layers; first, a header describing each message's payload, and second, a header providing a description of one or more concatenated messages in a packet:
- Message: [Message Header] {payload}
- Packet: [Packet Header] {Message1, ..., MessageN}
| Message Header Field | Description |
|---|---|
type | enumeration describing the payload type, such as a function name, whether is it an actionable payload, or the data-type |
length | byte-length of the message |
container-type | type of container as an enumeration, such as fundamental (eg. built-in type), aggregate (eg. vector, set, protobuf data-type, etc), or null |
container-element | unique container-element index, nullable |
container-size | aggregate element count |
| Packet Header Field | Description |
|---|---|
packet-length | cumulative byte-length of the header and message(s) |
protocol-version | version of the XCTP transport being relayed |
session-id | monotonically increasing identifier describing the connection state |
sequence-nbr | monotonically increasing unique identifier of the packet |
message-count | the count of messages in the packet-payload |
date-time | UTC send time of the packet out the wire |
thd-id | thread-id identifying the issuing thread of this packet (only relevant client-side) |
Combined, these fields make for a self-describing protocol fit for TCP and UDP multicast communication.
🌳 System Hierarchy & Members
A system is an extrinsic object (noun) — a scoped numerical domain with abstract or physical form and behavior, representing components, assemblies, or fields. Only a single type/kind of system exists in a computing sense — for all systems are human constructs; boundaries must be argued for most utility (plus, all variations can't be compiled into the software a-priori!). Generic systems are defined (and specialized) by specific runtime assignments ("state" — such as geometry, physics, boundary conditions, numerical solvers, and anything else that defines an abstract or physical system). A system can also supervene and manage any number of subsystems in a parent-child (uni-directional) relational tree hierarchy; spatial and temporal subspaces each have an assignable host and device resources, allowing a complex problem to be parallelized on SIMD hardware at more reasonable scales.
Data
Data: Scalars and vectors for singular and nodal values stored in a convenient format accessed as system.data(PK). In addition to numeric values, a given system's data defines its name, owner, and permissions; such attributes are intrinsic to data but can be utilized by parent objects. The dimensions of data entries are determined by the number of nodes in the system. If a geometry exists, the number of rows is equal to the number of nodes (or elements), while the number of columns is dictated by the number of components associated with the given PK and spatial dimensionality.
Geometry
Geometry: An optional pointer (address reference) to a local geometry, which is transformed to a global space instance using a model matrix. This permits a geometry to be shared between one or more systems and greatly reduces memory consumption with large numbers of duplicates or patterns. The global model matrix is computed as the recursive product of parent local matrices, providing linear complexity scaling for refresh starting at the root system and performing similar breadth-first recursive refresh to all subsystems. Geometries can be shared across systems and managed by the server application.
Physics
Physics: An optional pointer (address reference) to a physical model, which contributes algorithms to the System's solver in preparation for execution. As a model is defined to be a collection of algorithms to achieve an approximate result, physics is defined as the composition of models, itself a type of model. In the same way geometries can be patterned, physics can be referenced and managed at a higher level, eliminating duplication of setup steps. Most physical models utilize algorithms to define a system's spatial transport and state; similar method families may be combined.
Conditions & Links
Conditions: A sequence of algorithms applied to temporal and spatial boundaries to provide numerical closure. Initial conditions are typically applied to systems as part of a preprocessor, while boundary conditions are applied to regions repeatedly within the main solver sequence. Conditions are bound to instructions and inserted into the sequence; upon building a solver the algorithms are consolidated into the proper execution sequence. Explicit methods often assert conditions on system.data entries, while implicit methods typically manipulate the system.adjacency matrix and/or state.
Links and Contacts: A sequence of algorithms applied to couple abstract and spatial boundaries in lieu of conditions. Links and contacts implement an underlying duplexed coupling algorithm, permitting information to flow between boundaries in adjacent systems. Coupling is essentially a dynamic variation of static conditions, whereby directional flow control and different sampling approaches enable coupling to be customized to the specific configuration. Links are user-defined couplings, while contacts are automatically determined based on spatial proximity and overlap.
Materials
Materials: Optional data that define the intrinsic physical properties of a given substance to be referenced in one or more regions or as defaults for the system. Materials are implemented as the mapping between assigned regions and corresponding data sets managed by global Constants.materials. Typically, material properties are applied to regions that have the same dimensionality of the system; a 2D domain applies material properties to surfaces, while a 3D domain applies material properties to volumes. If materials are not specified, the system reverts to defaults defined in system.physics. Explicit schemes tend to apply material properties to nodes, while implicit schemes tend to apply material properties to elements, though this tends to be dependent on the specific numerical method.
Solvers
Solvers: A list of numerical methods to be executed as part of a workflow. Typically, there is a single system per solver instance; solvers are bound and executed against their owning system. A solver usually defines a self-contained numerical process such as mesh generation, finite volume, or finite element methods using a main iterative sequence plus optional recursive preprocessor and postprocessor stages. A solver contains faculties to compile a master sequence, assemble OpenCL code fragments, and generate a device compute program. One or more solvers can be listed to define a system's workflow.
Subsystems
Subsystems: A container of child systems used to further resolve a numerical domain into constituent parts, constructing a supervening tree hierarchy to change the computational complexity of the problem to abstractions more suitable for human and machine interaction at each level of fidelity and numeric parallelism. A given subsystem has direct access to its children but does not have direct access to its parent; a one-way organization allows systems to be replicated and emplaced under new systems as desired.
🔢 Model, Physics & Solver Assembly
Model & Physics Abstraction
A model is a collection of algorithms defining calculation rules to achieve a desired mechanism. Model typically specifies spatial scheme, while Solver specifies the temporal integration method. Explicit flux-based methods are interoperable as we can accumulate contributions to conserve Degrees-of-Freedom (DOF). Implicit matrix methods cannot be combined as they require a linear assembly stage.
Model Members: Degrees-of-Freedom (Independent variables to be solved), Calculation Rules (Mapping of PKs to set of algorithms with said PK outputs), Compatible Conditions (Region-specific algorithms to provide closure to system space-time boundaries), Compatible Solvers (Numerical methods that complement the integration strategy of the physical model).
Physics is a type of model. It can contain one or more models and superposed sub-models. All algorithms are unbound until a physics model is connected to a system, binding members to arguments to yield an executable instruction sequence. Thus, physical models define desired numerical behavior, but they are independent of the objects yet to be bound for processing.
Consider the application of a general Cauchy (Dirichlet-Neumann) problem in a classical digital computer, where some numerical process (e.g. PDE) is represented by discrete operator T applied to the state defined by K degree-of-freedom Φ = ϕ₁, ϕ₂, ..., ϕ_K quantities to yield some discrete change ΔΦ or state Φ in geometric domain Ω, and boundary conditions Φ₀(δΩ) and any additional arguments... (Δ)Φ(Ω) = T(Φ(Ω), Φ₀(δΩ), ...). The (change of) state in the domain is equal to the transformation that occurs upon the state within the domain given some set of prescribed conditions (or mediating links to other systems) on the boundaries.
Solver Execution & JIT Dispatch
Algorithms are hardcoded and immutable in static bytecode, but with the help of runtime containers, unique high-level behavior can be achieved via custom sequences to comprise physical models and numerical methods (aka. "solvers"). Together, the physical model and solver transform the PDE into a discrete ODE, typically integrating in spatial and temporal dimensions, respectively. Given a sufficient numerical-physical stability (e.g. CFL criterion), information is exchanged throughout the domain in space-time and a solution converges to some acceptable criteria.
In order to change physical behavior during runtime, T must be functionally changed, which is not possible for static binaries created with modern compilers unless virtualization is employed. We essentially must abstract a virtual computing machine inside of software to permit the customization of instruction sequences during runtime. This way, developers and users can create new complex functionalities with building blocks. To facilitate this, when an instruction is constructed, it references an algorithm that defines the behavior in code. The instruction also binds arguments such as a system, data, and geometry by reference prior to execution.
In order for software users to alter the definition of a physical model or solver, T must be updated. To mimic dynamic dispatch on device, a standardized callable function signature is required to create and call optimized functions for given argument types. If the numerical program is written in a static language like C or C++, then the application must be stopped and re-compiled against source code. If the method is expressed in code that can leverage just-in-time (JIT) compilation such as OpenCL, then the application need not be stopped; code is assembled providing an opportunity to include structural and functional runtime optimizations. This process is automated, following virtual function overrides and returning code fragments for each algorithm.
📚 Appendix A: Properties & Modifiers
Partial List of Standard Properties
AnyProperty, Position, Angle, Area, Length, SignedDistance, Volume, Curvature, Element, Feature, Quality, Acceleration, Displacement, Mass, Moment, Momentum, Traction, Velocity, Energy, Pressure, Temperature, Enthalpy, Entropy, CFL, Cp, Cv, Gamma, SpeedOfSound, Time, Stress, HeatTransfer, Poisson, Modulus, SmallStrain, Conductivity, Expansion, YoungsModulus, Distribution, Probability, Diffusion, Viscosity, Vorticity, Voltage, Charge, Permittivity, Permeability, Emissivity, Absorptivity, Aluminum, Beryllium, Boron, Carbon, Electron, Fluorine, Helium, Hydrogen, Lithium, Magnesium, Neon, Neutron, Nitrogen, Nitric, Nitrous, Oxygen, Sodium, Copper...
Partial List of Standard Modifiers
AnyModifier, Coefficient, Count, Density, Net, Size, Approx, Exact, Limit, Residual, Scratch, Equilibrium, Ratio, Reference, Standard, Total, Stagnation, Critical, Static, Dynamic, Molar, Turbulent, Electrical, Thermal, Adjacency, Assembly, Normal, Tangent, Filtered, Gaussian, Magnitude, Max, Mean, Median, Min, Mode, RMS, Standard, Deviation, Sum, Curl, Difference, Divergence, DT, DX, DY, DZ, Flux, Gradient, Integral, Shear, Compressive, Tensile, Torsional, Speed, Anion, Carbide, Cation, Chloride, Diatom, Dioxide, Fluoride, Hydride, Hydroxide, Monoxide, Nitride, Oxide...
🌊 Appendix B: Emergence of the Signed Distance Field (SDF)
SDF Initialization & Extension
Given some computational domain, to solve the SDF we compute and store the radial distance to boundary δΩ at discrete positions x[n] to minimize scalar field φ[n] = min||x(δΩ) - x[n]|| which intrinsically has slope magnitude of 1 = |Δφ| with gradient defined to be outward normal to the implicit boundary [12, 13]. SDF initialization complexity for F faces is approximately proportional to N_shell log(F), so to save on computational costs, the hyperbolic characteristics of the eikonal equation permits the boundary SDF narrowband of N_shell nodes to be solved exactly and extended approximately using fast-marching or equivalent. Extension typically proceeds two orders of magnitude faster than initialization.
The SDF field can be sampled for spatial-physics wall functions, gradients, curvature, and other SDF-derived differential geometry quantities [12]. It can be used to generate unstructured meshes or structured grids, each requiring special procedures to properly interpret elements in regions in close proximity to boundaries; resulting elements near or on boundaries of computable grids and meshes will be erroneously deleted (or marked invalid) preventing the ability to apply conditions. Such subtleties span the gap between academic and working capabilities on the topic.
Although it is possible to solve SDF on meshes, spatial sampling against unstructured geometries incurs complexity that is uncompetitive with constant time sampling on structured grids. The declarative nature of unstructured element topologies requires 20-50 times more memory than a procedural structured grid [1]. Therefore, within the scope of xcompute we assume that sampling occurs against grids to minimize memory footprint and accelerate computations. In practice, these background grids might not be useful to end-users, so the application should show or hide such support geometries as fit.
Grid Construction & Sampling
Creating a structured grid requires a few parameters: nodal resolution, spatial domain extents, and whether or not to expose regions to users (which defaults to false for background grids to save resources). A grid can be constructed with a smooth metric grading, approximating a desired total node count. Conversely for structured grids, it is more useful to explicitly specify the I-J-K dimensions to control anisotropicity.
Given a structured grid, any other geometry can easily and efficiently sample the background to first-order accuracy in constant time by quantizing the position into a I-J-K location code and computing the node-element weightings from the remainder. Since the SDF is typically linearly-varying, the sampling matches accuracy of the underlying field and returns accurate interpolations of SDF values, which is critical near zero where the boundary is defined by default.
For SDF to be useful to existing workflows, a method exists to convert explicit topologies (such as from an STL file) into implicit fields. The numerical complexity to initialize SDF around discrete surfaces restricts naive implementations to small problems [9, 13]. To scale to millions of elements, the collection of simplexes surrounding each reference face must be identified and then trimmed into a narrowband shell. Each node must then search for the minimum distance to the closest face using an octree to approximate far faces. This proceeds quickly in parallel implementations, but robustly determining the SDF sign (for inside-outside) requires an expensive winding number calculation; steradians are accumulated for each narrowband node in a parallel stack queue, dominating the SDF initialization wall time (and thus the interest in reusing SDF shells). Once the SDF value and sign are correctly determined for the narrowband, the SDF is said to have been initialized and can be extended to the majority of nodes in the grid using a fast-marching method.
Operations & Compression
A rich variety of operations are available for implicit SDF shapes, including: sampling, boolean operations, blending, contouring, thinning & thickening. Some of these procedures are required while preparing meshes and grids. The intrinsic properties of SDF and its derivatives allow spatial-physical algorithms to make better informed decisions and projections. Given any position on an SDF field and its gradient, one can estimate the vector to the closest surface in a few clock cycles in constant-time within first-order accuracy.
Per-Olof Persson's work briefly outlines general procedures to utilize SDF in conjunction with other field data to directly optimize shapes against physics [12, 13].
The SDF narrowband and hyperbolic solution can be exploited with index-based compression schemes; datagram size is proportional to the number of unique narrowband values while extension values are discarded, ignored, or assigned a fiducial value. Simple shapes have extreme compression ratios, while the compression size for complex shapes are proportional to the product of topological dimensionality, relative surface area, and resolution. Tolerance handling is also important; information quantizing and loss are not permitted in the SDF datagram floating-point data. Once an SDF datagram has been received, it is uncompressed, evaluated into a structured grid, and expanded into a full SDF field for use.
📖 References
- G. J. Orr. Unified Geometries for Dynamic HPC Modeling, 2018.
- Xplicit Computing. Messages. https://github.com/XplicitComputing/messages, 2022.
- Xplicit Computing. Introduction to Messages. https://github.com/XplicitComputing/messages/blob/master/doc/xcmessages.pdf, 2022.
- Top500. Top #1 Systems. https://top500.org/resources/top-systems, 2022.
- Tech-Clarity. Tech-Clarity Perspective: Reducing Non-Value Added Work in Engineering, 2014.
- Bill Haskins et al. 8.4.2 error cost escalation through the project life cycle. INCOSE International Symposium, 2004.
- Harald Hanche-Olsen. Buckingham's pi-theorem. NTNU, 2004.
- Eigen project. The Matrix class. https://eigen.tuxfamily.org/dox/group__TutorialMatrixClass.html.
- William Dawes et al. A practical demonstration of scalable, parallel mesh generation. 47th AIAA Aerospace Sciences Meeting, 2009.
- Google Inc. Protocol Buffers 3. https://developers.google.com/protocol-buffers.
- IANA. Xcompute service name and port assignment. https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=11235.
- Per-Olof Persson. The level set method. Lecture notes, MIT, 2005.
- Per-Olof Persson. Mesh generation for implicit geometries. PhD thesis, MIT, 2005.