SMTK: Simulation Modeling Tool Kit¶
Version: 23.04.0
Contents:
SMTK User’s Guide¶
The Simulation Modeling Tool Kit, or SMTK, is a framework to help you (1) describe a model in enough detail that it can be passed to a solver and (2) create input files for a variety of solvers in different simulation packages using your description.
This process can involve any or all of the following:
importing a geometric model of the simulation domain or the domain’s boundary;
assigning sizing functions to specify mesh element size for analysis;
submitting portions of the simulation domain to be meshed for analysis;
assigning material properties to regions of the simulation domain;
assigning boundary conditions to portions of the simulation domain’s boundary;
assigning initial conditions to portions of the simulation domain or its boundary; and
assigning global simulation properties such as convergence criteria.
SMTK provides an attribute resource that can represent arbitrarily structured data and a template system that can be tailored to describe almost any simulation’s expected inputs without allowing spurious or invalid input specifications. Thus, in addition to the process above, SMTK provides a way for simulation packages to expose settings to domain experts that makes preparing valid inputs simple.
SMTK also provides a uniform interface to several different solid modeling kernels for preparing discretizations of your simulation domain.
Obtaining, Building, and Installing¶
For instructions on obtaining, building, and installing SMTK, clone the repository:
git clone https://gitlab.kitware.com/cmb/smtk.git
and follow the instructions in the doc/dev/build.md
file in the source code. Other files in that directory
describe our development process and are relevant if you
intend to contribute source code to SMTK.
The rest of this user’s guide assumes you have built
and installed SMTK according to these instructions.
In addition to cloning anonymously as mentioned above, you are also welcome to create an account on either gitlab.kitware.com or github.com and fork the repository. Forking the repository will allow you to submit contributions back to SMTK for inclusion in later releases. See Contributing to SMTK for more on how to prepare and submit changes to SMTK.
An Overview of SMTK’s Subsytems¶
SMTK’s core library contains several subsystems, each of which is covered in depth in sections that follow this one. These subsystems are:
The resource system holds a set of base classes used by the systems below to provide basic provenance information about versions of data on disk. Each file is a resource, which may hold a resource of resource components; the resource manager assembles resources together for processing into a simulation input.
The attribute resource, which provides a way to specify how information should be organized for scientific and engineering workflows, accept that information from users, and ensure that it is consistent with the specification.
The model resource, which provides geometric modeling and allows you to tie information from the attribute resource to geometric entities (e.g., assign boundary conditions in the attribute resource to particular boundaries on a CAD model).
The mesh resource, which can manipulate meshes of geometric models; it provides a way to propagate simulation attribute information from model entities onto meshes. It also provides a way to run external mesh creation tools on the model.
The operation system, which provides an interface to constructing and executing operators, which create, modify and delete resources.
The simulation (also known as the export) system, which is a set of utilities that lets you convert resources (e.g., attribute, model, and mesh resources) into an input deck for a simulation using Python scripts (or C++ if you wish).
The view system provides user interface functionality that is independent of any particular operating system or platform. It serves as a layer between the resource system and the visual layout of resources so that multiple views of the same resources can exist in the same application.
A common system holding utility classes.
Python bindings that enable SMTK to be used by python scripts and SMTK to run python scripts as part of its normal operations.
A set of extension libraries, which provide additional functionality but also introduce dependencies on software beyond what the core smtk library already requires. These are used to create applications built on SMTK.
SMTK Common¶
SMTK uses several generic patterns throughout its code base. These patterns are defined in SMTK’s common directory.
Factory¶
SMTK’s factory pattern (Factory) allows consuming code to register types that share a common base class with a factory instance at runtime and subsequently construct instances of these types using the type (Type), type name (smtk::common::typeName<Type>()), or type index (typeid(Type).hash_code()). Upon declaration, a factory takes as arguments the base class type and a list of input parameter types passed to the generated classes upon construction. As a convenience, each of the above construction modes has an associated Interface to provide a uniform API when constructing objects.
Factories return unique pointers to objects they construct. Because shared pointers can be constructed from unique pointers, the factory pattern can be used in both cases where single and shared ownership are needed.
An example that demonstrates the prinicples and API of this pattern can be found at smtk/common/testing/cxx/UnitTestFactory.cxx.
Instances¶
As an extension to the Factory pattern, SMTK also provides a templated Instances class. The Instances class not only tracks types that can be constructed but also manages instances that have been constructed by holding a shared pointer to each object it creates.
The Instances class can be instructed to release any shared pointer it owns which — assuming no other shared pointers reference the same object — will delete the object.
Upon construction and release of objects its manages, the Instances object invokes Observers (covered later in this section). Observing the construction and imminent deletion of objects allows user interfaces an opportunity to present summaries of the system state.
Instances takes the same set of template parameters as the Factory: the common base type and signatures used to construct instances.
Generator¶
SMTK’s generator pattern (Generator) is similar to a factory, but with several key differences:
1. The generated instances are registered to the static list of generator types at compile time.
2. the API does not expose a means of selecting exactly which registered generator instance is used to satisfy a call. Instead, each generator instance attempts to consume the input in sequence.
An instance of the generator can reject an input at either the valid() check or during the call operator’s execution (where it can throw an exception to trigger the next generator to attempt to consume the input).
An example that demonstrates the prinicples and API of this pattern can be found at smtk/mesh/interpolation/PointCloudGenerator.h.
Links¶
SMTK’s links pattern (Links) describes a set-like container of “link” objects that have a user-defined base class and represent a connection between two different object types (a “left” type and a “right” type). Additionally, each link contains a “role” field to contextualize the link. The pattern uses boost’s multi-index array to facilitate efficient access to a subset of links according to their “left”, “right” and “role” values.
An example that demonstrates the prinicples and API of this pattern can be found at smtk/comon/testing/cxx/UnitTestLinks.cxx.
Observers¶
SMTK’s observers pattern (Observers) is a generic pattern for registering and executing callback routines when an “observed” class performs different tasks related to events in its lifetime or notifying others of its state. The observed class traditionally contains an instance of the Observers class, which is used to both register and call observer functors as these events occur.
Observer parameters¶
When an event that should be observed occurs, the Observers class may be provided with information about the event; this information determines the signature of the observer functors and is a template parameter to the observer class. For example, SMTK’s resource manager considers the addition, modification, and removal of resources to be significant events. It passes an enum specifying which of these events is occurring along with a shared pointer to the resource in question.
Observing state¶
Sometimes, observers may wish to observe the state of a class rather than lifecycle events; for example, SMTK’s resource manager notifies observers when resources are added to or removed from the manager. However, observers may need to maintain a list of all the resources in the manager — even those added before the observer was registered. Thus when an observer functor is registered with Observers, there is sometimes a need for the functor to be called immediately with the initial state as well as later when future events alter the state.
If this is the case, the Observers instance can be provided with a function at construction that “initializes” observers upon registration with the current state. If an initializer is provided, observers can opt in to being initialized as they are registered; if no initializer is provided, then any request to be initialized is ignored.
Registering an observer¶
An observer functor is registered to an Observers instance with
an optional integral priority value (higher priority functors are called before lower priority functors),
an option to initialize the observer by executing the functor immediately upon registration, and
a string description that is printed in debug mode when the observer functor is called.
When an observer functor is registered, a non-copyable key is returned that scopes the lifetime of the observer functor (when the key goes out of scope, the observer functor is unregistered from the Observers instance).
Early exit¶
If the Observers instance is constructed to have observers return a boolean value, observation is terminated if an observer functor returns false. This allows observers to potentially cancel an action that is being observed, letting the observers drive the actions of the observed class. If the Observers instance is constructed to have observers return void, all observer functors will be called without the possibility of cancellation.
Mutating observations and thread safety¶
Note that observers are normally invoked synchronously whenever the parameters of the observed event are provided to the Observers’ parenthesis operator. If used in a threaded environment, this means that multiple threads might potentially invoke observers — potentially simultaneously. If this is not your intention (many user interface libraries do not allow updates except on one thread), you must modify how observers are called.
The Observers pattern also contains logic to optionally modify the execution logic of the observer instance. This feature is used to control the context in which observer functors are executed (e.g., forcing all observer functors to execute on the applicaiton’s main thread, as is done by pqSMTKCallObserversOnMainThreadBehavior).
Example¶
An example that demonstrates the prinicples and API of this pattern can be found in SMTK’s UnitTestObservers test.
Thread Pool¶
SMTK’s thread pool pattern (ThreadPool) constructs a thread pool for asynchronously executing functors that return a common type. The number of threads in the pool can be assigned at construction, with the default number of threads selected by the hardware’s concurrency value. When a functor is passed to the thread pool, a std::future containing the return type is immediately returned. Multiple instances of the thread pool can exist simultaneously; each instance will allocate a user-defined number of threads that will stay idle until the thread pool is given a task to perform.
An example that demonstrates the prinicples and API of this pattern can be found at smtk/comon/testing/cxx/UnitTestThreadPool.cxx.
Type Container¶
SMTK’s type container (TypeContainer) is a generic container for storing single instances of types. The contained types do not have any requirements for sharing a common base class; as a result, the container does not require any parameters upon construction. Because of this, there is no run-time method for discovering the contents of a container, only for probing whether a given type is contained. As a tradeoff, element access from the container is templated on the type to be returned (the element type is used as a key to retrieve the element and subsequently perform the appropriate conversion). If an element is default-constructible, an attempt to access an element that is not currently in the container will result in the construction of an instance of that object.
An example that demonstrates the prinicples and API of this pattern can be found at smtk/comon/testing/cxx/UnitTestTypeContainer.cxx.
Type Map¶
SMTK’s type map (TypeMap) is a generalized map for storing and accessing data using both a type and a user-defined key type. Unlike TypeContainer, a type map has an additional key index; a TypeMap can therefore contain multiple instances of a single type, each accessed by a unique key. This additional level of misdirection gives the type map more flexibility than a type container, at the expsense of an additional lookup phase. In addition, serialization routines are provided for the type map that facilitate its transcription to and from json.
An example that demonstrates the prinicples and API of this pattern can be found at smtk/comon/testing/cxx/UnitTestTypeMap.cxx.
Update factory¶
SMTK resources – particularly the attribute resource – can store data that presumes or specifies a designer-provided schema in addition to user-provided content. As a project matures, developers and workflow designers must make changes to the schema but wish to provide users with a path to migrate their content to the new schema rather than abandoning it.
The update::Factory provides developers with a way to register functions that can accept resources in an old schema and copy it into a resource with a new schema, adapting the user’s content as needed.
SMTK’s Resource System¶
Unlike the major subsystems that follow, the resource system does not store information directly relevant to simulations itself; rather, it provides base classes holding metadata for each of the other systems so that the provenance of all the inputs to a simulation can be tracked.
Key Concepts¶
There are two base classes holding provenance metadata:
- Resource
instances correspond to files and thus have a URL. Each resource also has a UUID and a type. Resources may also provide access to a resource of components (see below) present in the file, indexed by UUID.
- Component
instances are meaningful subsets of data present in a resource. An attribute resource presents its attributes as resource components. Similarly, geometric model entities (vertices, edges, faces, etc.) are components in the model subsystem. Each component has a UUID and holds a weak reference to the resource which owns it.
In addition to these useful base classes,
- Manager
instances hold a resource of resources. How the resources are related to each other is determined by your application, but a common use is holding all of the resources related to a simulation.
- Links
Resource links connect persistent objects via a unidirectional relationship that exists for a specific purpose, or role. Links are stored separately from the objects they connect, but each resource instance owns the link that holds all outgoing connections from the resource and its components. The smtk::resource::Link class is an abstract API provided on both resources and components; on the resource, the link subclass provides actual storage, while on components, the subclass asks its parent resource for the links object in order to search for connections. Roles are simply integers. Roles less than zero are reserved for internal SMTK use while positive roles are available for user-specified (i.e., application-specific) roles.
- Properties
Resources and components contain a dictionary-like data structure that can hold any copyable type. If the property type has JSON bindings, it will be serialized to file along with the resource; otherwise, the property can still be stored, but will not be persistent. Currently enabled types include long, double, std::string, and std::vectors of these three types. As an example of how to extend the property system to handle non-POD (non-Plain Old Data) types, see the CoordinateFrame property and its JSON serializer.
Properties are stored separately from the objects they annotate; each resource instance owns the properties instance that holds all properties for both itself and its components. The smtk::resource::Properties class is an abstract API provided on both resources and components; on the resource, the properties subclass provides actual storage, while on components, the subclass asks its parent resource for the properties object to search for values.
- Queries
Resources hold a container of relevant Query objects. Queries prevent the resource or component classes from growing large APIs and internal state by splitting methods that perform queries — such as identifying the spatial bounds of a mesh or model object, finding the closest point on a mesh or model component to some location in space, etc. — into separate classes that are easy to create and invoke.
Furthermore, query objects can inherit their API from other queries, so it is possible to provide a uniform API with different implementations for each resource type.
Queries may also need to store state in order to be performed efficiently. A good example is closest-point searches; usually many queries of this type are performed in a batch and a point locator structure is built to accelerate the query. The cache should outlive the query object, which is usually constructed on the fly by an algorithm, but also be marked dirty when the resource’s components are modified. The Queries object owned by each resource provides a container for cache objects that individual Query objects may use. Multiple query classes can share the same cache object (e.g., ClosestPoint and ClosestCell might both use a PointLocator cache object).
Filtering and Searching¶
It is possible to ask the resource to filter its components using a string that specifies some search criteria (i.e., a filter). The default implementation provided by smtk::resource::Resource::queryOperation allows users to filter components based on the existence and values of properties. For example, a geometric model of a motor will have many model faces that might each be marked with properties to indicate which are bearing surfaces, which are fastener or alignment surfaces, which surfaces will be in contact with coolant or fuel, etc.
In order to allow user interface components to only show relevant model entities, the resource’s queryOperation method accepts strings in the following format:
[
property-type [{
property-name [=
property-value ]}
]
where
property-type
is one of the following string literalsstring
,floating-point
,integer
.property-name
is either a single-quoted name or a slash-quoted regular expression (i.e., a regular expression surrounded by forward slashes such as/(foo|bar)/)
.property-value
is one of the followinga single, single-quoted string value to match (when searching for string properties),
a single, slash-quoted regular expression to match (when searching for string properties by regular expression),
a single, unquoted integer or floating point value to match (when searching for properties of those types), or
a tuple (indicated with parentheses) of values, as specified above, to match. Note that this implies the property must be vector-valued and the length must match the specified tuple’s length in order for a match to be successful.
Whitespace is allowed anywhere but is treated as significant if it is inside any quoted string value or regular expression.
Note that single quotes are used because these filter strings will appear in XML and/or JSON serializations that use double-quotes to mark the start and end of the query string. The examples below include the double-quotes around the query as a reminder.
For regular expressions, the c++11 standard library is used to search for matches; the syntax must be accepted by the std::regex constructor and std::regex_search() must return true when passed property names or values in order for the corresponding entity to be included in filtered results.
Query string |
Results |
---|---|
“ |
Components with any string properties at all (but not components without string properties). |
“ |
Any component with an integer property named ‘counter’ (regardless of the value). |
“ |
Components with a string-property named pedigree whose value is “zz” |
“ |
Components with any vector<floating-point> property whose value is a 3-entry vector of zeros. |
“ |
Components with a vector<string> property named “alphabet” whose value is a vector of 2 strings: one valued “abc” and the next valued “def”. |
Query string |
Why This is Invalid |
---|---|
“ |
You must currently specify the property type. |
“ |
There is no way to search for properties with partially-matched array-valued entries. |
“ |
There is no way to search for properties whose value is a given length yet. |
SMTK’s Geometry System¶
The geometry system provides an extended Resource class that responds to queries for geometric data. It also provides a set of abstract classes that additional libraries can use to provide access to geometric data in multiple formats.
Key Concepts¶
A major role of model and mesh resources is visualization and interactive manipulation of a simulation’s domain. Additionally, some SMTK sessions may wish to perform analytical modeling tasks that require access to geometric data, such as approximating integrals over subsets of the simulation domain. These tasks require a geometric description of resources and components. While SMTK’s core library does not provide direct support for geometry — since that would introduce unwanted dependencies for many use cases — it does include support (if plugins providing geometry are loaded) for querying what components and resources have geometric data and for querying the bounds of any related geometry.
Furthermore, it is possible for a resource to provide support for multiple backends that represent geometry differently (e.g., VTK, VTK-m, vtk-js, Unity, Vulkan, PDF). It is even possible that multiple backends might be used simultaneously; for example, an application might provide interaction using VTK but use a different backend like vtk-js to publish content to a cloud service.
Another constraint on geometry providers is the need for caching of geometry in order to efficiently respond to repeated queries for bounding boxes, picking, etc. Many times, these operations require expensive tessellation operations and the result should be cached for re-use. Thus the interface that provides geometry should allow for a significant amount of state to be held; expecting each query for a bounding box to construct a new instance of some geometry provider would complicate efficient caching for repeated queries.
Therefore, SMTK provides:
- smtk::geometry::Backend
which is a mostly-empty class to be inherited by actual rendering backends. It is used as a key so that each resource can own multiple geometry provider objects as needed, while providing type-safe access to specific subclasses needed by different renderers. Currently, only smtk::extension::vtk::source::Backend inherits it.
- smtk::geometry::Geometry
which is a base class for objects that can respond to geometric queries. This class does not provide methods that return any actual geometric data; subclasses must implement methods for querying the existence of renderable geometry and its bounds for all the persistent objects owned by the parent resource of the geometry object. Thus, metadata such as existence and bounds are available to libraries linked only to SMTK’s core library.
- smtk::geometry::GeometryForBackend
which all concrete implementations should inherit since it can respond to geometric queries in a specific format (its template parameter). Backends will use the pure virtual data() method it declares to return geometry in a format specific to the backend.
- smtk::geometry::Cache
which is a convenience class for writing Geometry subclasses that will maintain a cache of geometric data for each object. Concrete implementations of of GeometryForBackend<Format> may choose to inherit this class, which implements several virtual methods declared in Geometry and defines new methods you must implement in order to populate the cache.
- smtk::geometry::Generator
is a subclass of smtk::common::Generator used to register and create instances of Geometry subclasses on demand. Note that generators return unique pointers (i.e., std::unique_ptr<Geometry>) so that large caches are not duplicated or multiply-owned. This class is used internally by smtk::geometry::Resource
This figure shows how rendering code is split across 3 libraries (left: the core, which defines a basic interface with no external dependencies), a rendering extension (right: a plugin used by the application to allow geometry interaction), and a session (center: a plugin loaded by the application to provide a new type of model, mesh, or attribute resource that has geometric components).¶
Writing a geometry object for a resource¶
When you create a new resource type with renderable geometry, you must (1) create new geometry class(es) that subclass GeometryForBackend<Format> (one for each Backend you wish to support), and (2) register the geometry class(es) so they will be created when needed. We’ll cover the latter task (registration) now and come back to subclassing a geometry provider afterward.
Each geometric Resource contains a map from geometry backend IDs to Geometry instances. When asked for geometry that it does not already have an instance for, the resource uses a Generator to query the application for one. There is no guarantee that geometry will exist; this may be because a particular backend is not supported or because the application has not been linked with a library that supports the backend.
For example, consider a lightweight, 2-D model resource that is used extensively by python scripts to do batch processing. You would probably not want the python module that registers this model resource to link to VTK since that could increase script runtimes adversely and the scripts do not need to provide renderings of your model. However, you probably want the modelbuilder application to be able to render components of your model. For this reason, you should put the VTK-based geometry in a separate library from the resource’s core implementation. The library with VTK dependencies can then expose a geometry object by implementing
class RegisterResourceFooBackendBar
: public smtk::geometry::Supplier<
RegisterResourceFooBackendBar>
{
public:
bool valid(const geometry::Specification& in) const override
{
// Only create providers for the "bar" backend:
smtk::bar::Backend backend;
return std::get<1>(in).index() == backend.index();
}
smtk::geometry::GeometryPtr operator()
(const geometry::Specification& in) override
{
// Only create providers for "foo" resources:
auto rsrc = std::dynamic_pointer_cast<
smtk::foo::Resource>(std::get<0>(in));
if (rsrc)
{
auto geom = new foo::BarGeometry(rsrc);
return smtk::geometry::GeometryPtr(geom);
}
return nullptr;
}
};
Whenever an application needs to render a resource, it asks the resource for a geometry object compatible with a given backend (such as VTK). If none already exists, a geometry::Generator is used to iterate over registered geometry::Supplier instances like the one above until a match is found or the registered list is exhausted.
Consuming geometry from a resource¶
Subclasses of smtk::geometry::Backend, should provide a method on the backend which accepts a geometry provider and a persistent object and returns its corresponding geometry. Internally, this method can cast the geometry provider it is passed into one or more smtk::geometry::GeometryForBackend<T> subclasses. When a match is identified, that provider’s geometry() method can be called to fetch geometry and convert it into something the rendering backend can use.
In this way, it is possible for one backend to use providers originally written for different backends. As an example, a VTK-m backend might include an adaptor for dealing with providers that can supply VTK data objects.
Geometric queries¶
SMTK’s geometry system provides abstract Query objects that define consistent APIs for several common geometric queries:
- Bounding box
which returns axis-aligned bounds for objects with geometry.
- Closest point
which identifies, for a provided (x,y,z) location, the closest modeled point on an object with geometry. This will always “snap” to the nearest point on the component’s discretization instead of choosing the absolutely closest point.
- Distance to
which computes the distance from a provided (x,y,z) location to the closest point on an object with geometry. Unlike the closest-point query, this query will return points that are not points in the discretization of the objects.
- Random point
which computes random point on a geometric resource component.
- Selection footprint
which is used by the ParaView user-interface components of SMTK to determine what entities should be highlighted when a selected entity does not have any renderable geometry itself (but has children that do have geometry).
SMTK’s Attribute Resource¶
General Description¶

SMTK’s first major component is its attribute modeling resource, which provides a way to model and represent non-geometric simulation data such as material properties, boundary conditions, and solver parameters.
Key Concepts¶
The attribute resource is composed of C++ classes, also accessible in Python, whose instances perform the following functions:
- Attribute
instances represent a dictionary of named values. The values are all subclasses of the Item class. The entries that may appear in an attribute’s dictionary are constrained by the attribute’s Definition. In addition to holding a set of Items, an attribute may optionally be attached to (or associated with in SMTK’s parlance) a set of geometric model entities from SMTK’s geometric modeling system.
- Definition
instances hold the set of possible key-value pairs that must be present in Attribute instances that reference them. A definition may inherit another definition as a base type. For instance, deflection, temperature, and voltage boundary condition definitions might all inherit a Dirichlet boundary condition definition. Even when the base class provides no requirements, this is useful for fetching attributes that meet a specific condition.
- Item
instances hold values in an attribute key-value pair. The particular subclass of Item determines the type of storage used to hold the value (e.g. Int, Double, String, RefItem, ModelEntityItem). Each item references an ItemDefinition that constrains the values that may be held in storage, in much the same way that an Attribute has a Definition. Some items (those derived from ValueItem) can have other items as children; this is used to implement conditional items, where the presence of children is predicated on the value taken on by their parent item.
- ItemDefinition
instances constrain the number of values that an Item instance may contain as well as the particular values that are considered valid. For example, an ItemDefinition for temperature could specify that temperature is a scalar (i.e., only a single value may be held in the Item), that it is a floating point value, and that it must be positive.
- Resource
instances hold resources of attributes associated with a particular purpose such as
defining a simulation’s input deck (see the simulation workflows repository for examples);
specifying locations where input and output files are located during the export process (SMTK’s simulation subsystem creates an attribute resource for this purpose); and
defining operations that can be performed on a geometric model (SMTK’s geometric modeling system uses an attribute resource to hold definitions for each modeling operation that can be performed by each of its modeling kernels).
At the top left is a UML diagram of the C++ classes in SMTK’s attribute system. At the bottom left is an example composed of instances of the C++ classes above, with like colors indicating the class of the instantiated objects. The right side of the figure shows how these components are presented in the modelbuilder application and associated to a geometric model entity.¶
Because it can be tedious to programmatically create a bunch of instances of the classes above to represent a particular simulation’s input deck, SMTK provides an XML file format for serializing and deserializing all of the attributes, definitions, items, and item-definitions stored in an attribute resource.
Interfaces to the attribute resource¶
The attribute resource has three interfaces:
1. An XML file syntax for specifying the kinds data to be modeled for individual simulation codes and problem domains. This file syntax is used to specify the unique inputs for a simulation code, including, for example, data types, valid ranges, and default values. Specific data can be associated with geometric model entities, and structural features can be used to group and classify simulation data.
2. A set of user-interface panels for end users to create and edit simulation data. Using standard windows components (buttons, text boxes, selection lists, etc.), users can enter all of the detailed data needed to specify a desired simulation. These user-interface panels are automatically produced by SMTK at runtime, based on the XML file(s) provided to the system.
3. An API for accessing the simulation data produced by end users. Once an end-user has completed specifying the simulation, application software can be used to traverse that data and generate the simulation input files. The native SMTK API is C++, and python bindings are also included. In practice, python scripts are typically used to access the simulation data and generate the simulation input files.
Example Workflow¶
SMTK can be used in a broad range of scientific and engineering simulation applications. In physics-based applications, such as structural mechanics, computational fluid dynamics (CFD), and electromagnetic (EM) modeling, simulations are often performed relative to a geometric model. This model may be created using computer-aided design (CAD), or computationally constructed/reconstructed from empirical data. In either case, a discretization process is typically performed with the model to generate geometric input suitable for the simulation code, often in the form of a finite element or finite volume mesh. To complete the simulation inputs, the non-geometric inputs are generated to enumerate the specific boundary conditions, material properties, solver parameters, etc.
With SMTK, the process of generating simulation input data can be automated, providing the dual benefits of work-product reuse and error reduction. Once the simulation-specific data are defined in an XML template, domain experts or other end users can create the simulation data, or attribute data, for specific problems using the SMTK user interface panels. Then simulation-specific python scripts can be used to traverse the attribute data and write the simulation input files.

The XML below is used to generate this user interface. The fields with yellow backgrounds show default values and white backgrounds indicate modified values.¶
<Definitions>
<AttDef Type="Example1" Label="Example 1" BaseType="" Version="0"
Unique="true" Associations="">
<ItemDefinitions>
<String Name="ExampleString" Label="String item:" Version="0"
NumberOfRequiredValues="1">
<BriefDescription>Enter some string of import</BriefDescription>
<DefaultValue>Yellow denotes default value</DefaultValue>
</String>
<Int Name="ExampleInteger" Label="Integer item:" Version="0"
NumberOfRequiredValues="1">
<BriefDescription>For some integer value</BriefDescription>
<DefaultValue>42</DefaultValue>
</Int>
<Double Name="ExampleDouble" Label="Double item:" Version="0"
NumberOfRequiredValues="1">
<BriefDescription>For floating-point precision values</BriefDescription>
<DefaultValue>3.14159</DefaultValue>
</Double>
<Double Name="ExampleVector" Label="Double item w/3 values:" Version="0"
NumberOfRequiredValues="3">
<BriefDescription>Number of components is set to 3</BriefDescription>
<ComponentLabels>
<Label>x</Label>
<Label>y</Label>
<Label>z</Label>
</ComponentLabels>
<DefaultValue>0</DefaultValue>
</Double>
<String Name="SecondString" Label="Another string item:" Version="0"
NumberOfRequiredValues="1">
<BriefDescription>Enter some string of import</BriefDescription>
<DefaultValue>whatever</DefaultValue>
</String>
</ItemDefinitions>
</AttDef>
<!-- Remaining content not shown -->
Conditional items¶
One particular workflow SMTK supports is attributes that can be defined by multiple, distinct sets of values. For example, there are many ways to define a circle, four of which are: using 3 points on the circle, using a center and radius, using 2 points (a center plus a point on the circle), and by inscribing a circle inside a triangle. There are times when you will want to represent an attribute that can accept any of these definitions instead of constraining the user to work with a single construction technique.
SMTK accommodates this by having you create an auxiliary item whose value enumerates the different possible definitions. This auxiliary item owns all of the items that are active depending on the auxiliary item’s value. For our example of a circle, the attribute definition would be
<Definitions>
<AttDef Type="circle" Label="Circle" BaseType="" Version="0">
<ItemDefinitions>
<!-- Here is our auxiliary item -->
<Int Name="construction method">
<!-- First we list *all* of the child items -->
<ChildrenDefinitions>
<Double Name="radius" NumberOfRequiredValues="1">
<Min Inclusive="false">0</Min>
<DefaultValue>0.5</DefaultValue>
</Double>
<Double Name="center" NumberOfRequiredValues="3">
<DefaultValue>0.0</DefaultValue>
</Double>
<Double Name="point 1" NumberOfRequiredValues="3"/>
<Double Name="point 2" NumberOfRequiredValues="3"/>
<Double Name="point 3" NumberOfRequiredValues="3"/>
</ChildrenDefinitions>
<!-- Now we enumerate the acceptable auxiliary values
along with the children used in each case.
-->
<DiscreteInfo DefaultIndex="1">
<Structure>
<Value Enum="3 points">0</Value>
<Items>
<Item>point 1</Item>
<Item>point 2</Item>
<Item>point 3</Item>
</Items>
</Structure>
<Structure>
<Value Enum="2 points">1</Value>
<Items>
<Item>center</Item>
<Item>point 1</Item>
</Items>
</Structure>
<Structure>
<Value Enum="center and radius">2</Value>
<Items>
<Item>center</Item>
<Item>radius</Item>
</Items>
</Structure>
<Structure>
<Value Enum="inscribe in triangle">3</Value>
<Items>
<Item>point 1</Item>
<Item>point 2</Item>
<Item>point 3</Item>
</Items>
</Structure>
</DiscreteInfo>
</Int>
</ItemDefinitions>
</AttDef>
</Definitions>
You can see that each “Structure” section describes a particular way to define the circle. Different subsets of the items are active depending on whether the auxiliary “construction method” value is 0, 1, 2, or 3.

The user interface generated for a conditional item definition.¶
When SMTK generates a user interface for the attribute above, the “construction method” value is represented as a tabbed widget with 1 tab for each of the “Structure” sections above. The default tab will be “2 points”.
Migration to newer schema¶
Over time, the schema used by an attribute resource may need to be
modified to fix issues or add features.
SMTK provides some support to aid you in migrating existing attribute
resources from an old schema to a new one while retaining as much
user-provided information as possible.
This is intended to be accomplished by copying attribute instances and
other information from the existing resource into a new resource whose
definitions are from a newer schema.
See the itemUpdater
test for an example how to register
functions or python scripts to help with this process.
Template File Syntax (Reference) for Version 6 XML Attribute Files¶
File Layout¶
All attribute template and instance files must contain the <SMTK_AttributeResource> XML element. The following table shows the XML Attributes that can be included in this XML Element.
XML Attribute |
Description |
---|---|
Version |
Integer value that indicates the SMTK attribute format (Required) Current value is 6 (latest version) |
DisplayHint |
Boolean value that indicates if the Attribute Resource should be automatically displayed in an Attribute Editor when loaded into memory. (Optional – default is false which means the Resource will not automatically be displayed) |
TemplateType |
A string providing a name for the workflow this attribute describes. (Optional – the default is an empty string.) |
TemplateVersion |
An integer describing the version number of the schema for this attribute resource. The default value is 0. |
This element can contain the following optional children XML Elements:
Includes : used for including additional attribute files (see Includes Section)
AdvanceLevels : used to define various advance access levels used in GUIs (see Advance Level Section)
Categories : used to define workflow specific categories (see Category Section)
Analyses : used to define various analysis groups (see Analysis Section)
Analysis Configurations: predefined analysis configuration from which the user can choose from (see Analysis Configuration Section)
Item Blocks : used to define reusable blocks of Item Definitions(see Item Blocks Section)
Definitions : used to define attribute definitions (see Definitions Section)
Attributes : used to define attributes
Views : used to define various Views (used to create GUIs)
Includes Section¶
The attribute format supports the ability to include other attribute files. This allows designers to assemble a complete attribute description by referencing attribute files that represent specific aspects. For example a set of attribute definitions may be referenced by several different simulation workflows. Below is an example of including two attribute files both located in a sub-directory IncludeTest.
<Includes>
<File>includeTest/b.xml</File>
<File>includeTest/a.xml</File>
</Includes>
Each include file is contained within a File XML Element.
Advance Level Section¶
This is an optional section used to describe the various access levels used in GUIs created using SMTK’s QT Attribute classes. For example a workflow could consist of the following advance levels:
General
Intermediate
Expert
Using the <AdvanceLevels> element the following represents the above access levels.
<AdvanceLevels>
<Level Label="General" Color="0.0, 1.0, 0.0"> 0 </Level>
<Level Label="Intermediate" Color="1.0, 1.0, 0.0"> 1 </Level>
<Level Label="Expert" Color="1.0, 1.0, 0.0"> 2 </Level>
</AdvanceLevels>
The value of each Level XML Element determines the ordering of the access levels and are used by the items contained within Definitions (see xxx). Notice that in the GUI example, the upper left corner “Show Level” entry is based on the Advance Levels.
Each advance level is represented by using a <Level> XML Element. The value of the element determines the absolute access level and should not be repeated among sibling Level Elements. The higher the value the more “advance” the level.
The following table shows the XML Attributes that can be included in this XML Element.
XML Attribute |
Description |
---|---|
Label |
String value representing the access level name to be displayed in the GUI (Optional - else the label will be the word Level followed by the advance level) |
Color |
String value representing the color to be used when displaying items that are associated with this access level. The format is “r, g, b” where r, g, and b are a value between 0 and 1 inclusive (Optional) |
Category Section¶
This is an optional section describing the set of categories used within the file. Attribute and Items Definitions can use these categories to define their category constraints. In addition, analyses are defined as sets of categories. For example, the following xml would define the following categories:
Constituent Transport
Heat Transfer
Flow
Time
General
<Categories>
<Cat>Constituent Transport</Cat>
<Cat>Heat Transfer</Cat>
<Cat>Flow</Cat>
<Cat>Time</Cat>
<Cat>General</Cat>
</Categories>
Each category is contained within a Cat XML Element.
The following table shows the XML Attributes that can be included in <Categories> Element.
XML Attribute |
Description |
---|---|
Default |
String value representing the default categories a Definition’s Item belongs to when no category is specified. |
Property Section for the Attribute Resource¶
This is an optional section describing a set of properties that should be added to the Attribute Resource. The Property section is defined by a XML Properties node which is composed of a set of children Property nodes as shown below:
<Properties>
<Property Name="pi" Type="Int"> 42 </Property>
<Property Name="pd" Type="double"> 3.141 </Property>
<Property Name="ps" Type="STRING">Test string</Property>
<Property Name="pb" Type="bool"> YES </Property>
</Properties>
You can also look at data/attribute/attribute_collection/propertiesExample.rst and smtk/attribute/testing/cxx/unitXmlReaderProperties.cxx for a sample XML file and test.
The following table shows the XML Attributes that can be included in <Property> Element.
XML Attribute |
Description |
---|---|
Name |
String value representing the name of the property to be set. |
Type |
String value representing the type of the property to be set. Note that the value is case insensitive. |
The values that the Type attribute can be set to are:
int for an integer property
double for a double property
string for a string property
bool for a boolean property
The node’s value is the value of the property being set.
The following are supported values for true:
t
true
yes
1
The following are supported values for false:
f
false
no
0
Note that boolean values are case insensitive and any surrounding white-space will be ignored.
If you include an Attribute XML file that also assigns Resource Properties, the include file’s Properties are assigned first. Meaning that the file using the include file can override the Properties set by the include file.
Note - the ability to unset a Property is currently not supported.
Note - Properties are currently not saved in the XML format, but are saved if using the JSON format.
Analysis Section¶
This is an optional section that define analyses. An analysis is defined as a resource of categories. For example, using the categories defined in the`Category Section`_, the following XML would define two analyses (Ground Water Flow, and Ground Water with Heat Transfer).
<Analyses>
<Analysis Type="Groundwater Flow">
<Cat>Flow</Cat>
<Cat>General</Cat>
<Cat>Time</Cat>
</Analysis>
<Analysis Type="Groundwater Flow with Heat Transfer">
<Cat>Flow</Cat>
<Cat>General</Cat>
<Cat>Heat Transfer</Cat>
<Cat>Time</Cat>
</Analysis>
</Analyses>
An analysis can be composed of sub-analyses from which the user can choose. By default, the user can select a subset of these sub-analyses as shown below:
Here you see analysis B is composed of 2 optional sub-analyses B-D and B-E. In this case B-E has been selected. An active sub-analysis will add its categories to those of its parent’s analysis. The above example could be rewritten using the concept of sub-analyses:
<Analyses>
<Analysis Type="Groundwater Flow">
<Cat>Flow</Cat>
<Cat>General</Cat>
<Cat>Time</Cat>
</Analysis>
<Analysis Type="Groundwater Flow with Heat Transfer" BaseType="Groundwater Flow">
<Cat>Heat Transfer</Cat>
</Analysis>
</Analyses>
You can also indicate if the user must select only one sub-analysis by indicating that the base analysis is Exclusive. In the example below, analysis C has been marked Exclusive and consists of two sub-analyses C-D and C-E. The user must select one of these analyses.
If you wish to have the user exclusively choose among the top level analyses, you can add the Exclusive attribute to the Analyses XML node itself as shown below.
<Analyses Exclusive="true">
<Analysis Type="A">
<Cat>A</Cat>
</Analysis>
<Analysis Type="B">
<Cat>B</Cat>
</Analysis>
</Analyses>
Please see smtk/data/attribute/attribute_collection/SimpleAnalysisTest.sbt smtk/data/attribute/attribute_collection/analysisConfigurationExample.sbt for complete examples which can be loaded into ModelBuilder.
Each Analysis is defined within an <Analysis> XML Tag.
The following table shows the XML Attributes that can be included in this XML Element.
XML Attribute |
Description |
---|---|
Type |
String value representing the type analysis being defined. Note that the type should be unique with respects to all other analyses being defined. (Required) |
BaseType |
String value representing the type of the analysis this analysis is a sub-analysis of. (Optional) |
Exclusive |
Boolean value indicating the sub-analyses of this analysis are exclusive. (Optional) |
Required |
Boolean value indicating the analysis is required and not optional. (Optional) Note - Required Analyses can not be sub-analyses of an Analyses with Exclusive set to true. |
Each Analysis element contains a set of Cat XML Elements.
Analysis Configuration Section¶
This is an optional section that define analyses configurations. The Analysis Section describes relationship between analyses, while the configuration section defines various analysis configurations from which the user can choose from. Here is an example configuration section from smtk/data/attribute/attribute_collection/analysisConfigurationExample.sbt:
<Configurations AnalysisAttributeType="Analysis">
<Config Name="Test A" AdvanceReadLevel="5">
<Analysis Type="A"/>
</Config>
<Config Name="Test B" AdvanceWriteLevel="10">
<Analysis Type="B"/>
</Config>
<Config Name="Test B-D">
<Analysis Type="B">
<Analysis Type="B-D"/>
</Analysis>
</Config>
<Config Name="Test C">
<Analysis Type="C"/>
</Config>
<Config Name="Test C-D">
<Analysis Type="C">
<Analysis Type="C-D"/>
</Analysis>
</Config>
<Config Name="Test C-E">
<Analysis Type="C">
<Analysis Type="C-E"/>
</Analysis>
</Config>
<Config Name="Test C-E-F">
<Analysis Type="C">
<Analysis Type="C-E">
<Analysis Type="C-E-F"/>
</Analysis>
</Analysis>
</Config>
</Configurations>
XML Attribute |
Description |
---|---|
AnalysisAttributeType |
String value representing the type name for the Attribute Definition used to represent an Analysis Configuration. (Required) |
The Configuration Element is composed of a set of Config Elements. Each Config Element represents an Analysis Configuration.
XML Attribute |
Description |
---|---|
Name |
String value representing the configuration’s name (which will also be the name of an Analysis Attribute that represents that configuration). (Required) |
AdvanceReadLevel |
Integer value representing the configuration’s read access level. If the advance level is set below the configuration’s AdvanceReadLevel, the configuration will not be displayed to the user. (Optional - default is 0) |
AdvanceWriteLevel |
Integer value representing the configuration’s write access level. If the advance level is set below the configuration’s AdvanceWriteLevel, the user will not be allowed to edit the configuration. (Optional - default is 0) |
Each Config Element consists of a nested group of Analysis Elements. Each Analysis Element represents an analysis defined in the Analysis Section. Specifying an Analysis indicates that the analysis is active when the configuration is selected. If the analysis is Exclusive then it must contain an Analysis Element referring to one of its sub-analyses. If it is not Exclusive then it can optionally contain multiple Analysis Elements indicating which sub-analyses should be active.
XML Attribute |
Description |
---|---|
Type |
String value representing an analysis type. |
Unique Roles Section¶
There are use cases where the developer would like to enforce a constraint among ComponentItems such that each item cannot point to the same resource component. In order to provide this functionality, we have introduced the concept of unique roles. Roles in this context refers to the roles defined in the resource links architecture and that are referenced in ReferenceItemDefinition. If the role specified in either a Reference or Component Item Definition is include in this section, it is considered unique.
Here is an example of an Unique Role Section
<UniqueRoles>
<Role ID="10"/>
<Role ID="0"/>
</UniqueRoles>
In this case roles 10 and 0 are considered to be unique.
Item Blocks Section¶
Item Definition Blocks allows the reuse of a group of Item Definitions in different Attribute Definitions. Providing a “hasA” relationship as opposed to the currently supported “isA”. These blocks can then be referenced in the “ItemDefinitions” nodes of Attribute or Group Item Definitions or in the “ChildrenDefinitions” nodes for Reference or Value Item Definitions. Blocks themselves can reference other blocks. But care must be taken not to form a recursive relationship. In the parser detects such a pattern it will report an error.
When referencing a Block, the items will be inserted relative to where the Block is being referenced.
Category constraints are inherited as usual and that Blocks can call other blocks. Here is an example of an Item Block:
<ItemBlocks>
<Block Name="B1">
<ItemDefinitions>
<String Name="s1">
<Categories>
<Cat>Solid Mechanics</Cat>
</Categories>
</String>
<Int Name="i1"/>
</ItemDefinitions>
</Block>
</ItemBlocks>
<Definitions>
<AttDef Type="Type1">
<Categories>
<Cat>Fluid Flow</Cat>
</Categories>
<ItemDefinitions>
<Double Name="foo"/>
<Block Name="B1"/>
<String Name="bar"/>
</ItemDefinitions>
</AttDef>
<AttDef Type="Type2">
<Categories>
<Cat>Heat Transfer</Cat>
</Categories>
<ItemDefinitions>
<Block Name="B1"/>
<String Name="str2"/>
</ItemDefinitions>
</AttDef>
<AttDef Type="Type3">
<Categories>
<Cat>Heat Transfer</Cat>
</Categories>
<ItemDefinitions>
<Block Name="B1" Namespace="globals2"/> <!-- An example of referring to an Item Block that is defined in namespace *globals2* -->
<String Name="str2"/>
</ItemDefinitions>
</AttDef>
</Definitions>
See data/attribute/attribute_collection/ItemBlockTest.sbt and smtk/attribute/testing/cxx/unitItemBlocks.cxx for examples.
The Item Block functionality has been extended so that an Item Block can be exported in one file and consumed in another using the Export XML attribute. In order to better organize Item Blocks, we have also introduced the concept of a namespace that behaves similar to namespaces in C++. A namespace scopes an Item Block and therefore must also be used when referring to an Item Block.
Note Namespaces are only used w/r Item Blocks and they can not be nested.
XML Attribute |
Description |
---|---|
Namespace |
String value representing the default namespace to which all of its Item Blocks belong to. (Optional) Note that if not specified SMTK assumes that the default namespace is the global namespace represented by “” |
XML Attribute |
Description |
---|---|
Name |
String value representing the name of the ItemBlock. (Required) |
Namespace |
String value representing the namespace that the Item Block belongs to. (Optional) Note If not specified it will use the default namespace specified in the ItemBlocks node. |
Export |
Boolean indicating that the ItemBlock should be made available to template files that include this one. (Optional) Note If not specified SMTK assumes that the value is false meaning that the ItemBlock is file-scope only |
Definitions Section¶
This is an optional section that defines a set of attribute definitions used to generate attributes with a SMTK-based program. This section is created using the <Definitions> XML Element. See the example XML for how to create a set of attribute definitions.
This element can contain the following children XML Elements:
XML Child Element |
Description |
---|---|
<AttDef> |
Defines a new Attribute Definition |
<Exclusions> |
Defines a set of Definitions whose Attributes would mutually exclude each other from being associated to the same Resource/Resource Component (Optional). See Exclusions Format. |
<Prerequisites> |
Defines a set of Definitions whose Attributes are required to be associated to a Resource or Resource Component prior to being able to associated Attributes from another Definition to the same Resource or Resource Component (Optional). See Prerequisites Format. |
This element define an attribute definition.
This element can contain the following children XML Elements:
XML Child Element |
Description |
---|---|
<CategoryInfo> |
Specifies the local category constraints specified on the Definition See CategoryInfo Format. |
<ItemDefinitions> |
Defines the items contained within the attributes generated by this definition (Optional). |
<AssociationsDef> |
Defines the association rules associated with this Definition (Optional). See AssociationsDef Format. |
<BriefDescription> |
Provides a brief description of the definition (Optional). |
<DetailedDescription> |
Provides a detailed description of the definition (Optional). |
The following table shows the XML Attributes that can be included in this XML Element.
XML Attribute |
Description |
---|---|
Type |
String value representing the attribute definition type being defined. (Required). Note that this value should be unique with respects to all other definitions being defined with this section as well as all definitions being included via the Includes XML Element (See Includes Section) |
BaseType |
String value representing the attribute defintion that this definition is derived from. (Optional) Note that the base definition must be defined prior to this definition either in section or in the Includes Section. |
Label |
String value representing the name display in a GUI (Optional) Note that if not specified, the Type value is displayed. |
Version |
Integer value representing the “version” of the definition. (Optional) This is used for versioning the definition. If not specified then 0 is assumed. |
Abstract |
Boolean value used to indicate if the definition is abstract or not. (Optional) If not specified, the definition is not abstract. Note that abstract definitions can not generate attributes. |
AdvanceLevel |
Integer value used to indicate the advance level associated with the definition and the attributes it generates for reading and modifying. (Optional) This value should match one of the advance values defined in the Advance Level Section. If not specified, 0 is assumed. |
AdvanceReadLevel |
Integer value used to indicate the advance level associated with the definition and the attributes it generates for reading. (Optional) This value should match one of the advance values defined in the Advance Level Section. If not specified, 0 is assumed. Note if the AdvanceLevel is also specified, AdvanceReadLevel will be ignored. |
AdvanceWriteLevel |
Integer value used to indicate the advance level associated with the definition and the attributes it generates for modifying. (Optional) This value should match one of the advance values defined in the Advance Level Section. If not specified, 0 is assumed. Note if the AdvanceLevel is also specified, AdvanceWriteLevel will be ignored. |
Unique |
Boolean value used to indicate if the attributes this definition generates are unique with respects to the model entities it associated with. A resource or resource component can only have one unique attribute of a given type associated with it. (Optional) If not specified, the definition is assumed to be non-unique. |
Nodal |
Boolean value used to indicate if the attribute effects the nodes of the analysis mesh or the elements. (Optional) If not specified the definition’s attributes are not nodal. |
RootName |
String value used to auto-generate names for attributes. (Optional) If not specified the definition’s type name is used. |
Associations |
String value indicating what type of model entities this definition’s attributes can be associated on. (Optional) The information is represented as a string consisting of a set of the following characters separated by vertical bars (|): v (vertices) e (edges) f (faces) r (volumetric regions) m (model) g (groups) An example would be “e|f” for an attribute which may be associated with both edges and faces. If not specified, the definition’s attributes can not be associated with any model entities. Note This is an old format and is only for model resource components. Instead you should be using an AssociationsDef XML Element. |
NotApplicationColor |
String value representing the color to be used when coloring model entities that are not associated with this definition’s attribute. (Optional) The format is “r, g, b” where r, g, and b are a value between 0 and 1 inclusive. If not specified its value is 0, 0, 0. |
Default Color |
String value representing the color to be used when coloring model entities that are associated with this definition’s attribute by default. (Optional) The format is “r, g, b” where r, g, and b are a value between 0 and 1 inclusive. If not specified its value is 0, 0, 0. |
This subsection of an AttDef Element describes the local category constants imposed on the Definition. All Item Definitions belonging to this Definition, as well as all Definitions derived from this, will inherit these constraints unless explicitly told not to.
The category information is divided into two elements:
<Include> - represents the set of categories that will make this Definition relevant
<Exclude> - represents the set of categories that will make this Definition not relevant.
The children of these elements include <Cat> elements whose values are category names. These elements can optionally include an XML attribute called Combination which can be set to Or or And. If set to And, all of the categories listed must be active in the owning attribute resource in order for the set to be considered satisfied. If Or is specified, then only one of the categories need to be active in order for the set to be satisfied. The default is Or.
The CategotyInfo Element itself can have the following XML Attributes:
XML Attribute |
Description |
---|---|
Combination |
String value representing how the Inclusion and Exclusion sets should be combined. If set to And, then both the Inclusion set must be satisfied and the Exclusion set must not be satisfied in order for the Definition (and the Attributes it generates) to be relevant. If it is set to Or, then either the Inclusion set is satisfied or the Exclusion set is not, in order for the Definition (and the Attributes it generates) to be relevant. (Optional - Default is And) |
InheritanceMode |
String value that indicates how the Definition should combine its local category information with the category information inherited from its Base Definition. If set to And, then its local category information will be and’d with its Base Definition’s. If it is set to Or, then its local category information will be or’d with its Base Definition’s. If it is set to LocalOnly, then the Base Definition’s category information will be ignored. (Optional - the default is And) |
The format is an extension of the Reference Item Based Definitions. When dealing with Model Resource Components you can use a MembershipMask XML Element to indicate the type of Model Resource Component that can be associated.
<AssociationsDef Name="BC Sets" NumberOfRequiredValues="0" Extensible="true">
<MembershipMask>edge</MembershipMask>
</AssociationsDef>
The following table shows the XML Attributes that can be included in this XML Element in addition to those supported by Reference Item Definition.
XML Attribute |
Description |
---|---|
OnlyResources |
Boolean value indicating that the association is only to Resources. (Optional). |
<ReferenceLabels> |
Defines the labels that should be displayed next to the Item’s values. This element should only be specified if the Item is either Extensible or has NumberOfRequiredValues > 1. See Specifying Labels. (Optional) |
Contains a set of Definition exclusion rules. An exclusion rule is a set of Definitions that exclude each other. This means an Attribute from one of these excluded Definition that is associated with a Resource/ Resource Component will prevent attributes from any of the other excluded Definitions from being associated to the same Resource/Resource Component.
XML Child Element |
Description |
---|---|
<Rule> |
Defines a new Exclusion Rule |
Each exclusion rule is a set of Def XML Elements. Each Def XML Element contains the name of a Definition type.
Note A rule must contain at least 2 Def Elements.
XML Child Element |
Description |
---|---|
<Def> |
Contains a Definition Type |
Here is an example of an Exclusion Rule where Definitions A and B exclude each other:
<Exclusions>
<Rule>
<Def>A</Def>
<Def>B</Def>
</Rule>
</Exclusions>
Contains a set of Definition prerequisite rules. An prerequisite rule refers to a Definition and a set of prerequisite Definitions.
XML Child Element |
Description |
---|---|
<Rule> |
Defines a new Prerequisite Rule |
Each Prerequisite rule refers to a Definition that has the prerequisite which is represented as a set of Definitions.
XML Attribute |
Description |
---|---|
Type |
String value representing the Definition type that has the prerequisite |
XML Child Element |
Description |
---|---|
<Def> |
Contains a Definition Type |
Here is an example of an Exclusion Rule where Definition A has a prerequisite requiring that an attribute of Type C must be associated to an object in order to be able to associate an attribute of type A to the same object.
<Prerequisites>
<Rule Type="A">
<Def>C</Def>
</Rule>
</Prerequisites>
This subsection of an AttDef Element contains the definitions of all the items to be created within the attributes created by the attribute definition. The section is represented by the <ItemDefinitions> XML Element and can contain any of the elements described in the Item Definition Section.
Item Definition Section¶
All of the XML Elements described within this section can be added to any of the following elements:
<ItemDefinitions> of an attribute definition <AttDef> or Group Item Definition <Group>
<ChildrenDefinitions> of a Value Item Definition (<Double>, <Int>, <String>)
- The types of items currently supported include:
Basic Values: Doubles, Integers, and Strings
Groups
Attribute References
Directories and Files
Model Information
Voids
All the elements can contain the following children XML Elements. Note that each element may have additional XML Children Elements that are specific to it.
This element can contain the following children XML Elements:
XML Child Element |
Description |
---|---|
<CategoryInfo> |
Specifies the local category constraints specified on the Item Definition. The format is identical to the one used for Attribute Definitions except that the category inheritance mode controls how the Item Definition’s local category information is combined with the category information it inherits from either its owning Attribute or Item Definition. (Optional) See CategoryInfo Format. |
<BriefDescription> |
Provides a brief description of the item (Optional). |
<DetailedDescription> |
Provides a detailed description of the item (Optional). |
All of the elements support the following common XML Attributes. Note that each element may have additional XML Attributes that are specific to it.
XML Attribute |
Description |
---|---|
Name |
String value representing the name of the item being defined. (Required) Note that this value should be unique with respects to all other items contained within this attribute definition (including its Base Type). |
Label |
String value representing the label that should be displayed in the GUI for the item. (Optional - if not specified the Item’s Name will be used) Note if it is set to ‘ ‘, this will indicate that no label should be displayed.. |
Version |
Integer value representing the “version” of the item. (Optional) This is used for versioning the item. If not specified then 0 is assumed. |
Optional |
Boolean value indicating if the item is considered optional or required. (Optional) If not specified the item is considered to be required. |
IsEnabledByDefault |
Boolean value indicating if the item is considered to be enabled by default. (Optional) Note this is only used when Optional=”true”. If not specified, the item is considered to be disabled. |
AdvanceLevel |
Integer value used to indicate the advance level associated with the item. (Optional) This value should match one of the advance values defined in the Advance Level Section. If not specified, 0 is assumed. |
AdvanceReadLevel |
Integer value used to indicate the advance read level associated with the item. (Optional) This value should match one of the advance values defined in the Advance Level Section. Note that this is ignored if the AdvanceLevel XML Attribute is used. If not specified, 0 is assumed. |
AdvanceWriteLevel |
Integer value used to indicate the advance write level associated with the item. (Optional) This value should match one of the advance values defined in the Advance Level Section. Note that this is ignored if the AdvanceLevel XML Attribute is used. If not specified, 0 is assumed. |
A Void Item Definition, represented by a <Void> XML Element, is used to represent boolean information. By itself, it contains no additional information beyond what is specified for the information above and its corresponding item value rely solely on its enabled state. As a result, Void Items are almost always Optional.
Basic Value Items include Strings, Integers and Doubles. They are represented as follows:
XML Element |
Description |
---|---|
<Int> |
Represents an Integer Item Definition |
<Double> |
Represents a Double Item Definition |
<String> |
Represents a String Item Definition |
XML Attribute |
Description |
---|---|
NumberOfRequiredValues |
Integer value representing the minimum number of values the Item should have. (Optional - if not specified assumed to be 1) |
Extensible |
Boolean value indicating that the Item’s number of values can be extended past NumberOfRequiredValues. (Optional - if not specified assumed to be false) |
MaxNumberOfValues |
Integer value representing the maximum number of values the Item can have. A value of 0 means there is no maximum limit. (Optional - if not specified assumed to be 0) Note - this is only used if Extensible is true. |
Units |
String value representing the units the Item’s value(s) are specified in. (Optional - if not specified assumed to be “”) |
XML Child Element |
Description |
---|---|
<DefaultValue> |
For Integer, String, and Double items, this element’s text contains the default value for the item. This element is not allowed for other ItemDefinition types. See Specifying Default Values. (Optional) |
<RangeInfo> |
Defines a value range for the Item’s values. See Specifying Ranges. (Optional - if not specified there is no range constraint.) |
<ComponentLabels> |
Defines the labels that should be displayed next to the Item’s values. This element should only be specified if the Item is either Extensible or has NumberOfRequiredValues > 1. See Specifying Labels. (Optional) |
<ExpressionType> |
Indicates that the Item’s value(s) can be represented using an expression of the indicated type. (Optional) |
<DiscreteInfo> |
Indicates that the Item is restricted to a discrete set of values See Modeling Discrete Information Section (Optional) |
<ChildrenDefinitions> |
A set of Item Definitions that are referenced in the Item’s <DiscreteInfo> element. The contents is identical to an Attribute Definition’s <ItemDefinitions> element. (Optional) |
For items that are not discrete and not extensible but do have NumberOfRequiredValues greater than 1, it is possible to provide a different default value for each component. In this case, commas are assumed to separate the values. If you wish to use a different separator, specify the “Sep” attribute on the DefaultValue tag.
For example, a String item with 3 components might use
<DefaultValue Sep=":">Oh, my!:Oh no!:Yes, please.</DefaultValue>
to specify different defaults for each component. You can also use the separator to prevent a default value from having per-component values. For instance, the same String item might use
<DefaultValue Sep=":">Commas, they are my style.</DefaultValue>
to force the default value to have a single entry used to initialize all components.
The RangeInfo Element can indicate the min and/or max value for the an Item’s value(s) as well as indicating if these values are inclusive or exclusive. This element is used for Double or Integer Item Definitions.
XML Child Element |
Description |
---|---|
<Min> |
Sets the minimum value for the Item’s value(s) (Optional) |
<Max> |
Sets the maximum value for the Item’s value(s) (Optional) |
XML Attribute |
Description |
---|---|
Inclusive |
Boolean value indicating if an Item’s value can be set to the min / max value. (Optional - if not specified it is assumed to be false) |
In the example below, CommonDouble must be strictly > 0.
<Double Name="CommonDouble" Label="Floating Pt Val" Version="0" NumberOfRequiredValues="1">
<DefaultValue>3.1415899999999999</DefaultValue>
<RangeInfo>
<Min Inclusive="false">0</Min>
</RangeInfo>
</Double>
XML Attribute |
Description |
---|---|
CommonLabel |
String Value representing the label that should precede each of the Item’s values when being displayed (Optional) |
If each value requires a different label, this element would contain a set of <Label> elements (one for each value) as shown below:
<ComponentLabels>
<Label>t</Label>
<Label>u</Label>
<Label>v</Label>
<Label>w</Label>
</ComponentLabels>
Note - Individual labels are not supported for extensible Items.
Used when a Double, Integer, or Double Item is restricted to a set of values. Each value can optionally have an enumeration string associated with that would be displayed in an UI instead of the value in order to improve readability. In addition, an enumeration can have categories associated with it, indicating that in addition to satisfying the Item’s category requirements, these requirements must also be met in order for the Item to be set to that particular value. An enumeration can also have an Advance Level associated with it.
An enumeration can also refer to a subset of the Item’s Children Definitions indicating additional Items that need to be filled out when set to that value.
XML Attribute |
Description |
---|---|
DefaultIndex |
Integer value representing the index of the the Item’s discrete value to be used as the Item’s default value. |
XML Child Element |
Description |
---|---|
<Value> |
Simple form when defining only discrete values without category information or associated Item Definitions. |
<Structure> |
Complete form which contains a <Value> Element |
Note - The DiscreteInfo element can be composed of a mixture of both types of children elements.
The Value Element defines the discrete value as well as an optional enumeration string and/or advance level.
XML Attribute |
Description |
---|---|
Enum |
String value representing the enumeration associated with this discrete value (Optional - if not specified, the value will be used) |
AdvanceLevel |
Integer value representing the advance level associated with the value. (Optional - if not specified, 0 is assumed) |
<Value Enum="e3" AdvanceLevel="1">c</Value>
The Structure Element contains a Value element along with optional category and required Item Definitions.
XML Child Element |
Description |
---|---|
<Value> |
Simple form when defining only discrete values without category information or associated Item Definitions. See Simple Discrete Value Form. |
<CategoryInfo> |
Specifies the additional category constraints associated with this discrete value. The format is identical to the one used for Attribute Definitions except that the category inheritance mode is not used. (Optional) See CategoryInfo Format. |
<Items> |
Represents the Children Items that are associated with this discrete value. These names should be defined in the Item’s ChildrenDefinitions element. |
<Structure>
<Value Enum="Specific Enthalpy Function">specific-enthalpy</Value>
<Items>
<Item>specific-enthalpy</Item>
</Items>
</Structure>
A Group Item Definition is defined as a vector of Item Definitions. The Group Item the Definition produces will refer to the corresponding vector of Items produced as a group. It can also be used to define a set of choices that the user needs to select from. In this case, all of the internal Item Definitions are assumed to be optional.
XML Attribute |
Description |
---|---|
NumberOfRequiredGroups |
Integer value representing the minimum number of Item groups the Item should have. (Optional - if not specified assumed to be 1) |
Extensible |
Boolean value indicating that the Item’s number of groups can be extended past NumberOfRequiredGroups. (Optional - if not specified assumed to be false) |
IsConditional |
Boolean value indicating that the Group Item represents a vector of choices. (Optional - if not specified assumed to be false) Note - this is only used if Extensible is true. |
MinNumberOfChoices |
Integer value representing the minimum number of choices that need to be selected in order for the item to be considered valid. (Optional - if not specified assumed to be 0) Note - this is only used if IsConditional is true. |
MaxNumberOfChoices |
Integer value representing the maximum number of choices that can be selected in order for the item to be considered valid. Setting this to 0 indicates that there is no maximum. (Optional - if not specified assumed to be 0) Note - this is only used if IsConditional is true. |
XML Attribute |
Description |
---|---|
<ComponentLabels> |
Defines the labels that should be displayed next to the Item’s values. This element should only be specified if the Item is either Extensible or has NumberOfRequiredGroups > 1. See Specifying Labels. (Optional) |
<ItemDefinitions> |
Defines the items contained within the group generated by this Item Definition (Optional). |
Here is an example of a Group Item Definition that represents a sets of choices. Please see smtk/data/attribute/attribute_collection/choiceGroupExample.sbt for more examples.
<Group Name="opt1" Label="Pick At Least 2"
IsConditional="true" MinNumberOfChoices="2" MaxNumberOfChoices="0">
<ItemDefinitions>
<String Name="opt1"/>
<Int Name="opt2"/>
<Double Name="opt3"/>
</ItemDefinitions>
</Group>
It produces the following in ModelBuilder:
A Reference Item Definition creates Items that can refer to other SMTK Persistent Objects such as Resources and Resource Components. It makes use of the queries functionally provided by the various resources. Component and Resource Item Definitions are derived from Reference Item Definition and therefore have many of the same XML attributes and children components in common.
XML Attribute |
Description |
---|---|
NumberOfRequiredValues |
Integer value representing the minimum number of values the Item should have. (Optional - if not specified assumed to be 1) |
Extensible |
Boolean value indicating that the Item’s number of values can be extended past NumberOfRequiredValues. (Optional - if not specified assumed to be false) |
MaxNumberOfValues |
Integer value representing the maximum number of values the Item can have. A value of 0 means there is no maximum limit. (Optional - if not specified assumed to be 0) Note - this is only used if Extensible is true. |
EnforceCategories |
Boolean value indicating if the reference item’s validity check should also check to see if the item is pointing to an Attribute that would be considered relevant based on the active categories |
HoldReference |
Boolean value that indicates whether the item should be forcing the Persistent Object to be kept in memory. (Optional: Default is false) Note - this is currently used for Operation Parameters to make sure removed Resources and/or Components are kept in memory after the operation runs. |
LockType |
String value that indicates whether the resource being referred to should be locked. Acceptable values are : DoNotLock, Read or Write (Optional) Note - this is currently used for Operation Parameters only! |
Role |
Integer value that sets the Role of the links used by the Reference Item. The main use for setting the Role is to use an Unique Role. See Unique Roles Section. |
XML Attribute |
Description |
---|---|
<Accepts> |
Defines the set of acceptance rules. In order for a SMTK Persistent Object to be referenced by this item, it must pass at least one of these rules (if any have been specified). These are represented by a set of Resource Elements. See Resource Query Format. (Optional) |
<Rejects> |
Defines the set of rejection rules. In order for a SMTK Persistent Object to be referenced by this item, it must not pass any of these rules (if any have been specified). These are represented by a set of Resource Elements. See Resource Query Format. (Optional) |
<ComponentLabels> |
Defines the labels that should be displayed next to the Item’s values. This element should only be specified if the Item is either Extensible or has NumberOfRequiredGroups > 1. See Specifying Labels. (Optional) |
<ChildrenDefinitions> |
Defines the items contained within the reference item generated by this Item Definition (Optional). |
<ConditionalInfo> |
Defines which of the children items should be considered active based on the type of Persistent Object the Item is referencing. See Specifying Conditional Information for Reference Items. (Optional) |
This is represented by a <Resource> Element and represents a query that can be used by ReferenceItems and Attribute Associations.
XML Attribute |
Description |
---|---|
Name |
String value that represents a Resource Type Name. For example smtk::attribute::Resource. These names are typically defined in the Resource class’s smtkTypeMacro. |
Filter |
String value representing a query that is valid for Resources referred by the above Name XML attribute. (Optional - if not specified, the Item is referencing Resources not Resource Components) |
Component Items can reference a Resource Component and includes all of the structure for Reference Item Based Definitions.
XML Attribute |
Description |
---|---|
<ComponentLabels> |
Defines the labels that should be displayed next to the Item’s values. This element should only be specified if the Item is either Extensible or has NumberOfRequiredValues > 1. See Specifying Labels. (Optional) |
Resource Items can reference a Resource and includes all of the structure for Reference Item Based Definitions.
XML Attribute |
Description |
---|---|
<ResourceLabels> |
Defines the labels that should be displayed next to the Item’s values. This element should only be specified if the Item is either Extensible or has NumberOfRequiredValues > 1. See Specifying Labels. (Optional) |
Provides the ability to activate the Item’s children items based on what the Item is currently pointed to. In the following example, the Item is pointing to a LiquidMaterial the Item will have 2 active children named initialTemp and initialFlow, but if it is pointing to a SolidMaterial it will only have 1 active child named initialTemp.
<ConditionalInfo>
<Condition Resource="smtk::attribute::Resource" Component="attribute[type='SolidMaterial']">
<Items>
<Item>initialTemp</Item>
</Items>
</Condition>
<Condition Component="attribute[type='LiquidMaterial']">
<Items>
<Item>initialTemp</Item>
<Item>initialFlow</Item>
</Items>
</Condition>
</ConditionalInfo>
XML Attribute |
Description |
---|---|
Resource |
String value that represents a Resource Type Name. For example smtk::attribute::Resource. These names are typically defined in the Resource class’s smtkTypeMacro. (Optional - if not specified, it assumes that Component’s query string is applicable to all Resource Components that passes the Item’s Accept/Reject conditions.) |
Component |
String value representing a Resource Component query that is valid for Resources referred by the above Resource XML attribute if specified or for Resources that passes the Item’s Accept/Reject conditions if it is not specified. (Optional - if not specified, the Item is referencing Resources and not Resource Components) |
Note - that at least one of the Resource or Component XML Attribute must be specified.
XML Child Element |
Description |
---|---|
<Items> |
Represents the Children Items that are associated with this discrete value. These names should be defined in the Item’s ChildrenDefinitions element. |
Attribute Section <Attributes>¶
Todo
Describe attributes and how they are serialized
Views Section <Views>¶
The Views section of an SBT file contains multiple <View> items, one of which should be marked as a top-level view by adding the TopLevel attribute to it. The top-level view is typically composed of multiple other views, each of which may be defined in the Views section.
Each <View> XML element has attributes and child elements that configure the view. The child elements may include information about which attributes or classes of attributes to present as well as how individual items inside those attributes should be presented to the user.
XML Attribute |
Description |
---|---|
Type |
A string that specifies the View’s type and should match the View class name or alias that was used to register the View implementation with the view::Manager |
Title |
A string that represents the name of the View |
Label |
A string that is used in place of the View’s title when it is displayed in the UI. (Optional - if not specified, the View’s title is used.) |
TopLevel |
Boolean value indicating whether the view is the root view that should be presented to the user or (if TopLevel is false or omitted) the view is a child that may be included by the toplevel view. (Optional - assumes it is not a top-level view if not specified.) |
Alias |
Type Name |
---|---|
Analysis |
smtk::extension::qtAnalysisView |
An Analysis View represents the analysis structure defined in an Attribute Resource. The View will set an Attribute Resource’s Active Categories based on the choices the user has made in the View. The View will generate both an Attribute Definition and Attribute based on the XML attributes if needed.
<View Type="Analysis" Title="Configurations" AnalysisAttributeName="analysis" AnalysisAttributeType="analysis">
</View>
XML Attribute |
Description |
---|---|
AnalysisAttributeName |
A string that defines the name of the Attribute representing the Analysis. Note - this name should be unique within the Attribute Resource |
AnalysisAttributeType |
A string that defines the type name of the Attribute Definition representing the Analysis structure. Note - this type name should be unique within the Attribute Resource |
Here in an example UI of an Analysis View.
Alias |
Type Name |
---|---|
Associations |
smtk::extension::qtAssociationView |
An Associations View allows users to change an Attribute’s associations but does not allow the user to change the Attribute’s values.
<View Type="Associations" Title="Materials Associations">
<AttributeTypes>
<Att Type="Material">
</Att>
</AttributeTypes>
</View>
The XML Element should have one child element called <AttributeTypes>. The <AttributeTypes> element is composed of a set of <Att> Elements. Each <Att> must contain an XML Attribute named Type which refers to an Attribute Definition type name.
The View will provide a drop-down list of all smtk::attribute::Attributes that are of (or derived from) one of the specified Attribute Definition types.
Here in an example UI of an Associations View.
Alias |
Type Name |
---|---|
Attribute |
smtk::extension::qtAttributeViewView |
An Attribute View allows users to create, modify, and delete smtk::attribute::Attributes.
<View Type="Attribute" Title="Materials">
<AttributeTypes>
<Att Type="Material"/>
</AttributeTypes>
</View>
XML Attribute |
Description |
---|---|
DisableNameField |
A boolean that indicates the user should not be able to edit the names of Attributes (Optional - default is false) |
DisableTopButtons |
A boolean that indicates the top buttons (New, Copy, and Delete) should be disabled thereby preventing the user from creating or deleting Attributes. (Optional - default is false) |
HideAssociations |
A boolean that indicates if the View GUI should not display association information. (Optional - default is false) |
RequireAllAssociated |
A boolean that indicates if all Resource/Resource Components that can be associated to a specific type of Attribute should have an Attribute of that type associated with it in order to be considered valid. (Optional - default is false) |
AvailableLabel |
A string that is used to indicate the Resources/Components that could be associated with the Attribute. (Optional - default is Available) |
CurrentLabel |
A string that is used to indicate the Resources/Components that are currently associated with the Attribute. (Optional - default is Current) |
AssociationTitle |
A string that is the title of the widget used deal with the Attribute’s association information. (Optional - default is Model Boundary and Attribute Associations) |
DisplaySearchBox |
A boolean that indicates if the user should be able to search Attributes by name. (Optional - default is true) |
SearchBoxText |
A string that is used in the search box to indicate what the box is for. (Optional - default is Search attributes…) |
AttributeNameRegex |
A string that is used to determine if the name the user entered for an Attribute is valid. (Optional - default is there is no constraint on the name of an Attribute) |
The XML Element should have one child element called <AttributeTypes>. The <AttributeTypes> element is composed of a set of <Att> Elements. Each <Att> must contain an XML Attribute named Type which refers to an Attribute Definition type name.
The View will provide a drop-down list of all non-abstract smtk::attribute::Definitions that are specified (or derived from) the types mentions in the <AttributeTypes> Element. When the user selects one of the Definitions, it will display all of the existing Attributes that match the type in the list view.
When an attribute is selected from the list, the view also displays editing fields for the items contained by the attribute, and an Association widget for assigning the attribute to resource components. (The associations widget is only displayed if the corresponding Attribute Definition includes associations and the HideAssociations option is not set to true.)
Here in an example UI of an Attribute View.
Alias |
Type Name |
---|---|
Group |
smtk::extension::qtGroupView |
A Group View represents a collection of other Views. This collection is laid out based on the Style attribute. The supported styles are:
tiled - each child view is displayed vertically in the Group View’s Frame.
tabbed - each child view is displayed in its own separate tab.
groupbox - each child view is displayed vertically within its own collapsible Frame.
<View Type="Group" Name="TopLevel" FilterByAdvanceLevel="true" TabPosition="North" TopLevel="true">
<Views>
<View Title="Set Analysis" />
<View Title="Materials" />
<View Title="Boundary Conditions" />
</Views>
</View>
Here is an example of a Group View with Style set to Tabbed.
Here is the same Group View with Style set to Tiled.
Finally, here is the same Group View with Style set to GroupBox. In this case one of the children is opened and other is closed.
XML Attribute |
Description |
---|---|
Style |
A string that indicates the style to be used to display the View’s children Views. (Optional and Case Insensitive - default is tabbed) |
ActiveTab |
A string that corresponds to a child view name. If set and Style is tabbed, then the corresponding tab will be displayed. (Optional) |
TabPosition |
A string that corresponds to which side the tabs of a tabbed Group View should be displayed (north, south, east, or west). (Optional and Case Insensitive - default is north) |
This XML element consists of a single child element called <Views>. The <Views> element contains a list of <View> XML Elements.
XML Attribute |
Description |
---|---|
Title |
String value that refers to an existing View that will be displayed as a child to this one. |
Open |
A boolean that indicates if the groupbox for that child view should be open or closed. (Optional - default is closed) |
Alias |
Type Name |
---|---|
Instance |
smtk::extension::qtInstanceView |
An Instance View will display a set of an Attributes for the user to edit.
<View Type="Instanced" Title="General">
<InstancedAttributes>
<Att Name="numerics-att" Type="numerics"/>
<Att Name="outputs-att" Type="outputs" />
<Att Name="simulation-control-att" Type="simulation-control" />
</InstancedAttributes>
</View>
The XML Element should have one child element called <InstancedAttributes>. The <InstancedAttributes> element is composed of a set of <Att> Elements.
XML Attribute |
Description |
---|---|
Name |
A string that indicates the name of the Attribute. |
Type |
A string that indicates the type of the Attribute. |
Style |
A string that indicates the Style to be used to display the Attribute. (Optional) |
Here in an example UI of an Associations View.
If you wish to customize how items of the chosen attributes are presented, you should add an <ItemViews> child to the <Att> tags in the attribute selectors covered in the previous section. Inside <ItemViews> you can add a <View> tag for each item whose appearance you wish to customize.
Todo
Describe <ItemsViews> children
XML Attribute |
Description |
---|---|
Type |
The name of a widget you would like the application to use to present the item (and potentially its children as well) to users. SMTK provides “Box” (for showing a 3-D bounding box widget), “Point” (for showing a 3-D handle widget), “Line”, “Plane”, “Sphere”, “Cylinder”, “Spline”. These types usually only make sense for Group items, although “Box” can also be used for a Double item with 6 required values (xmin, xmax, ymin, ymax, zmin, zmax). Items may also have custom views provided by the application or external libraries; in that case, the Type specifier will be the string used to register the custom view. |
ItemMemberIcon, ItemNonMemberIcon |
These attributes are only used for ReferenceItem, ResourceItem, and ComponentItem classes. They specify paths (either on disk or, with a leading colon, to Qt resource files) to images to use as icons that display whether a persistent object is a member of the item or not. |
Filtering, Searching and Reference Item Rules¶
It is possible to ask the attribute resource to filter its attributes using a string that specifies some search criteria (i.e., a filter). The current implementation expands on the base functionality provided by smtk::resource::Resource::queryOperation which allows users to filter components based on the existence and values of properties by adding the ability to search for attributes based on their type (i.e., the name of its definition and those definitions which inherit it).
Note that these query strings can be used in a resource’s find(…) method as well as in a Reference Item Definition’s Accept or Reject rules
The current grammar syntax for attribute resources is:
One of the following : attribute, *, or any followed by [
One or more of the following sub-phrases:
Attribute Type Phrase
type = followed by one of the following
‘attribute-type-name’
/regular-expression/
Property Phrase
property-type optionally followed by
{ property-name optionally followed by
= property-value
}
]
For example, consider an attribute resource consisting of the following:
Definitions (where a : b means a is derived from b)
Base
MyDef1 : Base
MyDef2 : Base
Attributes (where a : d means Attribute a is based on Definition d)
a : Base
b : MyDef1
c : MyDef2
c1 : MyDef2
Lets also add some properties to these attributes: + a has long property alpha = 10 + b has long property beta = 100 + c has long property alpha = 200 + c1 has long property alpha = 10 and double property gamma = 3.14
Query string |
Results |
---|---|
“ |
Returns a, b, c, c1 since all are derived from Definition Base. |
“ |
Returns c, c1 since they are derived from Definition MyDef2. |
“ |
Returns b, c, c1 since each has a Definition in its derivation whose type matches the RegEx. |
“ |
Returns a, c, c1 since they are have a long property named alpha. |
“ |
Returns a, c, c1 since they are have a long property named alpha. |
“ |
Returns c, c1 since they are have a long property named alpha and are derived from MyDef2. |
“ |
Returns a since it has a long property named alpha with value 10. |
“ |
Returns c1 since it is the only one with both a double named gamma and long property named alpha. |
See smtk/attribute/testing/cxx/unitAssociationTest.cxx for a complete example.
Evaluators¶
Motivation¶
Evaluators are an addition to SMTK’s Attribute system that are a mechanism to process an Attribute that is an expression. ValueItem already supported linkage of Attributes to a ValueItem by means of the <ExpressionType> SBT tag, however Evaluators provide a means to transform the Items in those expression Attributes into meaningful results inside SMTK, as opposed to consumers of an Attribute Resource. These results can be shown to the user in Qt components and serialized by consuming application code.
This document outlines changes and additions to support this feature.
Evaluator¶
smtk::attribute::Evaluator is the abstract base class of Evaluators. Subclasses must implement the method evaluate(), which processes the Attribute.
Implementations of evaluate() which depend on trees of expressions must manage the state of dependent symbols (i.e. Attributes) by using the API provided in smtk::attribute::SymbolDependencyStorage. SymbolDependencyStorage provides methods to maintain and check for expressions which depend on the result of one another, preventing infinite recursion in evaluate().
EvaluatorFactory¶
Evaluator are created per Attribute through EvaluatorFactory. Evaluators types are registered via template with an std::string alias, and multiple definition can be used for a single Evaluator, as long as they conform to the Items needed by the Evaluator. A Definition can only be used with one type of Evaluator at a time, else EvaluatorFactory could create multiple Evaluators for Attributes of that Definition type.
EvaluatorFactory::createEvaluator(Attribute) returns a std::unique_ptr to an Evaluator. Any consumer with access to an EvaluatorFactory can create an Evaluator as long as it is evaluatable.
Evaluator Registration¶
Plugins can register evaluator via a Registrar as is done by default for smtk::attribute::InfixExpressionEvaluator in smtk::attribute::Registrar with smtk::attribute::EvaluatorManager. This means an smtk::attribute::Resource created with an smtk::resource::Manager will have their EvaluatorFactory seeded with the Evaluator type and std::string alias specified in the Registrar.
Serialization/deserialization¶
The designer of an SBT can specify the Evaluator to be used for a Definition in the following way, as in the case of InfixExpressionEvaluator.
<Definitions>
<AttDef Type="infixExpression" Label="Expression">
<ItemDefinitions>
<String Name="expression" Extensible="true"/>
</ItemDefinitions>
</AttDef>
</Definitions>
<Evaluators>
<Evaluator Name="InfixExpressionEvaluator">
<Definition Type="infixExpression">
</Definition>
</Evaluator>
</Evaluators>
This means that an Evaluator registered with alias InfixExpressionEvaluator will be created by EvaluatorFactory for Definitions of type infixExpression. The Definition infixExpression has a StringItem called expression because InfixExpressionEvaluator expects an infix math expression in string form.
Changes to smtk::attribute::Resource and smtk::attribute::ValueItemTemplate¶
Attribute Resources are now constructed with an EvaluatorFactory to obtain Evaluators for Attributes in the Resource.
ValueItemTemplate has been modified to attempt to evaluate() expressions set on the Item in value(std::size_t element), and serialize their result to std::string in valueAsString().
Infix Expression Parsing¶
A tao PEGTL grammar and structures for computng infix expressions have been added to smtk::common in InfixExpressionGrammarImpl.h and InfixExpressionEvalution.h, respectively. These are wrapped together by InfixExpressionGrammar.
UI Additions Specific to Infix Expressions¶
A new qtItem has been added to the smtk::extension namespace. qtInfixExpressionEditor shows the result of an infix expression as the user changed a QLineEdit. This is linked to the underlying StringItem of the infix expression. It is created by specifying an ItemView with Type InfixExpression.
qtInputsItems has been modified to show the result of an evalutable expression displayed in a read-only QLineEdit when the Item is in expression mode and the expression Attribute is evaluatable.
Attribute Builder (Python, experimental)¶
An extension to the Python API is available for editing attribute components
from a dictionary-style specificiation. The API is implemented as a single
Python class smtk.attribute_builder.AttributeBuilder
. The initial
use for this feature is for replaying Python operation traces. To support this,
the following method is provided:
- smtk.attribute_builder.AttributeBuilder.build_attribute(self, att: smtk.attribute.Attribute, spec: dict:[str, list], resource_dict:[str, smtk.resource.Resource]=None) None ¶
This method modifies the contents of the input attribute based on the input
specification. In addition to the attribute and specification arguments, the
builder_attribute()
method includes an optional resource_dict
argument for specifying SMTK resource instances that can be referenced in the
specification dictionary.
Specification¶
The input specification is a Python dictionary object with two fields for
specifying a list of associations
and a list of items
.
Here is an example of the specification format:
{
'associations': [{'resource': 'model'}],
'items': [
{'path': 'string-item', 'value': 'xyzzy'},
{'path': '/double-item', 'value': [3.14159, None, 2.71828]},
{'path': 'int-item', 'enabled': True, 'value': 2},
{'path': 'int-item/conditional-double', 'value': 42.42},
{'path': 'comp-item', 'value': [
{'resource': 'model', 'component': 'casting'},
{'resource': 'model', 'component': 'symmetry'},
]},
{'path': '/group-item/subgroup-double', 'value': 73.73},
]
}
Note
The current implementation will raises a Python exception if it encounters a syntax error in the specification or if some editing step fails.
Attribute associations are specified by a list of dictionary items. Each item
includes a resource
key with its value set to one of the keys in the
resource dictionary (passed in to the build_attribute()
method).
This is sufficient to specify a resource association.
To specify a component belonging to a resource, a component
field
is added to specify the name of the component.
Item modifications are specified by a list of dictionary items. The fields for
each dictionary item can include path
(required),
enabled
, and value
.
The
path
field specifies the path from the attribute to the item being specified. It uses the same syntax as thesmtk.attribute.Attribute.itemAtPath()
method, with forward slash as the separator and the :py:attr:activeOnly argument set to :py:const:false. (Starting the path with a forward slash is optional.)The
value
field is for setting the item value. This can be a single value or a list of values. The type (string, int, float) and number of values must be consistent with the item and its corresponding item-definition, of course. ForReferenceItem
,ComponentItem
, andResourceItem
, the value is specified using the same syntax used in theassociations
list. If the value, or any of the values in a list, areNone
the corresponding item value will beunset
.The
enabled
field is for setting the item’s enabled state.Extensible group items may have several sub-groups. The
count
field for a group item specifies the number of sub-groups. Items in a sub-group use integer index N in their path to specify which sub group they belong to. For example:
- {
- ‘items’: [
{‘path’: ‘/group-item’, ‘count’: 2}, {‘path’: ‘/group-item/0/subgroup-double’, ‘value’: 73.73}, {‘path’: ‘/group-item/1/subgroup-double’, ‘value’: 83.83},
]
}
If an item belonging to a group needs to have a name that is an integer, it should always be preceded by the sub-group index.
SMTK’s Operation System¶
Actions that create, modify and destroy resources and resource components are encapsulated within operators.
Operators¶
In SMTK, operations performed on resources are accomplished with
instances of the Operation class.
Each operation is a subclass that contains the code to perform the
operation by overriding the operate()
and ableToOperate()
methods.
These subclasses register themselves (via SMTK’s auto-init macros)
with a Manager.
Operations are the only places where persistent objects (components and resources) should be modified, created, or destroyed. This is because operations hold locks to the resources they accept as inputs and may run in parallel in other threads. By using the locking mechanism which operations provide, you avoid race conditions, memory stomping, and many other issues encountered with parallel code.
The next sections describe in detail: first, how operators specify the inputs they require and outputs they produce; and second, how operators register themselves for introspection by applications and scripts.
Inputs and Outputs¶
The inputs that an operator requires in order to run and the outputs that an operator may produce are each modeled as attribute definitions. These definitions reside in an attribute resource.
Each operator’s inputs are specified by a Definition that inherits a base attribute-definition named “operator” while the its outputs are specified by a Definition that inherits a base attribute-definition named “result”. Furthermore, if an operator’s input specification is named “foo”, its output specification must be named “result(foo)”.
SMTK uses this naming convention to construct results automatically. Given an instance of an Operator subclass, you can call its specification() method to obtain the Attribute which models its inputs. When the operator is executed, it returns an Attribute instance that models its result.
Recall that an attribute resource holds definitions which contain item definitions. When an attribute is instantiated from a definition, all its active item definitions are instantiated as items. The definitions themselves specify what types of persistent objects may be associated with their attributes using a list of pairs of specifier strings; the first string in the pair specifies the type-name of resource while the second string specifies constraints on what types of components are allowed. Associations between an operation’s parameters and persistent objects such as model entities denote what objects the operator will act upon. The operation’s parameters may also contain attribute items that specify other configuration information for the operation such as point locations, geometric distances, etc. that determine how the operation will proceed.
The fact that inputs and outputs are specified using SMTK’s own attribute resource means that one need not construct an instance of the operator’s C++ class in order to obtain information about it; instead, simply call operatorSystem() on the session and ask for all the definitions which inherit “operator”.
Operation support¶
Besides the operations themselves, SMTK provides applications with support for determining which operations are apropos, scheduling operations, and responding to their results.
Sideband information¶
Applications may need to pass information to or retrieve information from operations. While technically applications could always modify their operations’ input parameters and output results, this is often inconvenient or aesthetically displeasing – especially if an operation may be used in different ways by different applications. Thus, SMTK provides a way to pass “side-band” or “out-of-band (OOB)” information to and from operations.
For example, an operation may be used inside a script where user interaction is not possible as well as inside an interactive client. When run interactively, the operation may wish to indicate to the application how to adjust camera parameters to focus on a modified component. While the operation’s definition could be modified to include camera information in its result data, this is undesirable because it clutters the output with information that is specific to the application’s behavior rather than the operation’s behavior. Also, there may be many operations that all need to provide this information keeping all of their result definitions consistent with one another is not easy to maintain.
In these cases, you may place application-specific information into an smtk::common::Managers object that you provide to the operation manager via a shared pointer. Any operation constructed by the operation manager will have access to the Managers object and can fetch arbitrary data from it in a type-safe manner. Be aware that when using asynchronous operation launchers, multiple operations may simultaneously access data in the Managers object; methods that may be accessed in this way must be re-entrant.
Groups¶
Operations specific to a modeling session often implement functionality present in many different sessions using a consistent set of parameters. SMTK provides a grouping mechanism so that applications can find sets of operations that provide “like” functionality.
For example, a CAD modeling session and a discrete modeling session
will generally provide operations to import, export, read, and write data.
Each of these tasks has its own group of operations that constrain their
member operations in different ways.
Consider the task of importing data;
import operations all require the location of the data to be imported.
The smtk::operation::ImporterGroup holds a list of
operations that can import files. It constrains operations added to the
group to have a filename
item.
Registration¶
Operators are registered with an operation manager via a Registrar class
that implements registerTo()
and unregisterFrom()
methods.
Besides informing the operation manager of classes implementing operations,
your registerTo()
method should also add operations to groups as needed.
Finally, each operation class may have an icon.
To register an icon for your operation, your Registrar class
should provide a second registerTo()
method that accepts a
view manager.
The view manager provides an operationIcons()
method that you can
use to register an icon to your operation.
Icons are functors that take in a secondary (background) color and return
a string holding SVG for an icon that contrasts well with the background.
Launching operations¶
You can always call the operate()
method of an operation instance
to run it immediately and synchronously.
However, operations may take a long time to complete and should not
interfere with applications tending to user input in the meantime.
Because of this, operations may also run in a separate thread from
user interface components.
The job of running operations asynchronously is delegate to
operation launchers.
SMTK provides a default launcher and one specific to the Qt
library (which is discussed in the Qt section).
If you use a launcher to run an operation, instead of getting
an operation result object, you will receive
a std::shared_future<Operation::Result>
.
Shared futures provide a way for applications to check whether
an operation is complete as well as block until an operation
completes (if needed).
In order to avoid multiple operations from making simultaneous changes to the same objects at once, the input parameters are scanned and each involved resource is read-locked or write-locked (depending on how the operation’s parameters are marked) as needed before the operation runs. Thus, operations that run on separate resources may run simultaneously, as may operations that only require read access to the same resource. However, operations that require write access to the same resource will be run sequentially.
Observing operations¶
Regardless of whether an operation is run synchronously or asynchronously, if it was created by an operation manager, the manager will invoke any observers that have been registered with it at two points:
When an operation is about to run, observers are invoked that may inspect the operation and its parameters. Any of the observers may cancel the operation at this point, so there is no guarantee that observing an operation before it is run will generate a second event.
When an operation has run, observers are invoked that may inspect the operation, its parameters, and its result (which indicates success or failure and which may also contain additional information).
While in general these observations may occur on any thread, most applications will force the observations to occur on the main thread because user-interface toolkits are rarely re-entrant.
Operation Hints¶
Generally, operations should avoid side effects (Sideband information) and aim to be purely functional (modifying or deleting only their inputs and possibly creating new output). However, for applications to behave intuitively, side effects are often desirable. For example, if an operation lofts two input curves to create a surface, the user might expect the application selection to hold the surface after the operation rather than the input curves (allowing them to run an extrusion operation directly).
To address this apparent paradox, SMTK provides operation results with hints that the application (rather than the operation) can use to update its state. Each hint is an attribute that held in a smtk::attribute::ReferenceItem named “hints” in the operation’s result.
Hint |
Description |
---|---|
render focus hint |
The camera will have its aim point adjusted to the center of the bounds of all the objects associated to the hint. |
selection hint |
The selection will be modified according to the hint’s “action” item (replace, add, subtract) using the “value” from the hint as the integer value in the map from the associated objects to selection values. If the “ephemeral” item is enabled, then the application should attempt to delete the associated objects when they are removed from the selection. |
browser scroll hint |
Scroll the resource browser tree to the first appearance of the first object associated to this hint. |
browser expand hint |
Expand the resource browser tree items to show all occurrences of each of the objects associated to the hint. |
There are several free functions in smtk/operation/Hints.h
that you
can use to add hints to your operation and inspect hints in your
application’s operation-observers.
SMTK’s Geometric Model Resource¶
SMTK’s second major component is its geometric modeling resource, which allows you to interact with multiple modeling sessions that may each interact with different solid modeling kernels.
Key Concepts¶
Like the attribute resource, the model resource is composed of C++ classes, also accessible in Python, whose instances perform the following functions:
- Resource
instances contain model topology and geometry. All of the model entities such as faces, edges, and vertices are assigned a UUID by SMTK. You can think of the resource as a key-value store from UUID values to model entities, their properties, their arrangement with other entities, their ties to the attribute resource, and their tessellations.
- Session
instances relate entries in a model Resource to a solid modeling kernel. You can think of the entities in a model Resource as being “backed” by a solid modeling kernel; the session provides a way to synchronize the representations SMTK keeps in a Resource and their native representation in the modeling kernel SMTK is interfacing with.
- Entity
is a subclass of Component specific to the model resource. The resource holds an Entity record for each vertex, edge, face, etc. since the types of information stored for geometric entities is similar. Methods that are specific to the type of geometry embodied by the entity are defined via the EntityRef class and its subclasses below.
- EntityRef
instances are lightweight references into the Entity records held by a model Resource’s storage. Each represents a single entity (e.g., a vertex, edge, face, or volume) and provide methods for easily accessing related entities, properties, tessellation data, and attributes associated with that entity. They also provide methods for manipulating the model Resource’s storage but these methods should not be used directly; instead use an Operation to modify the model so that the modeling kernel and model resource stay in sync. EntityRef subclasses include Vertex, Edge, Face, Volume, Model, Group, UseEntity, Loop, Shell, and so on. These are discussed in detail in Model Entities below.
Model Entities¶
This section covers two aspects of SMTK’s model entities: the implementation details such as the organization of the class hierarchy; and the principles and governing philosophy that the model entities embody.
Implementation¶
As mentioned above, the model Resource class is the only place where model topology and geometry are stored in SMTK. However, there are EntityRef-like classes, all derived from smtk::model::EntityRef, that provide easier access to model traversal. These classes are organized like so:
Each of the orange, green, purple, and red words is the name of an EntityRef subclass. The black arrows show relationships between instances of them (for which the classes at both ends provide accessors).¶
Each relationship shown in the figure above has a corresponding method in the EntityRef subclasses for accessing the related entities.
Organizing Principles¶
Model components are geometric entities; they represent geometric features that simulations use to specify the problem domain and geometric constraints on the problem due to physical phenomena, natural conditions, engineered behavior, and variations over time. Most of the entities that SMTK provides are related to boundary-representation (B-Rep) models, where a domain is specified by its boundary (and the boundary entities are specified by their boundaries, decreasing in dimension to vertices). Thus B-Rep models specify volumes via bounding faces, faces via bounding edges, and edges by vertices. Not every entity necessarily has a lower-dimensional boundary: an edge may be periodic and thus not need a vertex as its boundary, or infinite in extent (although usually, those are represented by a vertex placed “at infinity” via homogeneous coordinates). Similarly, faces and volumes may be unbounded due to periodicity or infinite extent.
The other common feature in most B-Rep models are use-records, which impart sense and orientation to geometric entities. Geometric entities such as faces are generally considered either topologically (i.e., an entity is a single thing (a set) given a name inside a collection of other things) or geometrically (i.e., an entity is a locus of points in space, and those points have features such as a measure that identifies the dimensionality of the locus). Neither of these approaches (topological or geometric) imply orientation or sense.
Orientation is a binary indicator (positive/negative, inside/outside, or forward/backward) associated with an entity. Occasionally, people may also consider it a tertiary indicator: inside/outside/on.
Similarly, the “sense” of an entity is the notion of how the entity is being employed to compose the model.
To understand how sense and orientation are distinct from one another, consider an edge in a 3-dimensional model that bounds 2 or more faces. Edges have both an orientation, which is the direction along the edge considered forward. The sense identifies which face a pair of oppositely-oriented “uses” bound. Since the same edge may bound arbitrarily-many faces in 3 dimensions, SMTK uses an integer to identify the sense.
Faces (in 3-dimensional models) always have 2 orientations and may partition volumes to either side. Because a face only has 2 sides, faces may bound at most two volume regions. This means that for faces, the sense may always be taken to be 0 without loss of generality.
Vertices are not considered to have an orientation but do have a non-trivial sense: consider the model vertex at the point where two conical surfaces meet. The vertex is used in a different sense by each cone; a different vertex-use record (if vertex uses are tracked by the modeling kernel) will be created for each volume bounded by the vertex when those volumes share no other points in the immediate neighborhood of the vertex.
Beyond geometric entities and use-records, SMTK also offers model entities for less formal models: groups, auxiliary geometry, geometric instances, models, and concepts.
Filtering and Searching¶
As with all classes that inherit smtk::resource::Resource, it is possible to ask the resource to filter its components using a string that specifies some search criteria (i.e., a filter). Model resources accept an extended syntax compared to other resource types since (1) model entities have so many types as described above and (2) in addition to these types, users and SMTK workflows often mark up these model entities with properties (covered in the Model Property System section) to provide high-level conceptual information that is useful in preparing simulations. For example, a geometric model of a motor will have many model faces that might each be marked with properties to indicate which are bearing surfaces, which are fastener or alignment surfaces, which surfaces will be in contact with coolant or fuel, etc.
In order to allow user interface components to only show relevant model entities, the model resource’s queryOperation method accepts strings in the following format:
type-specifier
[
property-type [{
property-name [=
property-value ]}
]
where
type-specifier
is any model-entity type specifier string such as face, group, model. A full list can be found insmtk/model/Entity.cxx
.property-type
is one of the following string literalsstring
,floating-point
,integer
.property-name
is either a single-quoted name or a slash-quoted regular expression (i.e., a regular expression surrounded by forward slashes such as/(foo|bar)/)
.property-value
is one of the followinga single, single-quoted string value to match (when searching for string properties),
a single, slash-quoted regular expression to match (when searching for string properties by regular expression),
a single, unquoted integer or floating point value to match (when searching for properties of those types), or
a tuple (indicated with parentheses) of values, as specified above, to match. Note that this implies the property must be vector-valued and the length must match the specified tuple’s length in order for a match to be successful.
Whitespace is allowed anywhere but is treated as significant if it is inside any quoted string value or regular expression.
Note that single quotes are used because these filter strings will appear in XML and/or JSON serializations that use double-quotes to mark the start and end of the query string. The examples below include the double-quotes around the query as a reminder.
For regular expressions, the c++11 standard library is used to search for matches; the syntax must be accepted by the std::regex constructor and std::regex_search() must return true when passed property names or values in order for the corresponding entity to be included in filtered results.
Query string |
Results |
---|---|
“ |
Any model explicitly marked as 3-dimensional. (This example has no limiting clause is here to be clear that existing query strings will continue to be accepted.) |
“ |
Vertices with any string properties at all (but not vertices without string properties). |
“ |
Any entity with an integer property named ‘counter’ (regardless of the value). |
“ |
Faces with a string-property named pedigree whose value is “zz” |
“ |
An entity of any type with any floating-point property whose value is a 3-entry vector of zeros. |
“ |
Any group with a string property named “alphabet” whose value is a vector of 2 strings: one valued “abc” and the next valued “def”. |
Query string |
Why This is Invalid |
---|---|
“ |
Multiple queries are not supported yet.
Also, it is unclear whether the limiting clause applies
to both types or just faces.
For now, use multiple filters to handle combination queries
with different limiting clauses.
Note that if this example had used |
“ |
You must currently specify the property type. |
“ |
There is no way to search for properties with partially-matched array-valued entries. |
“ |
There is no way to search for properties whose value is a given length yet. |
Sessions¶
As mentioned above, Sessions link, or back SMTK model entities to a solid-modeling kernel’s representation of those model entities. Not all of the model entities in a model manager need to be backed by the same session; SMTK can track models from multiple modeling kernels in the same model manager. However, in general you cannot perform modeling operations using entities from different sessions.
Sessions (1) transcribe modeling-kernel entities into SMTK’s storage and (2) keep a list of Operators that can be used to modify the model. As part of the transcription process, sessions track which entities have been incompletely transcribed, allowing partial, on-demand transcription. SMTK’s opencascade and discrete session types use their respective attribute resource’s modeler to hold SMTK-generated universal, unique IDs (UUIDs) for each model entity; modeling-kernel sessions may also provide a list of UUIDs in an unambiguous traversal order. This is useful if UUIDs cannot be stored in a model file but also in the event where you do not wish to modify the file by rewriting it with UUID attributes included.
When a model operation is performed, — depending on how much information the modeling kernel provides about affected model entities — entities in SMTK’s storage are partially or totally marked as dirty and retranscribed on demand.
Transcription involves mapping unique identifiers to Entity records, to Tessellation records, to Arrangement records, and to property dictionaries. A property dictionary maps a name to a vector of string, floating-point, and/or integer values of arbitrary length per model entity. While property names are arbitrary, there are some special property names used by the modeling system:
SMTK_GEOM_STYLE_PROP marks models with a ModelGeometryStyle enum indicating whether the model is discrete or parametric.
SMTK_TESS_GEN_PROP marks cells that have tessellations with an integer “generation” number indicating the age of the tessellation.
SMTK_MESH_GEN_PROP marks cells that have an analysis mesh with an integer “generation” number indicating the age of the mesh.
When a property value can be reliably determined by a session’s modeling kernel (independent of the model manager), the session should add that property name to the list reported to the model manager for erasure when a model entity is being deleted. (Other user-assigned properties are not deleted by default when an entity is erased.)
Registration and initialization of Sessions and Operators¶
Because sessions usually back SMTK model entities with representations in a solid modeling kernel, constructing a session (and thus initializing a modeling kernel) can be an expensive operation. This expense is even higher if the actual session must live in a separate process or even possibly on a remote machine.
Todo
Update how modeling sessions are exposed via plugins.
Todo
Discuss Remus and ParaView client/server sessions
Model Property System¶
In addition to associating modeling entities with attributes, SMTK’s model resource uses the resource’s properties mechanism to store string, integer, and floating-point properties on model entities. Unlike attributes that have a rigid format imposed by definitions, properties are free-form: given any model entity UUID and a property name, you may store any combination of string, integer, and floating-point values.
The intent of the model property system is two-fold:
Properties can be created by sessions to expose modeling-kernel data that cannot otherwise be expressed by SMTK. An example of this is the Exodus session, which stores the Exodus ID of each block and set it reads so that exporters can refer to them as they exist in the file.
Properties can be created by applications built on SMTK to mark up model entities. This might include user markup such as entity names or application data such as whether an entity is currently selected, or which views an entity should be displayed in.
Despite being free-form, SMTK does use the property system itself and so some property names are reserved for particular use. The list of these names is below and is followed by properties provided by existing sessions.
Reserved model properties¶
The following table lists the conventions that SMTK uses for property names. These names are used for the purpose described but the convention is not enforced programmatically.
Properties that applications will most commonly expose to users are summarized in the table below:
Property name |
Property type |
Description |
---|---|---|
color |
Float |
A 3- or 4-tuple holding RGB or RGBA color values. Clamped to [0,1]. An example would be [0., 0., 1., 1.] for blue. Color may be assigned to any entity with a visual representation. |
embedding dimension |
Integer |
The number of coordinates required to represent any point in the locus of all points contained in the entity’s underlying space. |
name |
String |
The name for the entity as presented to the user. |
url |
String |
Assigned to model entities loaded from data at the specified location. |
Properties used internally are in the following table:
Property name |
Property type |
Description |
---|---|---|
cell_counters |
Integer |
An array of 6 integers assigned to each model entity and used to generate model-unique names that are easier to read than UUIDs. See Entity::defaultNameFromCounters() to understand how the array is used. |
generate normals |
Integer |
When non-zero, this indicates that the given entity’s tessellation should have surface normals added via approximation (because the modeling kernel does not provide any and the geometry has curvature). When not defined, it is assumed to be zero. When defined, it should be a single value. Most commonly this is stored on models and is applied to all of the tessellated entities belonging to the model. However, it may be stored on individual entities as well. |
group_counters |
Integer |
An array of 3 integers assigned to each model entity and used to generate model-unique names that are easier to read than UUIDs. Three integers are used because groups with the MODEL_DOMAIN or MODEL_BOUNDARY bits set are numbered separately. See Entity::defaultNameFromCounters() to understand how the array is used. |
instance_counters |
Integer |
A single integer assigned to each model entity and used to generate model-unique instance names that are easier to read than UUIDs. See Entity::defaultNameFromCounters() to understand how the counter is used. |
invalid_counters |
Integer |
A single integer assigned to each model entity and used to number invalid child entities in a way that is easier to read than UUIDs. See Entity::defaultNameFromCounters() to understand how the counter is used. |
model_counters |
Integer |
A single integer assigned to each model entity and used to generate a unique name for each submodel of the given model. See Entity::defaultNameFromCounters() to understand how the counter is used. |
session pedigree |
String or Integer |
A session-specific persistent identifier assigned to the associated entity for use by the exporter and other tasks that need to refer to the entity when it is not possible to use UUIDs created by SMTK to do so. This happens when the original model file may not be modified and simulation input decks must refer to entities in that original file. Sessions should provide 0 or 1 values for each entity. |
shell_counters |
Integer |
An array of 5 integers assigned to each model entity and used to generate model-unique names that are easier to read than UUIDs. See Entity::defaultNameFromCounters() to understand how the array is used. |
use_counters |
Integer |
An array of 6 integers assigned to each model entity and used to generate model-unique names that are easier to read than UUIDs. See Entity::defaultNameFromCounters() to understand how the array is used. |
_tessgen |
Integer |
An integer used as a generation number for tessellation and bounding box data; it is incremented each time the entity geometry changes. |
_meshgen |
Integer |
An integer used as a generation number for mesh data; it is incremented each time a mesh is modified (e.g., by displacing it, not by remeshing). |
_geomstyle |
Integer |
Indicates whether the modeling kernel represents geometry discretely (smtk::model::DISCRETE) or parametrically (smtk::model::PARAMETRIC). |
_boundingBox |
Float |
The world-coordinate, axis-aligned bounds of an entity, reported as a 6-vector: [xmin, xmax, ymin, ymax, zmin, zmax]. |
Model properties of sessions¶
In general, sessions should choose a prefix for their property names so that developers can easily identify the source of the property, even when saved in a JSON model file. The exception to this rule is properties that should be universal across sessions, such as pedigree ID.
Properties specific to the Exodus session are listed in the table below.
Property name |
Property type |
Description |
---|---|---|
cmb id |
Integer |
A pedigree ID assigned to model entities in discrete sessions. |
exodus id |
Integer |
The block ID or set ID as stored in the Exodus file. |
exodus type |
String |
One of “block”, “side set”, or “node set”. This indicates how the group is represented in the exodus file. The group’s dimension bits can also be used to determine this information by comparing them to the parent model’s parametric dimension. |
Operators¶
In SMTK, operations performed on geometric or conceptual models are represented by instances of the Operator class. Each operation, such as creating an edge, is a subclass that contains the code to perform the operation by overriding the operate() and ableToOperate() methods. These subclasses register themselves (via SMTK’s auto-init macros) in a way that allows a list of applicable subclasses to be enumerated for any given type of model entity. Operators perform work on a model in situ: they modify the model in place. If you are familiar with VTK, this can be confusing, since VTK’s pipeline filters leave their input datasets untouched while SMTK’s operators do not. However, this in-place operation is a standard practice among solid modeling software.
Todo
Describe SMTK’s auto-init macros.
The next sections describe in detail: first, how operators specify the inputs they require and outputs they produce; and second, how operators register themselves for intropspection.
Inputs and Outputs¶
The inputs that an operator requires in order to run and the outputs that an operator may produce are each modeled as attribute definitions. These definitions reside in an attribute resource owned by an instance of a modeling session.
Each operator’s inputs are specified by a Definition that inherits a base attribute-definition named “operator” while the its outputs are specified by a Definition that inherits a base attribute-definition named “result”. Furthermore, if an operator’s input specification is named “foo”, its output specification must be named “result(foo)”.
SMTK uses this naming convention to construct results automatically. Given an instance of an Operator subclass, you can call its specification() method to obtain the Attribute which models its inputs. When the operator is executed, it returns an Attribute instance that models its result.
Recall that the attribute.Resource holds Definitions which contain ItemDefinitions. When a Definition is instantiated as an Attribute, all its active ItemDefinitions are instantiated as Items. The Definitions themselves specify what types of model-entities may be associated with their Attributes using a membership mask. Associations between an operator’s Attribute and model entities (i.e., subclasses of EntityRef) denote the entities that the operator will act upon. The operator-attribute’s Items specify input parameters such as point locations, geometric distances, etc.
The fact that inputs and outputs are specified using SMTK’s own attribute resource means that one need not construct an instance of the operator’s C++ class in order to obtain information about it; instead, simply call operatorSystem() on the session and ask for all the definitions which inherit “operator”.
The next sections go over conventions that SMTK uses for its inputs and outputs.
Inputs¶
There are no naming conventions for input parameters, however:
operators should use model-entity associations as the primary means for selecting geometry that an operator will act upon;
also, by using the attribute resource to hold operator specifications, simply checking whether an attribute is in a valid state is usually enough to guarantee that the operator can run successfully. This is what the default implementation of ableToOperate() does. Subclasses may override the method to provide additional checks (i.e., whether a file is writable);
when an operator needs only an associated set of model inputs, either because it has no parameters or they all take on valid default values, it can be run without further user input and applications are encouraged to provide a way to execute them immediately. However, when ableToOperate() returns false or applications wish to provide users a chance to override default parameters, some interface must be provided. The qtOperatorView class is provided for this purpose.
Outputs¶
Output attributes have several items with standard names used to tell applications what changes an operator has made to the model:
created
(ModelEntityItem) An array of model entities that were created during the operation. These items may have tessellations (meaning there are new renderable entities) or relationships to existing entities (meaning there are new items to insert in the tree view).modified
(ModelEntityItem) An array of model entities that were modified during the operation. This does not imply that an entity’s tessellation has changed; thetess_changed
entry below is used for that.expunged
(ModelEntityItem) An array of model entities that were removed entirely from the model manager during the operation.tess_changed
(ModelEntityItem) An array of model entities whose geometric tessellations changed during the operation. This is signaled separately frommodified
above to minimize the overhead in rendering when only topological changes have occurred.cleanse entities
(VoidItem) When present and enabled, this operator marks themodified
andcreated
entities as “clean” (meaning that they do not need to be saved; they are at exactly the state present in their owning-model’s URL).allow camera reset
(VoidItem) When present and enabled, this operator will allow (but not force) the camera of the active render view to be reset. A reset will actually occur when no renderable entities existed prior to the operation but at least one renderable entity exists afterward. Operators which load data from files are encouraged to include this item in their result attribute while operators which let users create or modify entities interactively — especially through interaction in render-views — are discouraged from allowing camera resets.
Registration¶
How to enumerate operators: ask the session.
Operators are registered with a particular session via the operator’s use of the smtkDeclareModelOperator and smtkImplementsModelOperator macros.
Operators registered with the base session are inherited unless the session’s constructor prevents it explicitly.
Presenting models in a user interface¶
Descriptive phrases are used to illustrate the structure of a model. Wherever a phrase has children, they are generated by a subphrase generator instance — which acts as a delegate for descriptive phrases.¶
SMTK provides several classes to help user-interface developers present hierarchical and/or list views of solid modeling entities to users. There are many different circumstances where these views are needed, so flexibility is a key requirement. Some of these use cases include:
A tree view showing modeling entities to allow selection when entities are obscured in a 3-D rendering by other geometry (e.g., a face inside a cavity) or not being illustrated (e.g., oriented shell uses are not usually explicitly rendered but may be associated with boundary conditions rather than faces in some multiphysics simulations).
A list view displaying group membership and allowing drag-and-drop modifications.
A list view showing entities ordered by their intersection points along a ray fired through a rendered view (for selection of entities as input to operations or association with simulation attributes).
A tree view illustrating relationships between entities and sizing functions and/or feature definitions used to constrain meshers.
A tree view showing relationships between model concepts or categories and model entities. For example, a simulation may be defined without reference to geometry, knowing only that a boundary condition exists wherever solid material meets ambient air. A tree view can be used to assign geometric regions to materials (solid, air) and the tree view would allow selections that render where the boundary condition would be applied with those assignments.
In some instances (use cases that present list views), each entity should be displayed without any children.
In other instances it may be desirable for entities in a tree view to show an abbreviated hierarchy rather than the full hierarchy; it is rare for users to interact directly with oriented use records (edge- or face-uses) or shells or loops. Rather than having cells present use-records as children and force users to open, for example, a face’s face-use and then the face-use’s loop and then one of the loop’s edge-uses to obtain an edge bounding the face, the face should present its bounding edges as direct children.
Finally, there are circumstances when the full solid-model hierarchy should be presented.
Instead of writing a separate model or widget for each use case, SMTK provides a framework that can express any of these cases. The framework consists of two base classes plus some subclasses of each. The base classes are not tied to any particular user interface library so that they can be used in both desktop and web applications. An adaptor to the Qt model-view-controller system is provided, named QEntityItemModel.
The first base class is DescriptivePhrase. Instances of DescriptivePhrase or its subclasses are arranged in a tree. Each phrase has at most one parent and zero or more children. List views are expressed as one parent phrase with no grandchildren. A descriptive phrase may be asked for a solid modeling entity related to it, but the returned entity may be invalid (such as the case when a phrase is used to group other phrases). Descriptive phrases also
must provide a type which user interfaces may use to alter presentation style,
must provide a textual title and may also provide a subtitle (as is commonly used in iOS table views),
may provide an icon and/or a color,
may mark elements such as the title, subtitle, or color as mutable (indicating that the user may change it).
The SubphraseGenerator class is the second base class in the user-interface presentation system. Descriptive phrases store a list of children but they are not responsible for computing that list, nor is the list computed except on demand. Instead, each phrase (or its parent, if it has one) stores a reference to an instance of a SubphraseGenerator that will compute children when they are requested from a descriptive phrase.
The SubphraseGenerator is separate from the DescriptivePhrase class so that different user-interface components can use the same set of phrases but arrange them in different ways. For example, a widget for selecting a model might subclass the subphrase generator to only enumerate sub-model and sub-group children of a DescriptivePhrase; while a model-detail widget might include volumes, faces, edges, and vertices as children. Another example is shown in the figure at the top of this subsection.
Here, additional summary phrases are inserted in the lower figure to reduce clutter when a phrase has many children. The subphrase generator is completely programmable, so multiple summaries could be added to distinguish children of different types.
Session: Discrete¶
SMTK has a session type named discrete that sessions a VTK-based discrete modeling kernel. This kernel provides access to 2-D and 3-D polygonal and polyhedral models, with operators that allow model edges and faces to be split or merged.
Models need not include a full topology (e.g., only volumes and faces may be represented, with edges implied; or geometric entities may be modeled but not all of their oriented use-records). However, several operations such as “create edges” exist to generate a full topology from a self-consistent but incomplete model.
Session: Exodus¶
SMTK has a session type named exodus that is not technically a modeling kernel but does allow you to mark up meshes saved in the Exodus-II file format for use in an existing simulation — assuming that element blocks and side sets that segregate the model into one or more regions per material and one or more groups per boundary-condition are already present in the exodus mesh.
Todo
Describe what blocks and sets are
Todo
Describe how blocks and sets are mapped to models and groups
Todo
Show simple example?
Session: Polygon¶
SMTK has a session type named polygon that sessions Boost.polygon’s modeling kernel. This kernel provides access to 2-D polygonal models, with operators that allow model edges and faces to be split or merged as well as boolean operations and Voronoi diagram computation.
Boost’s polygonal modeler uses integer arithmetic to achieve high performance with robust geometric predicates. SMTK converts 3-D floating-point coordinates into 2-D integer coordinates for you, but you must provide several pieces of information for each model instance:
A base point for the plane holding the polygon
Either x- and y-axis vectors (in 3-D world coordinates) describing the planar coordinate system you wish to use, or an x-axis and the plane’s normal vector.
Either a minimum feature size (in world coordinates) that your model should represent or an integer model scaling factor that each world coordinate is multiplied by before rounding to an integer.
This session does not allow model edges to have more than 2 model vertices. A model edge may have zero or one vertices when the edge is a periodic loop denoted with identical first and last points; in this case, the first and last point must also be the model vertex. A model edge must have two model vertices when it is not periodic, one at each end of the edge. Model edges may have any number of interior points that are not model vertices. These restrictions are imposed so that it is possible to quickly determine what model face lies adjacent to each model vertex. If model edges could have interior vertices, the assumption that each edge may border at most 2 faces would be much more difficult to enforce and validate.
This decision regarding model vertices and edges has further implications. Edges may not have any self-intersections other than at points where segments meet. When edges are joined into loops to form a face, they are intersected with one another first; if any intersections are found, then the model edges are split when the face is created.
Note that SMTK is slightly more restrictive (in that it splits edges and creates model vertices) than Boost requires because Boost does not model edges at all; instead it models polygons as sequences of points – optionally with a list of holes which are themselves polygons. In Boost’s modeler, points are not shared between faces; each face is a model in its own right. Because of this, it is simple for Boost to use keyholed edges to represent holes in faces. Keyholed edges are edges coincident along a portion of their length and effectively split a face with holes into a face with no holes but with infinitesimal slivers connecting the region outside the face to each hole. SMTK can accept keyholed edges but they must be broken into multiple model edges at intersections so that SMTK’s assumption that planar edges border at most 2 different surface regions.
Meshing Boost.polygon models¶
Boost polygonal models are conforming piecewise-linear cell complexes (PLCs), and may thus be meshed by any SMTK mesh worker that accepts models in this form.
PPG File Format¶
The SMTK polygon session includes an ImportPPG
operation for creating 2-D
models from text file input. The ImportPPG
operation is provided as a
convenience for exploring CMB’s many capabilities as well as for testing,
debug, and demonstration.
The “ppg” (Planar PolyGon) file format is a simple data format
that specifies 2-D geometry as a list of vertex coordinates and
polygon face definitions, with vertices connected implicitly by
straight-line model edges.
# example1.ppg
# Polygon domain with embedded face and hole
# Note that vertex indices start with 1
# Vertices 1-8 for the outer polygon
v 0.0 2.0
v 1.0 0.0
v 9.0 0.0
v 9.0 2.0
v 8.0 4.0
v 6.0 5.0
v 3.0 5.0
v 1.0 4.0
# Vertices 9-12 for the embedded parallelogram
v 2.0 1.5
v 4.0 1.5
v 4.5 3.0
v 2.5 3.0
# Vertices 12-16 for the square hole
v 7.0 1.0
v 8.0 1.0
v 8.0 2.0
v 7.0 2.0
# Faces
f 1 2 3 4 5 6 7 8 # face 1
e 9 10 11 12 # face 2 (embedded)
h 13 14 15 16 # hole
This example produces a model with two faces, a parallelogram-shaped face embedded in a polygon face that also contains a square hole.

PPG Features¶
As a tool intended mainly for educational use, the supported feature set is purposely limited.
Each polygon face must have simple geomety, specifically, polygons cannot be self-intersecting, and polygons with more than four edges must be convex.
Polygons may share edges but cannot otherwise intersect or overlap.
Polygons may include inner edge loops as either holes or embedded faces. Inner edge loops may not share edges or vertices with other loops, and embedded faces may not contain inner edge loops themselves.
The ImportPPG operation has only syntactic checking, so users are encouraged to check their input files carefully for geometric validity.
File Format¶
A PPG file may contain comments, vertex data, and face elements. Empty lines are ignored.
Comments
Anything following a hash character (#) is a comment.
Vertices
A model vertex is specified via a line starting with the letter
v
followed by the x and y coordinates.
Face Elements
Model faces are specified using a list of vertex indices. Vertex indices start with 1. Each face is defined by three or more vertices.
An model face is specified with the letter
f
followed by an ordered list of vertex indices. ImportPPG will create a model edge between each pair of adjacent vertices in the list, and between the last and first vertices, to form a polygon.Inner edge loops can be specified in the lines immediately following the model face.
Embedded faces are specified with the letter
e
followed by an ordered list of vertices.Holes in the model face are specified with the letter
h
followed by an ordered list of vertices.
Shared Edges
As noted above, model faces can be adjacent with a common internal edge between them. Note that the vertices at either end of the common edge must be included in the vertex list of each face.
# example2.ppg
# Two adjacent faces with common edge
v 0 0
v 3 0
v 3 1
v 1 1
v 0 1
v 3 2
v 1 2
f 1 2 3 4 5 # face 1
f 3 6 7 4 # face 2

SMTK’s Graph-based Model Resource¶
An alternative to SMTK’s geometric model resource is the graph-based model resource. Unlike the geometric model resource, which has a set of fixed component types, the graph-based resource has a user-defined set of components (nodes) and relationships (arcs) between those components.
Key Concepts¶
Where the geometric model resource has a fixed set of component types, the graph-based model is intended to hold application-specific components. Furthermore, it uses modern C++ template metaprogramming techniques to offer type safety.
- Resource
instances contain graph nodes and arcs (which are traditionally called graph edges — but we wish to avoid confusion with geometric edges). All of the nodes you create are owned by the resource, along with the arcs between them. By default, nodes are held in a NodeSet whose underlying storage is a
std::set<std::shared_ptr<smtk::graph::Component>>
with a comparator that orders components by UUID. However alternate types of storage are supported and the programming interface is geared to make indexing nodes by their UUID, type, and name possible.The resource class itself requires a single template parameter that is a type-traits object which contains
a
NodeTypes
type-alias, expected to be astd::tuple
of subclasses of the graph component class described below;an
ArcTypes
type-alias, expected to be astd::tuple
of arcs allowed to connect nodes to form a graph; andan optional
NodeContainer
type-alias which the resource will inherit as the container for shared-pointers to nodes (instead of NodeSet) if it is present.
Thus the resource class is not a concrete implementation but rather a base class for other resources.
- Component
is a subclass of the geometry subsystem’s Component; instances of graph components serve as nodes in the resource’s graph. This subclass exists to extend the base API with methods for accessing graph nodes related by registered arc types. As above, you are expected to subclass this class with node types specific to your application.
- Arc
is any struct or class holding type-traits and methods that specify how arcs of the given type behave (i.e., what types of nodes they may connect) and, optionally, are stored. An arc class may present either explicit or implicit arcs.
Explicit arcs are those which are explicitly stored by SMTK as pairs of connected node IDs.
Implicit arcs are those which are implied by the nature of data being represented (e.g., in a structured grid, points are implicitly connected to neighors; also, every node in one layer of a neural network is connected to every node in the next layer – given a pair of nodes in the neural network it is easy to decide whether they are connected by looking at their layer assignment) or by a third-party library (e.g., a CAD modeling kernel already stores relationships between edges and faces; rather than duplicate and maintain this relationship in SMTK, we forward requests about connectivity to the CAD library).
Resources that include an arc class in their list of arc types decorate the type-traits class you pass with templates that completely implement a convenient and consistent interface from the description and any partial implementation your arc class provides.
The decorated version of your arc class is then instantiated and stored as an entry in the resource’s ArcMap. The arc map, held by a resource instance, is where explicit arc storage lives (if any).
Developers who wish to construct their own implementation of a graph resource should start by going through smtk/graph/testing/cxx/TestArcs.cxx to understand the front-facing API before going through the implementation.
Todo
Create runtime-configurable classes (ProgrammableResource, ProgrammableNode, ProgrammableArc) that can be Python-wrapped and configured via a JSON serialization.
Nodes and Arcs¶
The graph session has been designed to accommodate a broad range of use cases, including situations where the graph’s arc data exist in an external modeling kernel and are “discovered” as they are accessed. This level of flexibility is achieved by using generic programming principles to describe the graph’s node and arc types and API, while recovering compile-time type safety using the graph resource’s traits class that lists its available nodes and arcs. The relationship between nodes and arcs is modeled using a generic accessor pattern, where the nodes are the first-class elements of the graph and the arcs augment the nodes’ API to access neighboring nodes.
To declare the node and arc types a resource accepts,
define a graph-traits class with type-aliases named NodeTypes
and ArcTypes
that are std::tuple
objects listing
types for nodes (which must inherit smtk::graph::Component)
and arcs (which must provide type-aliases described below),
respectively.
1class ExampleTraits
2{
3public:
4 using NodeTypes = std::tuple<Thingy, Comment>;
5 using ArcTypes = std::tuple<InvertibleImplicitArc, ImplicitArc, ExplicitArc>;
6};
Although not present in the example above,
you may also provide a type-alias named NodeContainer
that names the type of container the resource should use to hold
references to its nodes.
If present, the NodeContainer
class will be publicly inherited
by your resource, making its storage available as you see fit.
By default, smtk::graph::NodeSet is used, which is simply a
set of shared-pointers to components.
You may wish to consider the boost multi-index container as an
alternative, since it may be indexed by node type (a common query).
In that case, see the NodeSet API required for custom storage.
The type-traits class, such as the ExampleTraits
class above, is
used by several templated classes to compose implementations of
methods used to access nodes and arcs in resources and components
as shown below. We will cover these in more detail in the following
sections.
How graph-traits type-aliases are processed with templates into actual node and arc implementations. In most cases, you will not need to implement storage for arcs.¶
Nodes¶
As the first-class element of the graph structure, Component (and types that derive from it) have access
to the parent resource and node id. The remainder of its API
(outgoing()
, incoming()
, set()
, and get()
) exists to
provide access to other nodes related by arc types as described below.
Graph components (nodes) do not store arcs themselves – they expose arcs from their parent resource. This separation of API implementation from the node to the arc facilitates the traversal of arcs or sets of arcs with different storage layouts without modifying the node class description.
Note that, unlike arcs – whose storage is segregated by type – nodes
must be globally indexed (by UUID at a minimum). Otherwise we would
use the same storage pattern that arcs use.
You can specify a type-alias, named Serializable
on a node type
indicating you would like it to be serialized when saving the resource.
Otherwise, nodes are not serialized to file.
Arcs¶
As mentioned above, any class (or struct; we’ll use class here for simplicity) may describe a type of arc; inheriting a common base class is not required. Instead, arc classes are used as type-traits objects that specify how arcs behave via type-aliases and class-static methods. Note that each type of arc is responsible for storing all arcs of that type for the entire resource. This table summarizes the type-aliases available (some of which are mandatory):
Type alias |
☐/☑ |
Description |
---|---|---|
|
☑ |
The type of node serving as the source (origin) of the arc. |
|
☑ |
The type of node serving as the destination (end) of the arc. |
|
☑ |
True ( |
|
☐ |
If present and true ( When this type-alias is absent, bidirectional indexing is
presumed. In that case, you must provide an This tag is unsupported and will cause an assertion at compile time if the arc type is undirected. |
|
☐ |
If present and true, this mark forces the arc editing methods
on the smtk::graph::ArcImplementation to have no effect
(and always return false), even if the traits class provides
|
|
☐ |
If present, this must be true ( This is used, for example, in CAD boundary-representation models
to order the edges bounding a face loop.
Assuming the arc’s FromType is a If present on explicit arcs, additional indexes are created in the arc container to preserve these orderings. |
Arcs may be explicit (meaning the resource holds pairs of node UUIDs representing each arc as part of its storage) or implicit (meaning that arcs are generated procedurally). An example use of implicit arcs is the OpenCascade session whose nodes form the boundary-representation (B-Rep) of a CAD model. The arcs are not stored by SMTK but instead are obtained by calling methods on the OpenCascade modeling kernel.
Explicit arcs are the simplest to declare because SMTK provides a default implementation for you (using smtk::graph::ExplicitArcs). For example:
1class ExplicitArc
2{
3public:
4 using FromType = Thingy;
5 using ToType = Comment;
6 using Directed = std::false_type;
7 // using ForwardIndexOnly = std::false_type; // false is the assumed default.
8 // MaxOutDegree is implicitly smtk::graph::unconstrained().
9 static constexpr std::size_t MaxInDegree = 1; // Comments may apply to at most one component.
10};
is all that’s required.
SMTK will insert an instance of ExplicitArcs into the resource’s ArcMap for you;
when users create or remove ExplicitArc
arcs between nodes,
an instance of ExplicitArcs<ExplicitArc>
in the resource’s arc map is edited.
(NB: The ExplicitArcs<ExplicitArc>
instance is not stored directly; we will
discuss that further in Arc-trait decoration).
Beyond the type aliases above, several class-static member variables and methods may also be present in the arc-traits class if you wish to avoid explicitly storing arcs in the resource.
Member/method |
Description |
---|---|
|
The maximum number of arcs of this type that any |
|
The maximum number of arcs of this type that any |
|
The minimum number of arcs of this type that any |
|
The minimum number of arcs of this type that any |
|
Whether one arc endpoint should prevent the other endpoint from removal. A value of None (the default) means neither endpoint owns the other. The Delete operation uses this to prevent deletions of nodes being “possesively referenced” by other nodes. |
|
A method (usually templated on If this method is not provided, the arc is assumed to be explicit and an implementation will be provided for you. |
|
A method (usually templated on This method is only required on implicit arcs, and only if the ForwardIndexOnly trait is absent or false. |
|
A method (usually templated on This method is optional and recommended only for implicit arcs. If you do not provide an implementation, then ArcImplementation will generate a slow version which traverses every node in the resource to identify those that may have outgoing arcs. |
|
A method (usually templated on This method is optional and recommended only for implicit arcs. If you do not provide an implementation, then ArcImplementation will generate a slow version which traverses every node in the resource to identify those that may have incoming arcs. |
|
A method that returns true if an arc of this type exists between the given from and to nodes. For undirected arcs whose FromType and ToType are identical, this method must return the same value given either (from, to) or (to, from). If this method is not provided, an implementation that uses outVisitor will be provided (but this is to be avoided if possible since it will not be efficient). |
|
Insert an arc given its endpoint nodes and, optionally, a second pair of nodes specifying the the insertion location of each endpoint. The optional second set of (pre-existing) nodes is only used when the Ordered type-alias exists on this arc. If this method is not present on an implicit arc, insertion is impossible (i.e., the arcs are read-only). In this case, the decorated arc object will include an implementation of this method that always returns false so that users do not need to test for its existence. |
|
Remove an arc given its endpoint nodes. If this method is not present on an implicit arc, removal is impossible. In this case, the decorated arc object will include an implementation of this method that always returns false so that users do not need to test for its existence. |
A simple example of an arc out-node visitor from the TestArcs
example is here:
1 template<typename Functor>
2 smtk::common::Visited outVisitor(const Thingy* component, Functor ff) const
3 {
4 auto resource = component ? component->resource() : smtk::resource::Resource::Ptr();
5 if (!component || !resource)
6 {
7 return smtk::common::Visited::Empty;
8 }
9 smtk::common::VisitorFunctor<Functor> visitor(ff);
10 // Implicitly generate fully connected graph (except for self-connections
11 // which are impossible given the FromType and ToType):
12 std::string filter = "'" + std::string(ToType::type_name) + "'";
13 auto others = resource->filterAs<std::set<std::shared_ptr<ToType>>>(filter);
14 std::cout << "Filter <" << filter << "> produced " << others.size() << " hits\n";
15 for (const auto& other : others)
16 {
17 ++s_outVisitorCount;
18 if (other && visitor(other.get()) == smtk::common::Visit::Halt)
19 {
20 return smtk::common::Visited::Some;
21 }
22 }
23 return smtk::common::Visited::All;
24 }
Note that the example above is contrived for the purpose of testing.
If only the outVisitor
method is provided, then the resulting implicit
arc will only be traversible in the forward direction (i.e., given
a FromType
node, visit its ToType
nodes) and will be
immutable – meaning that no arcs may be added or removed by users.
(Generally, this means that the arcs are managed by a third party library
that only allows arcs to be added or removed in patterns consistent with
their modeling intent.)
Arc-trait properties¶
SMTK provides an ArcProperties template that can be used to examine traits objects. It is used internally to decide how to decorate each arc’s traits into a class that provides a complete implementation (described in the next section) of all the accessor and editor methods needed.
You can and should use this class when writing logic to analyze arcs. It is important to understand that there are generally two different types you might pass as template parameters to the arc properties class:
the user-provided arc traits class (in which case the returned properties describe what the user has passed to the ArcImplementation); or
the computed arc-storage object (i.e.,
detail::SelectArcContainer<ArcTraits,ArcTraits>
), in which case the returned properties describe smtk::graph::ExplicitArcs templated on the user-provided arc traits class.
Generally, you will want to pass the user-provided traits but there are times you may wish to identify methods implemented by the arc-storage object.
Arc-trait decoration¶
Since arc classes are not required to implement all of the methods described above, SMTK provides the ArcImplementation class templated on your traits class.
The arc implementation template is responsible for
providing a complete and consistent set of methods for examining and editing arcs of one type;
holding an instance of some object (either your
ArcTraits
class or ExplicitArcs<ArcTraits>) that stores or fetches arcs on demand; andproviding interface objects (see Arc endpoints) for graph components to expose.
The storage that arc implementations use is selected by the SelectArcContainer template. This template selects either the arc traits-class itself for storage (in the case of implicit arcs where the traits-class implements methods as needed) or one of SMTK’s explicit arc-storage classes.
Arc endpoints¶
You may have noticed that the arc-traits objects have an API that is functional but not necessarily easy to use. The arc implementation object also provides “endpoint interface” objects (ArcEndpointInterface) that behave as containers for all the nodes attached at one endpoint by arcs of the given type.
The endpoint interface classes hold a reference to a single component and provide access to nodes at the other end of arcs of the given type. When the arc is mutable and the component is not const-referenced, then methods are available to connect and disconnect nodes to the subject endpoint.
Arc direction and indexing¶
SMTK’s implementation of an abstract graph involves adapting the mathematical description of a graph to an implementation in the C++ language. In order for the implementation to be consistent and efficient, there are some places where the theoretical and practical implementations differ.
Graph theory is not typically concerned with the computational complexity of queries, but our implementation must be. Indexing nodes in a large graph consumes memory, so SMTK allows arcs to be stored without data structures to traverse arcs in their “reverse” direction (i.e., from
ToType
nodes toFromType
nodes). This indexing is independent of whether the arcs are considered directed or not.When a graph is undirected and the nodes at each endpoint are of the same type (i.e.,
std::is_same<FromType, ToType>
is true), then visiting the incoming arcs is identical to visiting the outgoing arcs. If you visit both the outgoing and incoming arcs incident to a node, you will iterate over all the (undirected) arcs twice.In this case, it is also important to note that constraints on the in-degree and out-degree must be identical –
MaxOutDegree
must be equal toMaxInDegree
– since the same arc between nodes A and B may be stored as (A, B) or (B, A).When a graph is undirected but requires nodes of different types at each endpoint, traversing the graph will require you to call both the incoming and outgoing arc visitors. This is because the C++ callbacks for visiting nodes connected by arcs of these types must be passed a pointer to the proper node type.
Filtering and Searching¶
It is possible to ask the resource to identify some nodes using a string that specifies search criteria (i.e., a filter). The default implementation provided by smtk::graph::Resource::queryOperation allows users to filter nodes based on their component type-name as well as the existence and values of properties attached to them. For example, a geometric model of a motor will have many model faces that might each be marked with properties to indicate which are bearing surfaces, which are fastener or alignment surfaces, which surfaces will be in contact with coolant or fuel, etc.
In order to allow user interface components to only show relevant model entities, the resource’s queryOperation method accepts strings in the following format:
node-typename
[
property-type [{
property-name [=
property-value ]}
]
where
node-typename
is the name returned from the node’s typeName() method.property-type
is one of the following string literalsstring
,floating-point
,integer
.property-name
is either a single-quoted name or a slash-quoted regular expression (i.e., a regular expression surrounded by forward slashes such as/(foo|bar)/)
.property-value
is one of the followinga single, single-quoted string value to match (when searching for string properties),
a single, slash-quoted regular expression to match (when searching for string properties by regular expression),
a single, unquoted integer or floating point value to match (when searching for properties of those types), or
a tuple (indicated with parentheses) of values, as specified above, to match. Note that this implies the property must be vector-valued and the length must match the specified tuple’s length in order for a match to be successful.
Whitespace is allowed anywhere but is treated as significant if it is inside any quoted string value or regular expression.
Note that single quotes are used because these filter strings will appear in XML and/or JSON serializations that use double-quotes to mark the start and end of the query string. The examples below include the double-quotes around the query as a reminder.
For regular expressions, the c++11 standard library is used to search for matches; the syntax must be accepted by the std::regex constructor and std::regex_search() must return true when passed property names or values in order for the corresponding entity to be included in filtered results.
See the documentation for smtk::resource::Resource Filtering and Searching for examples of filtering on property types and values.
Serialization¶
The graph resource provides facilities to serialize itself to/from JSON. Note that different subclasses of the graph resource have different serialization needs.
Some graph-resource subclasses (such as the OpenCascade session) use a third-party modeling kernel (i.e., OpenCascade) to construct nodes upon load; thus serialization should only include properties, links, and an array of preserved UUIDs (in the order OpenCascade will iterate over them as it loads an OpenCascade file). Similarly, arcs are not stored explicitly in the system but are programmatically fetched by calling third-party APIs. Thus, no data should be serialized for arcs.
On the other hand, some graph-resource subclasses should store node and/or arc data. An example of this is a markup resource, where users create nodes and arcs explicitly and there is no third-party modeling kernel that stores this data. Therefore, markup resources need to serialize both arcs and nodes.
What SMTK provides¶
The graph-resource subsystem provides the to_json()
and from_json()
free functions
that the nlohman::json package expects for the base resource class;
however, you do not need to include or use them in implementing your own storage.
You must implement these free functions for your resource’s node types.
Beyond this, the following rules apply:
Nodes are only serialized if they explicitly marked with a type-alias named
Serialize
whose value evaluates to true (e.g., is aliased tostd::true_type
).Implicit arc types are not serialized (this may change in the future to allow developers to specify which implicit arcs should be serialized).
Explicit arc types are only serialized if they are mutable (because there would be no way to deserialize the arcs if not). See the
Immutable
arc trait documented above for more information.
The order of operations¶
The following outline illustrates the order in which deserialization occurs.
This order is important to understand as nodes must exist before explicit arcs
that connect them may be added.
Your read operation (let’s call it your::session::Read
) should do the
following inside its operateInternal()
implementation:
Your read operation creates a resource like so:
resource = your::session::Resource<Traits>::create();
You must inform the smtk::resource::json::Helper that a new resource is being deserialized in the current thread, like so
smtk::resource::json::Helper::pushInstance(resource);
Then, you can import JSON data into the resource you created:
your::sesssion::Resource<Traits>::Ptr resource = jsonData;
The JSON library will eventually invoke your custom converter:
some::from_json(const json& j, some::Resource<Traits>& resource);
Inside your converter, you should assign the resource by fetching the value from the helper.
Load any third-party modeling-kernel data at this point.
Create any nodes that you do not expect to have been explicitly serialized by SMTK.
Explicitly invoke SMTK’s graph-resource JSON conversion function like so:
smtk::graph::from_json(j, resource);
SMTK will explicitly invoke the base resource JSON coverter like so:
smtk::resource::from_json(j, resource);
This method will assign the resource’s UUID, location, link, and property data.
Next, graph-resource nodes that have been explicitly serialized will be deserialized.
Finally, all arcs that have been explicitly serialized will be deserialized. At this point, all nodes that the arcs connect must exist.
You must inform the smtk::resource::json::Helper that your resource deserialization is complete, like so:
smtk::resource::json::Helper::popInstance();
Example¶
SMTK’s test (TestArcs) provides a sample implementation to test serialization.
First, note that the base node type (Thingy
) declares that it should be serialized:
1class Thingy : public smtk::graph::Component
2{
3public:
4 smtkTypeMacro(Thingy);
5 smtkSuperclassMacro(smtk::graph::Component);
6
7 using Serialize = std::true_type; // Mark this node for JSON serialization.
Because the only other node type (Comment
) inherits the node type above and
does not override the Serialize
type-alias, it will also be serialized.
Since node types may contain application-specific data, you are responsible
for providing free functions that serialize and deserialize your node types.
In the example, both node types inherit the same base class so this single function is all that’s required for serialization to JSON:
1void to_json(json& j, const Thingy* thingy)
2{
3 j["id"] = thingy->id();
4 if (!thingy->name().empty())
5 {
6 j["name"] = thingy->name();
7 }
8}
Note that things are different for deserialization; the free function is responsible for explicitly creating a shared pointer to a node of the proper type. Thus you must either provide an implementation for each class or a template that fixes the node type at compile time. The example uses the latter approach:
1template<typename NodeType>
2void from_json(const json& j, std::shared_ptr<NodeType>& node)
3{
4 auto& helper = smtk::resource::json::Helper::instance();
5 auto resource = std::dynamic_pointer_cast<smtk::graph::ResourceBase>(helper.resource());
6 if (resource)
7 {
8 // Construct a node of the proper type with its resource and UUID set.
9 // Note that you must provide a constructor that passes these arguments
10 // to the base graph-resource component class or you will have build errors.
11 node = std::make_shared<NodeType>(resource, j["id"].get<smtk::common::UUID>());
12 auto it = j.find("name");
13 if (it != j.end())
14 {
15 node->setName(it->get<std::string>());
16 }
17 // Adding the node can fail if the node's type is disallowed by the resource.
18 if (!resource->addNode(node))
19 {
20 smtkErrorMacro(smtk::io::Logger::instance(), "Could not add node.");
21 }
22 }
23}
The resource has one explicit arc type (which will be serialized since it is mutable) and two implicit arc types (which will not be serialized). SMTK automatically serializes arcs for you; no additional code is required for this.
SMTK’s Markup (Annotation) Resource¶
Building on SMTK’s Graph-based Model Resource, the markup resource provides nodes and arcs typical of an annotation workflow. Where the graph resource makes no assumptions about nodes and arcs, the markup resource provides discrete geometric modeling entities (image data, unstructured data, subsets, and sidesets), groups, and relationships to a formal ontology.
The markup resource is intended as
a reference for how to subclass the graph subsystem;
a resource type for annotating geometric models;
a base class for domain-specific annotation.
In the final case, derived resources would be expected to add new node and arc classes; to add operations, queries, and filter grammars; and to subset the existing markup operations exposed to users (via an OperationDecorator).
Key Concepts¶
The Markup Resource is focused on geometric annotations (such as selecting regions where boundary conditions apply and identifying the spatial composition of materials). In particular, it deals with discrete geometric models (as opposed to smooth parametric models like the OpenCascade resource) and annotations made on those models, which tend not to be spatial or geometric in nature except for how they reference other nodes’ geometry.
Node types¶
The nodes are arranged in an inheritance hierarchy:
- Component
is the base node type for all components in the markup resource. Several arc types (such as groups and labels) can apply to any node type, so there are methods here to provide easy access to any groups a component may be a member of or labels which apply to a component.
- SpatialData
is a base type for any component that has renderable geometry. The geometry maps from some abstract Domain into world coordinates. These nodes may also be connected to Fields, which define functions over the space occupied by the node.
- UnstructuredData
represents components that have irregularly-connected cell primitives defined by “corner” points.
- ImageData
represents components that have a regular lattice of cell primitives (called voxels).
- AnalyticShape
is a base class for components whose domain is an analytic function rather than a cell complex. Children include boxes, cones, planes, and generalized spheres.
- Field
instances represent functions defined over SpatialData nodes.
- Label
is a base class for information used to mark another node (i.e., to label it). The base class makes no assumption about the form of information used to mark up its subject node.
- Comment
is a label that holds flat text provided by the user.
- URL
is a kind of label that references data on the internet via a uniform resource locator (URL).
- OntologyIdentifier
is a label that holds a specific URL labeling its subject components as being “named individuals” in a formal ontology. These labels can be used to perform automated inference of relationships to other nodes via their labels.
- Group
is a node with arcs that denote membership. Any component can be placed into as many groups as desired. Groups may contain other groups. Deleting a group requires you to delete all of its members (i.e., the members prevent deletion of the group unless they, too, are deleted). However, ungrouping a group first removes its members before deleting the group.
- Ontology
is a node that serves as the parent to a set of ontology identifiers and has a location indicating the source of the ontology identifier URLs.
Arc types¶
Arcs connect nodes to add contextual information to them
- Boundaries to shapes
are arcs from side sets to the shapes on whose boundaries they lie. An example is the surface bounding a tetrahedral mesh.
- Fields to shapes
are arcs from functions to the shapes on which those functions are defined (i.e., their domains). An example is a label map defined on an image; an image may have many label maps (and other fields). Many fields are also defined over not just one shape but a collection of them (i.e., a simulation might compute deflection of many tissues, each modeled as its own component.
- Groups to members
are arcs from groups to their members. As discussed above, groups may contain other groups. A component can be contained in many groups. Components prevent their groups from being deleted.
- Labels to subjects
are arcs from labels to the components they annotate. Since the label class is a base class for other, more specific, forms of information used to annotate components, this type of arc does not indicate the specific nature of the annotation. Other arcs below are used in contexts that require it.
- Ontology identifiers to individuals
are arcs that indicate the target node is a “named individual” of the ontology class the identifier represents. Thus, where the “femur bone” ontology identifer is connected to nodes via this arc, those nodes are marked as instances of femur bones.
- Ontology identifiers to sub-types
are arcs from ontology identifers to other identifiers representing subclasses of the originating identifier. An example is a femur, which is a hindlimb long bone, which is a leg bone, and so forth. Each of these specializations are a sub-type.
- Ontology to ontology identifiers
are arcs from side sets to the shapes on whose boundaries they lie. An example is the surface bounding a tetrahedral mesh.
- References to primaries
are implicit arcs from any reference-based geometry (side sets or subsets) to the nodes which they reference (currently unstructured data or image data). Unlike the similar BoundariesToShapes arc type – which connect the totality of a boundary to the shape it bounds – these arcs include references to small, potentially overlapping, portions of boundaries (e.g., areas on bones serving as ligament insertion points).
- URLs to data
are arcs from URLs where data is located to the data as loaded into memory. These URLs are files on disk containing geometric data as read (not imported) along with the nodes that reference them. A single file may contain data for many components; in that case many nodes will connect to a single URL node.
- URLs to imported data
are arcs from URLs where data was located at the time it was imported to the data as loaded into memory. This information is not needed to load a markup resource but serve as provenance information marking the source of the data.
Todo
Add a section domain types, especially IdSpace for discrete geometry.
SMTK’s Mesh Resource¶
SMTK’s third major component is its meshing resource, which allows you to interact with multiple mesh generators and associate one or more meshes with each geometric model entity.
Key Concepts¶
Like the model resource, the mesh resource is composed of C++ classes, also accessible in Python, and is based on concepts from MOAB, which it uses internally. The paradigm we use facilitates an interaction with meshes and their components in aggregate, avoiding granular queries whereever possible (e.g. favoring such set-theretical actions as “intersection” and “union”, rather than iterating over individual elements).
To enforce the concept of interacting with mesh elements in aggregate, all mesh elements are represented as sets: there are no classes that represent individual mesh elements (point, cell or mesh). A user can interact with individual mesh elements via the MeshForEach interface, which iterates over single elements that may be represented as element sets containing a single element. It should be noted that element-wise access to a mesh is usually not the correct approach for most algorithms.
The class instances, listed hierarchically from the most granular to the most aggregate, are as follows:
- Handle
instances refer to a single smtk::mesh entity, which may be a single primitive mesh element (a point or a cell, e.g., a triangle or quadrilateral), a set of points (an PointSet), a set of cells (an CellSet), or a set of mesh elements (an MeshSet) grouped together for some purpose.
- HandleRange
instances refer to a range of mesh entities. A range is a run-length encoded list of mesh handles (which are opaque but internally represented by integers). By making HandleRanges a primitive type, meshes with implicit connectivity can be compactly represented.
- PointSet
instances group HandleRange instances referring to points together so they can be marked with properties such as a common association with a model entity, material, boundary condition, or initial condition. As a set, each PointSet supports set-theoretic actions (intersect, difference and union). Each PointSet instance holds a reference to its parent resource (described below) and provides methods for setting attributes, computing subsets, and accessing individual points.
- CellSet
instances group HandleRange instances referring to cells together so they can be marked with properties such as a common association with a model entity, material, boundary condition, or initial condition. As a set, each CellSet supports set-theoretic actions (intersect, difference and union). Each CellSet instance holds a reference to its parent resource (described below) and provides methods for setting attributes, computing subsets, and accessing individual cells.
- MeshSet
instances group HandleRange instances referring to meshes together so that they can be marked with properties such as a common association with a model entity, material, boundary condition, or initial condition. As a set, each MeshSet supports set-theoretic actions (intersect, difference and union). Each MeshSet instance holds a reference to a parent resource (described below) and provides methods for setting attributes, computing subsets, and accessing individual meshes. A MeshSet also has access to its underlying CellSet and PointSet.
In general, a MeshSet will not contain elements that overlap spatially. Instead, a meshset usually has a boundary that conforms to neighboring meshsets (or to empty space). Often, an SMTK modeling entity (corresponding to a CellEntity) will be associated with a meshset that approximates its point locus; however, not all MeshSets have an associated model entity.
- Collection
instances hold related MeshSets together. Problem domains are often the union of several instances of MeshSet in a Collection. Often, the problem domain may be decomposed in several ways, so that all of the MeshSet`s in a resource may cover the problem domain several times over. For example, a `Collection may have one MeshSet for each geometric model cell as well as a MeshSet for each material. Either of these alone would cover the entire problem domain; together, they cover it twice.
All of the cells in all of the MeshSet instances of a Collection have their connectivity defined by indices into the same set of points.
Each Collection has a parent mesh Manager.
- Manager
instances contain Collections and provide an interface to an underlying mesh package, such as MOAB, that implements methods to access the mesh.
IO¶
- WriteMesh
Writes out a given Collection, or only the elements that match a given Domain, Dirichlet or Neumann property. This is a preserving process: all information relevant to the mesh is written to file, and subsequent reads from this file should restore the mesh exactly.
- Supported formats:
MOAB (h5m, mhdf)
Exodus II (exo exoII exo2 g gen)
- ReadMesh
Load a given file in as a new Collection or part of an existing Collection. Also supports loading just elements that a given Domain, Dirichlet or Neumann property. This is a preserving process: all information relevant to the mesh should restore the mesh in the same state as when it was written.
- Supported formats:
MOAB (h5m, mhdf)
Exodus II (exo exoII exo2 g gen)
- ImportMesh
Import a given file in as a new Collection or part of an existing Collection. Imports are not preserving processes: all mesh information contained in a file is not guaranteed to be read into the mesh database via ImportMesh.
- Supported formats:
MOAB (h5m, mhdf)
Exodus II (exo exoII exo2 g gen)
VTK (vtk)
SLAC (slac)
General Mesh Viewer (gmv)
ANSYS (ans)
Gmsh (msh gmsh)
Stereolithography (stl)
- ExportMesh
Export a given Collection to file. Exports are not preserving processes: all mesh information contained in the mesh database is not guaranteed to be written to file via ExportMesh.
- Supported formats:
MOAB (h5m, mhdf)
Exodus II (exo exoII exo2 g gen)
VTK (vtk)
SLAC (slac)
General Mesh Viewer (gmv)
ANSYS (ans)
Gmsh (msh gmsh)
Stereolithography (stl)
XMS 2D/3D (2dm 3dm)
Serialization¶
- SaveJSON
Export all the Collection that have associations with the any model that is part of the passed in Resource. The exporter will save each Collection using WriteMesh with the file type MOAB and extension h5m.
The format of the created json is:
"70ec982c-9562-44bd-a7e7-bd12b84a3271": {
"formatVersion": 1,
"name": "",
"fileType": "moab",
"location": "/tmp/output.0.h5m",
"nc": 40,
"np": 28,
"cell_types": "000000100",
"domains": [],
"boundary_conditions": {
"0": {
"value": 2,
"type": "dirichlet"
},
"1": {
"value": 2,
"type": "neumann"
},
},
"modelEntityIds": ["0442f22c-26dc-4e6b-bdd8-1e77b75e5d36", "7d42284b-c7e0-4777-8836-3b77d6aed0e3", "8cdcf988-36bd-43ed-bb60-c76443907f16", "c7a90a24-f058-4d79-8b75-bb58470547bf"],
"meshes": {
"0": {
"nc": 10,
"np": 7,
"cell_types": "000000100",
"domains": [],
"boundary_conditions": {
"0": {
"value": 2,
"type": "neumann"
}
},
"modelEntityIds": ["0442f22c-26dc-4e6b-bdd8-1e77b75e5d36"]
},
"1": {
"nc": 10,
"np": 7,
"cell_types": "000000100",
"domains": [],
"boundary_conditions": { },
"modelEntityIds": ["7d42284b-c7e0-4777-8836-3b77d6aed0e3"]
},
"2": {
"nc": 10,
"np": 7,
"cell_types": "000000100",
"domains": [],
"boundary_conditions": {
"0": {
"value": 2,
"type": "dirichlet"
}
"1": {
"value": 2,
"type": "neumann"
}
},
"modelEntityIds": ["8cdcf988-36bd-43ed-bb60-c76443907f16"]
},
"3": {
"nc": 10,
"np": 7,
"cell_types": "000000100",
"domains": [],
"boundary_conditions": { },
"modelEntityIds": ["c7a90a24-f058-4d79-8b75-bb58470547bf"]
}
}
}
- LoadJSON
Imports all the Collection that are listed in the provided JSON string. Each Collection will be marked as being associated with the provided model Resource.
SMTK’s Project System¶
The project system provides a data management capability to assist end users with creating, aggregating, and organizing SMTK resources and operations used to carry out simulation tasks and workflows. The Project System simplifies the loading and saving of a workflow by treating all of the necessary resources and operations atomically. In a non-project workflow, the user is responsible for loading all of the resources required to implement the workflow, and for explicitly saving those resources and tracking them on disk.
Key Concepts¶
The project system is composed of C++ classes, also accessible in Python, whose instances perform the following functions:
- Project
instances represent an encapsulation of a set of SMTK Resources and Operations for the purpose of accomplishing a targeted set of tasks. Each project instance contains Resources and a list of Operations that are pertinent to the Project. As a descendent of Resource, the Project class also contains links, properties, and Query functionality. When a project instance is read from the file system, all of its constituent resources are loaded as an atomic operation. Each resource in a project is identified by a unique string referred to as a role, which is specified when the resource is added to a project instance. The Project class provides accessors to the project resources, operations, and a project-version string.
- Operation
is a base class for operations that require access to a project manager. Operations that inherit from this class and that are created by an operation manager that has a project manager observing it will have the project manager assigned to them upon creation. (Otherwise, the project manager must be set manually.)
- Manager
is a singleton instance with methods for registering project types, registering project operations, creating projects, and accessing project instances. Project types are registered with four arguments: a unique name (string) identifiying the project type, an optional list of resource types that can be added to the project, an optional list of SMTK operations that can be applied to the project resources, and an optional version string (that defaults to “0.0.0”). Each registered project type can either use the baseline SMTK Project class for storage or implement a subclass to include additional project data and/or behavior.
SMTK’s Task System¶
Simulations can require input from many people with different skill sets. People with backgrounds in engineering; physical modeling; geometric discretization; numerical analysis; visualization and data analysis; statistics; and project management may all be involved, each with different tasks to complete. SMTK accommodates this range of interests and tasks by providing applications with ways to prompt users – especially infrequent or inexpert users – with suggested tasks required to complete the portion of the design process.
These tasks, together with their inter-dependencies, form a a graph with tasks as nodes and dependencies as arcs.
Key Concepts¶
A task is some activity that a user must complete to accomplish a simulation pre- or post-processing objective (e.g., generating a geometric model of a simulation domain, associating attributes to model geometry, meshing a model, exporting a simulation input deck, submitting a simulation job, post-processing simulation results). Each task has state that indicates how complete it is. Tasks may reference other tasks as dependencies, which means the referenced tasks must be completed before their dependent tasks may be undertaken by the user.
The graph of tasks (with dependencies as arcs) indicates what tasks a user may work on, what tasks are incomplete, and what tasks cannot be performed because of unmet dependencies.
It is possible to have multiple, disjoint graphs of tasks. Each connected component is called a workflow or pipeline.
A task becomes active when its dependencies are met and the user chooses to focus on the task. An application can compute the set of tasks which users are allowed to focus on (make active) by cutting the graph along arcs that connect completed tasks to incomplete tasks. When a task becomes active, the application will generally change its appearance and functionality to aid the user in performing the task. The visual style adopted by the application should be guided by the style class-names (arbitrary, author-defined strings) assigned (by the task author) to the task.
Tasks are allowed to have children. When a task has children, the children form a workflow with one or more head tasks and may not have dependencies on external tasks (i.e., on tasks that are not children of the same parent). The parent task should configure its children and its internal state should be some function of the state of its children. To work on a child task, the user must first make the parent task active and may then make one of the children head-tasks active.
Note that a workflow may have multiple “head” tasks (i.e., tasks without dependencies whose dependents reference all of the head tasks).
- State
is an enumeration of the states a task may take on. Tasks are unavailable until dependencies are met; then they are incomplete until the task’s condition is met; then they are completable (but not completed) until the user marks the task as complete.
- Task
instances have dependent tasks and a flag to store whether the user has marked a given task as complete. You may observe task state transitions. You should subclass this to implement logic that determines whether your task is unavailable, incomplete, or completable; the base class is unavailable until its dependencies are met, at which time it will transition straight to completable.
- Task instance-tracker and factory
is used to create instances of registered task classes. Any plugins that provide new Task subclasses should register those classes with the factory in their registrar (see SMTK’s Plugin System).
- Active
is used to get the currently active task or switch to a different task. There can only be one active task at a time, although there may be no active task. Active tasks must be managed instances so that there is some indication before deletion that the task will be destroyed and thus should not be active. This object can be observed for changes to the active task.
- Adaptor
instances configure a dependent task when the dependency changes state. This way, information provided by the user can have an effect on the state and user-interface of subsequent tasks. You should subclass this to implement logic that determines what information should be transmitted from one task to another.
- Adaptor instance-tracker and factory
is used to create instances of registered adaptor classes. Any plugins that provide new Adaptor subclasses should register those classes with the factory in their registrar (see SMTK’s Plugin System).
- Manager
is an object applications can create to hold a task factory and the set of task instances the factory has created. It also holds the active task tracker.
- Pipelines
are tasks that form a directed acyclic graph of dependencies. There is no explicit class representing pipelines since they can be produced by visiting related (dependent) Task instances given the task(s) at the “head” of the pipeline (i.e., tasks with no dependencies).
Instead of providing an explicit representation of pipelines, SMTK provides observers for changes to the set of pipeline head tasks. The task Instances class has a
workflowObservers()
method that you may use to be informed of workflow events.
Guide to task classes¶
The following sections describe the different task subclasses that exist for use in your workflows, the use cases that motivate them, and how to configure them.
Task¶
The base Task class does not monitor anything on its own but can be used to collect dependencies. It is Completable by default. The following JSON can be used to configure it:
title
: an optional string value holding instructions to users.style
: an optional array of strings holding presentation style-class names for the task.completed
: an optional boolean value indicating whether the user has marked the task complete or not.
Example¶
{
"type": "smtk::task::Task",
"title": "Instructions to users.",
"style": [ "unique-component-colors", "fancy-menu" ],
"completed": false
}
FillOutAttributes¶
The FillOutAttributes task monitors operations for attribute resources with particular roles. When an operation creates or modifies a matching resource, the task checks whether all the attributes with matching definitions are valid. If so, the task is Completable. If not, it is Incomplete. It is Completable by default (i.e., if no matching resources or attributes exist).
This task accepts all the JSON configuration that the base Task class does, plus:
attribute-sets
: a JSON array of required attributes, organized by role. Each array entry must be a JSON object holding:role
: an optional string holding an attribute-resource role name. If omitted, any role is allowed.definitions
: a set of smtk::attribute::Definition type-names specifying which types of attributes to validate before allowing completion.auto-configure
: either true or false (the default), depending on whether resources with matching roles should automatically be added. The default is false since a task-adaptor, such as ResourceAndRole, will normally configure only those resources identified by a user as relevant in a dependent task.
Example¶
{
"type": "smtk::task::FillOutAttributes",
"title": "Assign materials and mesh sizing.",
"attribute-sets": [
{
"role": "simulation attribute",
"definitions": ["SolidMaterial", "FluidMaterial"]
},
{
"role": "meshing attribute",
"definitions": [
"GlobalSizingParameters",
"FaceSize",
"EdgeSize"
]
}
]
}
In the example above, you can see that two different attribute resources (one for the simulation and one for a mesh generator) are specified with different roles and the definitions that should be checked for resources in those roles are different.
Group¶
A task Group exists to collect similar or related child tasks together in order to organize the workflow and reduce clutter. The Group’s state and output are dependent on its children.
The Group instance is responsible for configuring its children, including creating dependencies among them; this is accomplished by accepting adaptors that link the Group to its child task and vice-versa. The Group provides adaptors with an “adaptor data” object where they can store configuration information and isolate the children from external tasks.
The Group has a “mode,” which describes how children are related to one another: when the mode is parallel, children have no dependency on one another; the parent group configures them independently. When the mode is serial, children must be completed in the order specified (i.e., each successive task is dependent on its predecessor) and each child task may configure its successor as it becomes completable.
Task groups are completable by default (i.e., when no children are configured). If children exist, the group takes its internal state as a combination of its children’s states:
irrelevant if all of its children are irrelevant;
unavailable if all of its children are unavailable;
incomplete if any of its children are incomplete;
completable if all of its relevant children are completable; and
completed when the user marks either it or all of its children completed.
As with other task classes, the group’s overall state also includes the state of its external dependencies.
The task Group class accepts all the JSON configuration that the base Task class does, plus:
mode
: eitherserial
orparallel
.children
: an ordered JSON array of child task specifications. Each child task may have an integerid
whose value may be referenced byadaptors
below.adaptors
: an array of task-adaptor specifications that inform the group task how to configure children. The reservedid
of 1 refers to the Group itself. Child tasks are numbered 2 and above.adaptor-data
: a dictionary of key-value pairs. The keys are arbitrary strings provided by adaptors and the values are serializations of configuration information to be passed to child tasks from the parent or vice-versa. This is not typically specified when authoring a workflow but is saved and loaded when saving task state.
Example¶
{
"type": "smtk::task::Group",
"title": "Perform the child tasks in order.",
"mode": "serial",
"children": [
{
"id": 2,
"type": "smtk::task::Task",
"title": "Step 1."
},
{
"id": 3,
"type": "smtk::task::Task",
"title": "Step 2."
}
],
"adaptors": [
{
"//": "How the parent configures its child."
"type": "smtk::task::adaptor::ResourceAndRole",
"from-tag": "simulation",
"from": 1,
"to": 2
},
{
"//": "How the parent configures its child."
"type": "smtk::task::adaptor::ResourceAndRole",
"from-tag": "model",
"from": 1,
"to": 3
},
{
"//": "How the serial task configures its successor."
"type": "smtk::task::adaptor::PassComponents",
"from": 2,
"to": 3
},
{
"//": "How a child task configures its parent's"
"//": "output. Be careful to avoid loops."
"type": "smtk::task::adaptor::PassComponents",
"from": 3,
"to": 1
}
]
}
GatherResources¶
The GatherResources class monitors a resource manager and is incomplete until its configured list of required resources is acceptable, at which time it transitions to completable. It is Incomplete by default unless unconfigured (in which case it is Completable). It accepts all the JSON configuration that the base Task class does, plus:
auto-configure
: either true or false (the default), depending on whether resources should be automatically pulled from the resource manager based on their roles (true) or whether a user must explicitly assign resources (false).resources
: a JSON array of required resources, organized by role. Each array entry must be a JSON object holding:role
: an optional string holding a resource role name. If omitted, any role is allowed.type
: an optional string holding a resource typename. If omitted, any resource type is allowed.min
: an optional integer specifying the number of resources with the given role and type that must be present. Only non-negative values are accepted. It defaults to 1, which makes the requirement mandatory. If set to 0, the requirement is optional.max
: an optional integer specifying the maximum number of resources with the given role and type allowed. Negative values indicate that there is no maximum. It defaults to -1. It is possible to set this to 0 to indicate that resources of a given role/type are disallowed.
Example¶
{
"type": "smtk::task::GatherResources",
"title": "Load a geometric model (or models) and a simulation template.",
"resources": [
{
"role": "model geometry",
"type": "smtk::model::Resource"
},
{
"role": "simulation attribute",
"type": "smtk::attribute::Resource",
"max": 1
}
]
}
Guide to adaptor classes¶
The following sections describe the different adaptor subclasses that exist for use in your workflows, the use cases that motivate them, and how to configure them.
Adaptor¶
The base Adaptor class is abstract and cannot be instantiated on its own but does have configuration parameters that are common to all adaptors and must be provided:
id
: this is an integer identifying the adaptor. Although currently unused, all adaptor JSON should provide a unique, non-zero value as it may be used in the future to reference adaptors during configuration.type
: this is the fully-qualified class name of the adaptor class that should be created.from
: an integer used to identify the task which should be observed and used to configure another.to
: an integer used to identify the task which should be configured when the “from” task has changed into a completable or completed state.
Example¶
{
"id": 1,
"type": "smtk::task::Adaptor",
"from": 1,
"to": 2
}
ResourceAndRole¶
The ResourceAndRole adaptor exists to pass information from GatherResources to FillOutAttributes, possibly passing this information “through” Groups that have these tasks as children. When configured, only attribute resources chosen by the user will be examined for validity.
The ResourceAndRole adaptor accepts two additional JSON configuration entries beyond the base Adaptor class:
from-tag
: this is a string used only when the “from” task is a Group. It specifies a key in the Group’s interal configuration object from which to fetch data to apply to the destination task. The default value is an empty string.to-tag
: this is a string used only when the “to” task is a Group. It specifies a key in the Group’s interal configuration object to which to write data that will be applied to the destination task. The default value is an empty string.
The Group from- and to-tag strings exist because a Group task may need to aggregate state from multiple child tasks or configure multiple child tasks separately. By providing the key, authors can control which child tasks share or do not share configuration information. If the default empty string is used, then all tasks will share the same configuration information.
Example¶
{
"id": 1,
"type": "smtk::task::adaptor::ResourceAndRole",
"from": 1, /* must be a GatherResources or Group task */
"to": 2, /* must be a FillOutAttributes or Group task */
"to-tag": "foo" /* if "to" is a Group, "foo" is a key for resource+role data. */
}
Serialization and deserialization¶
Tasks are created by passing JSON configuration data; this constitutes the majority of deserialization. However, beyond configuration, deserialization involves connecting tasks via dependencies and potentially other information – such as functors and references to SMTK’s various managers – that cannot be simply encoded into JSON.
For this reason, the task manager may be serialized and deserialized. It uses a helper to swizzle pointers to tasks into integer indices during serialization and adds a phase during deserialization that un-swizzles the integers back into pointers to the objects it created.
Serialization also requires a functor for each class that examines a task and returns a JSON object with its full serialization. See smtk::task::json::jsonTask for an example and note that subclasses of task should invoke the functor for their superclass rather than trying to reproduce its logic.
SMTK’s Simulation System¶
Once you have a model or mesh and an attribute resource describing a simulation, you are ready to create an input deck for the simulation.
One option is to have your simulation link directly to SMTK, load the saved information from SMTK’s native file formats, and use SMTK to initialize the simulation.
Often this is not feasible, and so SMTK provides stubs for writing a file in the format a simulation expects, commonly known as an input deck 1. We expect you will use python to write the input deck as it is much simpler and easier to maintain a python script than a large C++ code base to write what is usually a flat text file.
The smtk::simulation::ExportSpec class aggregates all of the information you should need to write the input deck:
an attribute resource holding simulation parameters
an attribute resource holding locations of files involved in the export
an object that provides methods for querying the analysis grid (be it a model or mesh).
Your python export script is expected to take a single argument (an instance of ExportSpec) and write whatever files are needed to run the simulation.
- 1
This term is historical, as many of the first simulations were run on computers that used punch cards for offline storage; a deck of punch cards holding simulation parameters was often kept as a way to reproduce results for a simulation run used in a report or journal article.
SMTK’s View System¶
One of the main functions of SMTK is to present attributes, models, and meshes to users so that they can edit and annotate them. The view system provides classes that aid with this task without depending on any particular user interface technology (Qt, HTML+JavaScript, VTK, etc.).
Key Concepts¶
There are 2 ways that presentation is abstracted in SMTK: views and trees of descriptive phrases.
- View
instances are containers that hold information used to configure a view. There are currently 4 types of views in SMTK: * attribute views, which allow users to inspect and edit an entire attribute resource; * operation views, which are attribute views specifically for editing operation parameters; * phrase views, which allow users to inspect and edit any resource by interacting with
a tree of one-line summary information related to a resource or component; and
custom views, which may be subclasses of any of the above but are provided by a plugin.
- PhraseModel
instances represent a tree of descriptive phrases that together summarize information about resources and/or components. Each entry in the tree is a descriptive phrase that represents a single piece of information via a title string, an optional subtitle string, and optionally a set of badges. Phrases may be queried for a related resource or component, what badges (if any) apply to the phrase, and — most importantly — a list of child phrases that provide further details.
Consider an example where we wish to present information about a model face. The face itself could be a phrase whose title is the user-provided name of the face, whose subtitle might indicate whether the face is planar or curved, that might have a badge showing the face’s color, another badge showing its visibility in a 3-d view, and whose child phrases could include details about the face such as (1) a list of edges bounding the face, (2) properties defined on the face such as user annotation, and (3) attributes associated with the face such as boundary conditions. Developers may wish all or none of this information to be editable.
Configuration information specifying how phrases should be arranged and what portions should be editable is held in a View instance, since a view will hold the top of the phrase tree.
Besides views and descriptive phrases, there are 2 important presentation tools that SMTK provides in its view system:
- Selection
instances hold a map from persistent objects to an integer value representing the “level” or “type” of selection that the object is participating in. For example, an object may be highlighted as a pointer hovers over it or selected more permanently. Different integer values indicate which (or both, if the integer is interpreted as a bit vector) of these types of selections an object belongs to.
- AvailableOperators
instances provide a list of editing operations that a user may perform. This list may vary based on the workflow as well as the current selection.
The following sections discuss portions of the view system in depth.
Views¶
As mentioned in the previous section, views may show editable attribute resources or trees of descriptive phrases. Configuration information is different for each of these types.
Attribute views¶
This view information is used to configure qtBaseView instances. Views of an attribute resource may need to display many attributes, some of which may be created upon user input (i.e., by adding a new material or boundary condition). However, there must be one view marked as the top-level view for any given attribute resource. This view may have child views that it uses to organize (1) fixed, one-per-simulation attribute instances as well as (2) attribute instances that may vary in number to fit the needs of the simulation.
Phrase views¶
These views appear in 2 types of widgets in SMTK: panels holding tree views (such as the resource panel) and widgets for selecting components or resources, where only a flat list is presented (such as the reference item widget).
Hierarchical Phrases¶
Descriptive phrase trees may be flat or deeply nested. They are flexible and may be configured to present the same resources and components in many different ways, while accommodating changes to those resources and components in response to editing operations.
In order to deal with this complexity, the duties of phrases are split across several classes:
Phrase models are responsible for determining which descriptive phrases belong at the root of the tree. They also provide utilities used to
watch for events that may change the tree and
determine how to notify user interfaces of those changes in terms of deleted, added, re-ordered, and modified phrases.
mark each descriptive phrase with optional, clickable badge icons.
Descriptive phrases are only responsible for storing child phrases and, optionally, a reference to a subphrase generator that can create or update the list of child phrases on demand. They also own a reference to another class responsible for the phrase’s content (i.e., title, subtitle, and other related information).
Subphrase generators create or update a list of child phrases when given a reference to a potential parent phrase.
Phrase content classes own a reference to a persistent object and determine what about that object should be presented (i.e., they determine the topic of the phrase). One content class may hold a model face and present the face’s name as the phrase title/topic, while another class may hold the same face but present one of the face’s properties as its topic.
Phrase Models¶
Todo
Describe the base phrase model class
There are several phrase model subclasses:
- ReferenceItemPhraseModel,
which lists components available for use in a ReferenceItem. The top-level phrases it presents are persistent objects that either are or may be added to a ReferenceItem that you provide to the phrase model during configuration. It is used by the qtReferenceItem class to present components that may be selected by a user in order to populate some item.
- ResourcePhraseModel,
which lists resources matching a given set of filters at its top level. It is used by the pqSMTKResourcePanel class to present a list of resources that have been loaded.
- ComponentPhraseModel,
which lists components matching a given set of filters at its top level.
- SelectionPhraseModel,
which lists objects held by a Selection <smtk::view::Selection> (which you provide at configuration time). By default, it will list all selected objects, regardless of the selection value. However, you may optionally specify a bit mask so that only objects whose selection values match the mask are shown.
Phrase Content Types¶
Todo
Describe the base phrase content class and its children
Subphrase Generators¶
Todo
Describe the base subphrase generator class and its children
Badges¶
Each phrase model owns a BadgeSet used to decorate phrases. User interfaces that present phrases can ask the badge set for an array of Badges that apply to a given phrase. The returned array of badges will be ordered consistently. Each badge has an SVG string to use as an icon, an optional tool tip, and an “action” method used to perform some task when users click on the badge.
SMTK’s Extensions¶
The core SMTK library has few dependencies on external projects.
However there are external projects that SMTK can either make use
of or can be of use to.
The extension
directory in SMTK provides additional libraries
that introduce dependencies on external projects but which provide
additional functionality.
Qt¶
SMTK provides Qt classes that allow users to edit attributes and inspect model and mesh resources.
Visualization Tool Kit¶
SMTK provides classes that act as VTK sources for model and mesh geometry, allowing visualization of models.
ParaView¶
SMTK provides ParaView plugins that, in concert with the VTK extensions, allow users to load models and meshes in ParaView; perform selections and filtering on model entities; run operations on models; and set attribute values by interacting with 3-D widgets. These plugins form the core of the ModelBuilder application (version 6 and later).
Anatomy of ParaView¶
In order to use SMTK within ParaView, it is important to understand some of ParaView’s design. A core premise of ParaView is that the user interface will frequently be a separate process from rendering and I/O tasks, as these must be run in a distributed-memory parallel environment. Thus, ParaView has a client process and 1 or more server processes. Even when running in serial (i.e., non-parallel) mode, ParaView makes a distinction between tasks run on the client and the server(s).
ParaView’s design has the client create and manage objects on the server. Nearly all objects on the server are subclasses of vtkObject and thus can be managed using wrappings created by VTK (similar to Python wrappers). These wrappers are called client-server wrappers and allow ParaView to serialize, deserialize, and remotely invoke methods on server-side objects.
Nearly all objects on the server have a matching instance on each server process. For example, if the client creates a file-reader and an actor to display data from the file, it will instruct each server to create a reader and an actor; and have each server attach the reader and actor — even though some of the servers’ readers may not have data for that actor to display because the data is unevenly distributed.
This pattern simplifies the work the client must do, however it also means that server-side objects should rarely (if ever)
send signals to the client or
perform blocking operations based on their local data;
the reason for this is that,
if every server process sent a signal when an action was performed, a large number of servers would quickly overwhelm the client and
since each server process holds a different portion of the data, not every server process would block at the same time which can lead to deadlocks and race conditions.
So, although there are infrequent exceptions, be aware that ParaView takes great care to initiate all actions on the client, even when it seems that the server should do so.
Integration with SMTK¶
In ParaView, there are 3 types of objects that SMTK frequently interacts with:
Subclasses of vtkObject which live on each server process and are “wrapped” by C++ code that allows remote invocation of their methods across the client-server (CS) connection each server maintains with either the rank-0 server or the client. Several SMTK classes have instances owned by a “wrapper” class that inherits from vtkObject to make this communication possible.
Subclasses of vtkSMProxy, which live on the client process. Subclasses specific to SMTK are only required when the client needs to expose methods to manage SMTK data on the server that cannot be handled by ParaView’s client-server wrapping. For example, methods that take non-primitive objects, such as pointers to SMTK classes as input, since these methods cannot be CS-wrapped.
Subclasses of pqProxy, which live on the client process and reference an instance of vtkSMKProxy (or its subclasses). Subclasses specific to SMTK exist to expose Qt signals and slots related to SMTK. If Qt is not required, then you should subclass vtkSMProxy instead.
Inside the smtk/extension/paraview
directory, the top-level of directories
hold code placed in VTK modules (thus they have a vtk.module
file).
In directories with VTK modules, some subdirectories hold ParaView plugins
(thus they have paraview.plugin
files).
VTK modules are libraries that export symbols; other libraries may depend on
them and import their symbols.
ParaView plugins may be implemented as libraries, but they do not expose
symbols and on some platforms cannot be linked to.
Therefore, whatever classes might be reused or referenced by other plugins
should be placed in a VTK module; the ParaView plugins exist only to expose
functionality from VTK modules in ParaView.
Plugins¶
Some notes about the plugins:
the server directory (which holds source for the smtkPVServerExtPlugin plugin) is for code that can be built without Qt and will reside — at least in part — on the server. Some client-side proxy classes (that do not use Qt) whose counterparts reside on the server are also included in this plugin. This is where the resource representations reside. Note that the representations display the SMTK selection, not the ParaView selection, although the two selections are generally kept in sync. Besides dealing with SMTK selections in a consistent way, the model representation uses a glyph mapper to draw instance prototype geometry at all instance placement points.
the appcomponents plugins are dependent on the server plugin for the VTK-wrapped and CS-wrapped objects that it creates proxies for on the client. Many of the components in this plugin are ParaView “behaviors.” A behavior is a QObject subclass that customizes the user interface of ParaView. In this case:
the pqSMTKBehavior creates instances of vtkSMTKWrapper objects on the server and manages them via vtkSMSMTKWrapperProxy objects on the client. Each wrapper exposes an smtk::resource::Manager, an smtk::operation::Manager, and an smtk::view::Selection to server-side VTK classes (such as the resource readers and representations).
the pqSMTKSelectionFilterBehavior adds a toolbar to ParaView allowing users to specify what types of resource components they want to select. It then installs a filter onto an SMTK selection manager to force the selection to match the specification.
Besides behaviors, other user-interface components include:
the pqSMTKResourcePanel class adds a panel to ParaView that shows the resources and components available.
the pqSMTKColorByToolBar class adds a tool bar to ParaView that allows users to choose how model entities should be colored (e.g., by their assigned color, by the assigned color of the volume they bound, by field values such as simulation results).
the pqSMTKSubtractUI class is a singleton that provides methods for removing ParaView UI components (and restoring them if needed). Your plugin may call methods on it to customize the application for a particular workflow. Note that many user interface components can only be removed after the event loop has started; if you want to disable components from within a plugin’s start up, you should use a QTimer to schedule calls to the UI subtractor once the event loop has started. Dock-widget panels in particular require approximately a ~1 second delay in order to be removed at application startup.
the ApplicationConfiguration class defines a pure virtual interface that applications implement and expose in an application-specific plugin in order to configure SMTK’s user-interface components. This is currently only used by the operation-toolbox panel but is likely to expand to other components.
some extension directories create multiple paraview plugins. This is done because the SMTK library containing the components has a well-defined purpose, but not every application using SMTK wishes to expose all of the components in the library. Therefore, several
plugin-
subdirectories may exist and each one exposes a different set of components from the library. The appcomponents directory is an example of this:The
plugin-core
directory exposes the majority of user interface components;The
plugin-legacy-operations
directory exposes a panel for debugging operations;The
plugin-operations-panel
directory exposes two production-ready panels for choosing operations and editing their parameters; andThe
plugin-panel-defaults
directory exposes an application-configuration interface class.
Lifecycle¶
Below are some examples of the way objects are managed in ParaView-based SMTK applications. These examples help describe the communication patterns in ParaView, which are constrained by the assumption that data is distributed in parallel on 1 or more server processes. One unintuitive implication of this assumption is that almost all communication begins on the client and moves to the server(s). This makes some things difficult since events on the server cannot result in client-side actions.
Plugin initialization. SMTK registers, via CMake, an autostart plugin. This plugin’s startup method creates an instance of pqSMTKBehavior which listens for ParaView servers attaching to/detaching from the client. When one attaches, it directs ParaView’s pqObjectBuilder to create an instance of vtkSMTKWrapper on the server and vtkSMSMTKWrapperProxy on the client. The plugin also registers (again via CMake macros) a pqProxy subclass named pqSMTKWrapper to be created whenever a vtkSMSMTKWrapperProxy is instantiated.
Reading an SMTK file. SMTK registers, via an XML configuration file, a VTK-based reader (vtkSMTKModelReader) for the files which outputs a multi-block dataset. ParaView’s file-open dialog then constructs instances of the reader on the server as well as proxies (pqSMTKResource) on the client. The client proxies connect to the resource manager and register the resource with the SMTK resource manager instances owned by the client and server. (At this point, the client does not own a separate instance but instead uses the server’s.)
Displaying an SMTK model. When an SMTK model resource is read (as described above), ParaView creates vtkPVDataRepresentation objects (on the servers) and a vtkSMRepresentationProxy and a pqRepresentation instance on the client for each view. Instead of creating a pqRepresentation, SMTK’s XML configuration tells ParaView to create a subclass named pqSMTKModelRepresentation. Similarly, on the server, vtkSMTKModelRepresentation instances are created instead of vtkPVDataRepresentation instances. The vtkSMTKModelRepresentation instance looks for an instance of vtkSMTKResourceManagerWrapper. If one exists, then it uses the SMTK selection owned by the resource manager when rendering.
Representations¶
In ParaView, each view that a user creates may display datasets in the pipeline via a representation. Formally, a representation is a vtkAlgorithm subclass that adapts data for rendering based on the type of view: a spreadsheet view uses a different algorithm to prepare data for display than a 3-D render-view. Besides adaptations for different view types, representations may adapt different types of input datasets. SMTK provides a representation, vtkSMTKResourceRepresentation, that adapts models, meshes, and other resources (any that provide tessllation data on their components) for display in 3-D render views.
Integrating ParaView and SMTK Selection¶
Before we go further, it is important to understand what information ParaView provides during the selection process and what events trigger the selection process.
ParaView selection semantics¶
There are 3 basic output types for selections in ParaView:
Points – these are corners of cells or discrete spatial locations where glyphs are drawn;
Cells – these are spatial continuua defined by a set of corner points (although vertex cells are included even though they are isolated rather than continuous); and
Blocks (and/or entire datasets when the data is not composite).
Generally, ParaView will only allow one of these three output types at a time. Besides the type of output, there are two approaches to selecting data that ParaView provides in 3-D views:
Hardware selection – the framebuffer is rendered but instead of saving colors, the dataset ID, block ID, and point or cell ID are saved in special framebuffer objects. These IDs are used to create a
vtkSelection
data object. Thus hardware selections are always represented as ID-based selections (rather than frustum selections, etc.).Software selection – A
vtkSelection
data object is created with a frustum using the current camera position. When representations process this type of selection, they use VTK filters to identify all the cells inside the frustum.
Different tasks that users perform in ParaView combine these two basic approaches and three output types. Many (but not all) of these tasks are initiated via the in-view toolbar shown in the figure below.

The in-view toolbar directly above the render window holds buttons used to perform selections in ParaView. Each one combines the output type, selection apprach, and user intent in a different way but the same two functions in ParaView do all of the work.¶
The 3-D view provides a context menu. If there is no pre-existing selection when the right mouse-button is clicked, ParaView selects what is underneath the mouse in order to determine the context.
Clicking on the “change center of rotation” toolbar button followed by a click in a 3-D render view initiates a selection event to determine where to place the center of rotation.
Clicking the in-view toolbar’s block-selection button (“Select block”) generates a rectangular block selection. This selects data from zero or more representations and, if those representations show composite data, particular blocks from them that overlap the given rectangle.
Clicking the in-view toolbar selection buttons (“Select cells/points on/through”) generates rectangular selection using either the hardware selector (when selecting “on” a surface) or a software selector (when selecting “through” the entire view frustum).
Clicking the in-view toolbar polygon-selection buttons (“Select cells/points with polygon”) generates a polygonal selection using the hardware selector.
Clicking the in-view toolbar interactive selection buttons (both the “Interactive select cells/points on” and “Hover cells/points” buttons) generates a pixel-sized rectangular selection underneath the mouse as it moves.
Finally, it is important to understand how ParaView’s client-server
mechanism is involved in selection.
Recall that the client is responsible for broadcasting commands based
on user input to the server(s), which the server(s) then perform.
The results from each server are combined (a.k.a. reduced) into a
single response sent to the client for display.
Selections thus originate on the client (where the user manipulates a
pointer/cursor inside a render-window) and are sent to the server
(where the GPU is used to render the selection as highlighted).
However, when hardware selection is involved, there is an additional
round trip to/from the server involved because – in order to create a
vtkSelection
holding the IDs of points, cells, or blocks being
selected – the client must ask the server(s) to render IDs to a framebuffer.
The vtkSelection
data created by the client is distributed to each
server process and attached to each selected representation as an
additional input (beyond the pipeline source’s data). It is up for each
representation to use the vtkSelection
object as it sees fit to render
the selection.
ParaView vs SMTK¶
ParaView/VTK and SMTK have different models of selection:
ParaView and VTK represent selections as a tree of selection nodes, each of which may use different criteria (such as IDs, frustum bounds, or field data thresholds) to select points or cells or blocks. The nodes of the tree are then combined using boolean set-theoretic operations. The result is a set that may contain points, cells, and blocks (but ParaView currently expects only one of these at a time). ParaView has a global, application-wide selection.
SMTK represents a selection as a map (not a set) from persistent objects (usually components) to integer values. The integer values are usually treated as bit vectors — each bit in the value is reserved for a different purpose. In modelbuilder, we currently use bit 0 for the “primary” selection and bit 1 to indicate a “hover” or “secondary” selection. SMTK allows multiple instances of smtk::view::Selection, but the ParaView plugins that SMTK provides create a single instance for use on each ParaView server in order to harmonize with ParaView’s global selection. This single instance is owned by the vtkSMTKWrapper object on each server.
Because these are very different models, some care must be taken when selections are made. What follows is a description of how selections are handled:
SMTK resource representations render their data using only SMTK selections; they ignore the ParaView/VTK
vtkSelection
inputs that are provided. This is done in order to allow the different integer values in SMTK’s selection to affect how components are rendered.When a user creates a selection in ParaView’s render window (or with ParaView’s other tools such as the Find Data dialog), the VTK selection object is translated into an SMTK selection which replaces whatever value was previously present as SMTK’s selection.
Also, user selections in ParaView are _filtered_ selections; this means that components indicated by VTK’s selection may not be directly added to SMTK’s selection but instead may suggest related components for selection. This is done so that users can click on rendered faces but select the volume contained by the faces when the workflow requires a volume. Other suggestions are possible as well (e.g., choosing an entire group when a group member is selected, choosing an associated attribute when an associated component is selected). The pqSMTKSelectionFilterBehavior class provides a toolbar for users and workflows to adjust what filtering is performed.
When SMTK widgets change the SMTK selection, ParaView views containing SMTK representations should be re-rendered so that the updated selection is presented graphically.
Selection Translation¶
Because different resource types (e.g., mesh and model) may need to
interpret VTK selection nodes differently, SMTK uses operations to
translate VTK selections into SMTK selections.
The process is initiated on ParaView’s client by
the vtkSMTKEncodeSelection class.
This class inherits a ParaView class (vtkPVEncodeSelectionForServer
)
used to translate VTK selections on the client into a set of
selection objects to be sent to each server on which some portion
of the selected data resides.
SMTK’s application-components plugin registers a vtkObjectFactory override
so that when ParaView’s selection changes and ParaView asks for a new
instance of vtkPVEncodeSelectionForServer, an instance of vtkSMTKEncodeSelection
is provided instead.
So, each time a selection is made, vtkSMTKEncodeSelection
is instantiated
and its sole virtual method is called with a vtkSelection
object.
This method invokes the parent method but also introspects the selection
object passed to it before it is modified;
each vtkSelectionNode
at the top level of the vtkSelection
object
represents a different representation involved in the user’s selection.
The encoder loops over these top-level nodes and, when it finds SMTK representations,
it loops over operations in the VTKSelectionResponderGroup of operations
that can be associated to the representation’s resource until it finds one that
succeeds – indicating it was able to parse the VTK selection and
modify the SMTK selection accordingly.
All operations in the VTKSelectionResponderGroup are expected to be subclasses of RespondToVTKSelection. These operations should always modify the selection without notifying observers (see the documentation for modifySelection for how to do this) so that a single signal can be sent to selection-observers after all operations have completed on all the affected resource selections.
The RespondToVTKSelection operation provides block-selection translation for any resource with tessellated or glyphed data. If all your application needs is graphical selection of components, you do not need to implement a custom selection responder. However, if your application wants to process selections of points or primitives, you should implement a custom selection responder for your resource. Typically, discrete geometric resources (i.e., those in which the model is not a smooth spline surface, but a collection of triangles, quadrilaterals, and other primitives) will want to provide users with a way to select its primitives.
The next section discusses a common use case for selections: creating a new model entity whose geometry is some portion of an existing model entity. This is often done when creating subsets or sidesets on which boundary conditions or material parameters will be applied.
Ephemeral selections¶
A common task in preparing discrete models for simulations is to partition their primitives into regions representing different materials, boundary conditions, initial conditions, or areas where different physical processes will be modeled. Because users often need to iteratively edit these selections, it is useful to create “ephemeral” components. Ephemeral components are removed as soon as users are finised editing a selection.
Panels¶
SMTK provides several panels for use in ParaView-based applications
a resource tree panel that displays a configurable hierarchy of resources and their components.
an attribute panel that displays and edits attribute resources.
a “legacy” operation panel that displays and edits operation parameters.
an operation toolbox panel that displays operations.
an operation parameter-editor panel that lets users edit operation parameters before running them.
The last two operation panels are separate to allow users with small displays (laptops, tablets, etc.) arrange them separately for better use of available space.
Most panels register themselves with ParaView by calling pqApplicationCore::registerManager()
and can thus be retrieved by calling pqApplicationCore::manager()
with their registered
name (listed in the sections below).
Resource panel¶
The pqSMTKResourcePanel exposes an instance of pqSMTKResourceBrowser
to show a tree-view of resources and components.
It is registered with the pqApplicationCore instance as a manager named “smtk resource panel”.
The panel has a setView()
method that accepts a view configuration
so your application can reconfigure the panel.
Attribute panel¶
The pqSMTKAttributePanel displays the top-level view of an SMTK attribute resource.
It is registered with the pqApplicationCore instance as a manager named “smtk attribute panel”.
It can also be programmatically configured to show views (including non-top-level views) by
calling the displayResource()
or displayView()
methods.
Legacy operation view¶
The pqSMTKOperationPanel was created mainly for debugging operations and not intended for production use (although it has been used as such). It is registered with the pqApplicationCore instance as a manager named “smtk operation panel”. It contains a vertical splitter with a list of operations at top (limited by default to those operations that match the current SMTK selection); an operation view (either a qtOperationView or a custom qtBaseView) beneath that for editing parameters; and an a widget at the bottom that was intended to show documentation but is largely unused. At some point in the future, this panel will likely be deprecated and removed. You should prefer the operation toolbox and parameter-editor panels below as replacements.
Operation toolbox panel¶
The pqSMTKOperationToolboxPanel provides a searchable list of operations. It is registered with the pqApplicationCore instance as a manager named “smtk operation toolbox”. The list can be filtered
by search text input from the user;
by a whitelist of operations (passed to the view-widget’s model); and
by how applicable the current SMTK selection is to the operation’s associations.
Operations appear as a grid of push-buttons with icons and the text of the operation’s label. If an operation can be run without editing any parameters (other than associations, which are configured using the SMTK selection), then clicking a push-button will run the operation immediately. Long-clicking a push-button will emit a signal that the parameter-editor panel below accepts to allow further user configuration before running.
You can activate the search bar for operations (i.e., switch the keyboard focus
to the search bar) at any time by pressing Ctrl+Space
(or Cmd+Space
on macos).
While the search bar has focus, pressing the Return
key will emit a signal to
edit the parameters of the first (top, left-most) push button in the grid.
Operation parameter-editor panel¶
The pqSMTKOperationParameterPanel provides a tab-widget holding
zero or more operation or custom views (a combination of
qtOperationView or
qtBaseView instances).
It is registered with the pqApplicationCore instance as a manager named “smtk operation parameters”.
Upon creation, it attempts to connect to signals provided by the operation toolbox
described above. If the toolbox panel exists, the parameter panel will respond
to its requests for editing.
Otherwise, your application must configure it to edit operations directly.
Similarly, if the toolbox panel exists, the parameter editor will connect operation views to
launch operations via that panel.
Otherwise, your application is responsible for listening for the runOperation()
signal
and launching the provided operation.
Widgets¶
SMTK provides special widgets for interacting with model and mesh resources inside ParaView. These qtItem subclasses may be used by specifying them in an attribute’s View configuration.
Design¶
Because SMTK is using ParaView, which is an application built on top of VTK, designing new 3-d widgets for use with SMTK involves several API boundaries and thus several classes.
- VTK.
The basic concept of a 3-d widget is implemented in VTK without any notion of a matching Qt user interface. VTK provides 2 classes that new widgets should inherit: a representation class which is responsible for generating renderable data to provide the visual representation of the widget and a widget class, which is responsible for user interaction related to the visual appearance (i.e., translating mouse hover, click, drag, and even keyboard events into parameter changes that affect the visual representation). For some widgets, a third class is used to hold parameters that define an implicit function which can be used to model related geometry.
A good example of these classes are vtkConeRepresentation and vtkConeWidget. The implicit function representing the cone being modeled is the vtkImplicitConeFrustum class. Frequently, these implicit functions are used to select cells, trim geometry, or perform other editing actions as the user interacts with the widget.
- ParaView.
ParaView uses VTK 3-d widgets. These widgets are rendered on the server but must respond to user interaction that takes place on a desktop client. ParaView uses its client-server protocol to distribute mouse and keyboard events to the server and its client-server property system to retrieve updated parameters from the server as needed (e.g., a sphere’s center and radius might be updated by a VTK widget+representation on the server and then transferred to the client via two DoubleVector properties). ParaView’s widgets have both a 3-d appearance (obtained via VTK) and a 2-d Qt user interface that allows users to inspect and enter exact numeric values when needed. ParaView widgets inherit the pqInteractivePropertyWidget class.
A good example of a ParaView widget class is pqConePropertyWidget, which uses the VTK classes above, but indirectly via server-manager XML in the plugin named smtkPVServerExtPlugin; when a pqConePropertyWidget is created on the client process, it constructs a proxy object named ConeWidgetRepresentation. This proxy object has subproxies named ConeWidget and ConeRepresentation that are backed on the server process by the VTK classes above. When users interact with the VTK classes in the render window, changes to those classes are mapped to ParaView server-manager proxy property objects and notifications are sent to the client that properties are updated. Similarly, changes on the client change proxy properties that are transmitted to the server and used to update the VTK classes.
- SMTK.
SMTK maps ParaView properties back and forth between SMTK attribute items. For example, the cone widget in ParaView requires properties for a generic truncated cone (two points — to determine the axis — and two radii — to determine the radius perpendicular to that axis at each endpoint). However, an SMTK attribute may only want to accept cylinders (a single radius for each point). The SMTK qtItem subclass maps between the items available in the attribute and the properties in ParaView. Each time a ParaView property is set, the updateItemFromWidgetInternal method is invoked. Likewise, when the attribute is modified externally (for example, by a Signal operation being performed), the updateWidgetFromItemInternal method is invoked. These methods both generally invoke a protected method specific to each widget to fetch items from the attribute. Based on the items available, these two methods synchronize values between SMTK attribute items and ParaView properties.
A good example of an SMTK item widget is pqSMTKConeItemWidget.
Gallery¶
SMTK’s existing widgets are listed below with instructions for their use.
The pqSMTKPointItemWidget can be attached to an SMTK Double item with 3 entries: the x, y, and z coordinates of a point in world coordinates. When the widget is in its fully enabled state, the ‘P’ and ‘Ctrl+P’ (or ‘Cmd+P’ on macos) keys may be used to pick point placement in the render window (on any fully-opaque surface underneath the mouse pointer when the key is pressed). The latter version will choose the nearest vertex in the dataset rather than the point on the pixel directly under the pointer.

The Qt widget (left) and 3-D widget (right) for editing point locations.¶
The pqSMTKLineItemWidget can be attached to an SMTK Group item 2 Double children holding 3 values each. Each Double item specifies one line endpoint’s coordinates. In the item’s view configuration, specify Point1 and Point2 attributes naming the names of the Group’s children that represent these points.

The Qt widget (left) and 3-D widget (right) for editing line segments.¶
The pqSMTKPlaneItemWidget can be attached to an SMTK Group item with 2 Double children holding 3 values each: one specifying an Origin point and the other specifying a Normal vector.

The Qt widget (left) and 3-D widget (right) for editing (unbounded) planes.¶
The pqSMTKBoxItemWidget can be attached to an SMTK Double item with 6 entries in the order (xmin, xmax, ymin, ymax, zmin, zmaz). It can also be attached to a group with Min, Max, and optionally Angles items representing two corner points in space and a set of 3 Tait-Bryan rotation angles. Finally, the box widget also accepts a group containing a Center, Deltas, and optionally Angles items representing the box centroid, lengths along 3 orthogonal axes from the center to each face of the box, and rotation angles as above.

The Qt widget (left) and 3-D widget (right) for editing boxes.¶
The pqSMTKConeItemWidget can be attached to an SMTK Group holding two Double items, each with 3 values, representing the bottom and top points of a truncated cone; plus two more Double items, each with a single value representing the radius at each point. Alternately, a single radius Double item may be provided to accept only cylinders.

The Qt widget (left) and 3-D widget (right) for editing truncated cones.¶
The pqSMTKSphereItemWidget can be attached to an SMTK Group holding a Double item with 3 values representing the Center of a sphere and another Double item with a single value representing the Radius.

The Qt widget (left) and 3-D widget (right) for editing spheres.¶
The pqSMTKSplineItemWidget may represent a polyline or a cardinal spline, depending on whether its View configuration has a Polyline attribute set to true or not. The widget must be attached to an SMTK Group holding a Double item with 6 or more values representing the 3-D coordinates of handle points and an Int item interpreted as a boolean that indicates whether the curve should be a closed loop or an open segment.

The Qt widget (left) and 3-D widget (right) for editing polyline and spline curves.¶
SMTK’s Bindings¶
SMTK is written in C++ and uses pybind11 to generate python bindings. Because SMTK also provides extensions to VTK and ParaView – which have their own bindings which do not use pybind11 – SMTK’s python bindings may sometimes return these objects wrapped using other techniques in order to support interoperability.
When you build SMTK with python support enabled (SMTK_ENABLE_PYTHON_WRAPPING
),
you must supply a python library and interpreter.
SMTK’s python bindings use this library’s API and ABI, so import smtk
will only work with this interpreter.
If you enable python support and ParaView support, then
SMTK and ParaView must use the same exact python library and interpreter.
In this case, you can use SMTK inside ParaView’s python shell
and in scripts run by the pvpython
executable shipped with ParaView.
Using bindings generated with one interpreter from a different interpreter is unsupported and will generally cause failures that may be hard to detect due to differences in API/ABI and/or compilation flags.
Python overview¶
There are several python environments from which you can use SMTK
Interactively inside ParaView’s (or ModelBuilder’s) Python Shell. In this case, many plugins have already been loaded and you can obtain pre-existing managers from
smtk.extension.paraview.appcomponents.pqSMTKBehavior.instance()
. Also, python commands you run may have an effect on the user interface of the application. You are responsible for importing whatever SMTK modules you need.Interactively from a python command prompt or by running a python script. In this case, plugins are not loaded unless you manually load them (discussed below). You are responsible for importing whatever SMTK modules you need. User interface components are not available.
From an SMTK operation written in python. In this case, you can assume the environment is prepared, either by the script or the user interface. However, operations should generally not attempt to perform user interaction or assume a user interface is present. You are responsible for importing whatever SMTK modules you need.
Regardless of the environment, SMTK provides python support via two mechanisms:
python modules (
smtk
,smtk.resource
,smtk.operation
, …) that you can import andshared-library plugins (built when ParaView support is enabled) that you can load.
Python modules provide access to C++ classes.
The modules are arranged to mirror the directory structure of SMTK’s source
code (e.g., the smtk.resource
module contains bindings for C++ classes
in the smtk/resource
directory).
C++ classes are wrapped as needed and more effort has been put into wrapping
classes that expose basic functionality than into subclasses that extend
functionality.
This is because most of SMTK’s functionality can be exercised via the
methods on base classes such as smtk::resource::Component;
frequently subclasses do not need wrapping.
Second, shared-library plugins can be loaded from python.
These plugins are typically used to register resource types,
operations, and view classes to managers.
While it is possible to wrap the Registrar
classes each
subsystem of SMTK provides, this is not always done.
In these cases, you should load the plugin and call
the smtk.plugin.registerTo()
method to populate your Manager
instances with classes contained in the loaded plugins.
Consider the following python script:
1# We need to import SMTK's python modules that expose
2# python bindings to C++ objects:
3
4# After importing, we can create manager instances.
5rsrcMgr = smtk.resource.Manager.create()
6opMgr = smtk.operation.Manager.create()
7# The instances are initially empty:
8print('ops:', len(opMgr.availableOperations()))
While it imports the operation and resource modules and creates managers, these managers are not initialized with any resource types or operations because no registrars have been added to the plugin registry. We can load plugins like so:
1# Use ParaView's plugin infrastructure to initialize C++ functionality
2# that may not be python wrapped but that can be used from Python via
3# the wrapped API (such as resource types and operations).
4smtkLibDir = os.path.join(appDir, 'lib', 'smtk-' +
5 smtk.common.Version.number())
6
7# Load the plugins containing Registrars
8LoadPlugin(os.path.join(smtkLibDir, 'smtkAttributePlugin/smtkAttributePlugin.so'))
9LoadPlugin(os.path.join(smtkLibDir, 'smtkOperationPlugin/smtkOperationPlugin.so'))
10# Have the plugins populate the operation manager with their operations.
11smtk.plugin.registerPluginsTo(opMgr)
12smtk.plugin.registerPluginsTo(rsrcMgr)
13# Now we should see some operations. For my configuration, this prints "ops: 62".
14print('ops:', len(opMgr.availableOperations()))
With the plugins loaded, the registrars have been added and the managers can be registered to all the loaded plugins. Finally, we can then ask the resource manager to load a resource for us:
1# Read an attribute resource using the resource manager:
2fta = rsrcMgr.read('smtk::attribute::Resource',
3 '/path/to/fileTestAttribute.smtk')
4allAttributes = fta.filter('*') if fta else []
5print('Read %d attributes from file.' % len(allAttributes))
As an alternative, we can create an operation and run it to load or import a file. The example below imports an SimBuilder Template (SBT) file.
1# It is also possible to use operations, either directly
2# or via an operation group such as the Reader group.
3importOp = opMgr.createOperation('smtk::attribute::Import')
4importOp.parameters().findFile('filename').setValue(
5 '/path/to/fileTestAttribute.sbt')
6result = importOp.operate()
7outcome = smtk.operation.Operation.Outcome(res.findInt('outcome').value(0))
8if outcome == smtk.operation.Operation.SUCCEEDED:
9 print('Imported file')
10else:
11 print('Operation failed with status %s' % outcome)
12 print('Operation log:\n%s' % importOp.log().convertToString())
Python plugins in modelbuilder¶
Because of the complex mechanics of the different python environments, modelbuilder provides a simple way to register operations that should be automatically available each time you run (rather than requiring you to manually click on the “File→Import Operation…” menu each time).
Instead, once you have your Python operation defined in a module file, you can tell modelbuilder to treat it as a plugin, which can be set to auto-load on startup. To do this, we’ll add a few lines of code to the bottom of your module like so:
import smtk.operation
class CustomOperation(smtk.operation.Operation):
# Define your operation methods here as usual...
if __name__ != '__main__':
from contextlib import suppress
with suppress(ModuleNotFoundError):
import smtk.extension.paraview.appcomponents as app
app.importPythonOperation(__name__, 'CustomOperation')
In the example above, you will already have defined the CustomOperation
class and only need to add the if
-block at the bottom.
If your module has multiple operations, you can call app.importPythonOperation()
as many times as you like.
Once you have added this to your python module, click on the “Tools→Manage Plugins…” menu item in modelbuilder. When the dialog appears, click on “Load New” and select your module. It should load and immediately register your new operation. If you want the operation to be available each time you start modelbuilder, just click on the “Auto-load” option in the plugin manager and exit modelbuilder; the setting will be saved and your module will be imported on subsequent runs.
Generating pybind11 bindings¶
SMTK’s pybind11 bindings are generated via python scripts that use
pygccxml to construct a C++ header file corresponding to each header
file in SMTK, and a C++ source file for each namespace in
SMTK. Generally, python modules are a reflection of C++ namespaces
(which are generally contained within a subdirectory). The generated
pybind11 C++ files for each module are in the pybind
subdirectory of
the module.
To generate a C++ header file for a new SMTK class, use
[smtk-root-directory]/utilities/python/cpp_to_pybind11.py
with the
appropriate arguments for the header file, project root directory,
include directories (e.g. -I "[smtk-build-directory]
[smtk-root-directory]/thirdparty/cJSON path/to/vtk/include"
) and
generated file prefix; the module’s binding source file (also located
in the pybind
subdirectory of the module) must then be updated to
call the functions defined in the generated header file. To generate
all of the C++ headers and the module C++ source file for a namespace
at once, use
[smtk-root-directory]/utilities/python/generate_pybind11_module.py
with the appropriate arguments for the module directory, project root
directory and include directories.
The generated bindings should be treated as a starting point for customization to create a more pythonic interface.
Customizing pybind11 bindings¶
One common shortcoming of the generated bindings is that templated methods
are not wrapped.
Sometimes, a templated method is the only way to set or get the state of
a C++ class instance and the template is used so that the various STL
containers can be used.
Consider the method smtk::resource::SelectionManager::modifySelection
which is templated on the type of container holding objects to replace, be added to,
or be removed from the selection.
Without wrapping this method, it is impossible to change the selection
from Python.
By adding this definition to smtk/resource/pybind11/PybindSelectionManager.h
:
PySharedPtrClass< smtk::resource::SelectionManager > instance(m, "SelectionManager");
instance
.def("modifySelection",
(bool (smtk::resource::SelectionManager::*)(
const ::std::vector<
smtk::resource::Component::Ptr,
std::allocator<smtk::resource::Component::Ptr> >&,
const std::string&,
int,
smtk::resource::SelectionAction))
&smtk::resource::SelectionManager::modifySelection)
we cast the function pointer to be wrapped to a particular template type that we know pybind can handle (a vector of shared pointers to components). While these casts of member functions can be verbose and sometimes difficult to read, they make it easy to expose templated member functions to pybind without changing the C++ class declaration, which is very desirable.
Todo
Explain how to make this robust against re-runs of the script that generates bindings.
SMTK’s Plugin System¶
SMTK’s functionality can be extended by consuming projects via the generation of plugins.
Plugins¶
SMTK plugins are extensions of ParaView plugins that allow for the definition of SMTK managers and the automatic registration of components to these managers. They are created using the CMake function “add_smtk_plugin”, which requires the developer to explicitly list a registration class known as a “Registrar” and a list of SMTK manager types to which the plugin registers. SMTK plugins can be introduced to a ParaView-based application in several ways. The consuming project can
1) list the plugins in a configuration file that is subsequently read at runtime, deferring the inclusion of plugins to the application’s runtime. This approach requires plugins to reside in certain locations that the application is expected to look, but facilitates the presentation of a plugin to the user without automatically loading the plugin. For this approach, a consuming project can call “generate_smtk_plugin_config_file” to convert the list of smtk plugin targets (which can be a part of the project or imported from another project) described by the global property “SMTK_PLUGINS” into a configuration file. The consuming project can also
2) directly link plugins into the application. This approach pushes the requirement of locating plugins to be a build-time dependency, which can be advantageous for packaging. Plugins that are directly linked to an application cannot be disabled, however (i.e. the target property ENABLED_BY_DEFAULT is ignored, as it is true for all plugins). To use this approach, a consuming project can call “generate_smtk_plugin_library” to to use the list of smtk plugin targets (which can be a part of the project or imported from another project) described by the global property “SMTK_PLUGINS” to generate a library against which the application can link to directly incorporate the associated plugins.
Managers are introduced to SMTK by registering them with the instance of smtk::common::Managers. Upon registration, the singleton instance of smtk::plugin::Manager can be used to register additional plugins to the newly created manager. See smtk/resource/Registrar.cxx for an example of introducing a manager.
String token utilities¶
SMTK provides classes for string tokenization and fast comparison of tokens in it’s string directory.
Motivation¶
String equality tests can be slow, but using integer values to enumerate names for things (e.g., labels, tags, enum and event names, keywords in an input deck, etc.) can prevent plugins from extending the set of names with new values and can also lead to errors when values are written to files that must be maintained even as the set of valid values is modified.
To ameliorate the situation, SMTK provides some utility classes for string tokenization. Treating some set of fixed strings as tokens allows compact storage (the token ID can be stored in place of a potentially long string) and easy extension (since files can contain the mapping of token IDs to strings and those mappings can be reconciled at runtime).
Concepts¶
SMTK provides a string Token class
to represent a string with an integer ID;
a token can be constructed from a std::string
or
from a string literal like so:
#include "smtk/string/Token.h"
using smtk::string::Token;
Token a = "foo";
Token b = """bar"""_token;
Token c = std::string("baz");
A token can provide the source string data if it was constructed
by passing a string to be hashed, but may throw an std::invalid_argument
exception if then token was constructed by passing the hash code
directly (i.e., with smtk::string::fromHash()
). So, in the example
above, a.data()
and c.data()
will return “foo” and “baz”,
respectively, but b.data()
will throw an exception unless “bar” was
added to the string-token manager elsewhere.
Equality comparisons are done by comparing integer token identifiers (hash codes) and are thus fast. Inequality comparisons resort to string-value comparisons and thus may be slow for large strings with identical prefixes.
std::cout << "a is \"" << a.data() << "\" with id " << a.id() << "\n";
// prints: a is "foo" with id 9631199822919835226
std::cout << "b is \"" << b.data() << "\" with id " << b.id() << "\n";
// prints: b is "bar" with id 11474628671133349555
a == b; // This is fast since it only compares token IDs.
a != b; // This is fast since it only compares token IDs.
a < b; // This is slow since it compares underlying strings.
As noted in the example above, less-than and greater-than operators are slow because they compare the underlying strings. This preserves lexographic ordering when you store tokens in ordered containers.
Switch statements¶
As of SMTK 22.10, string tokens may be constructed via a
constexpr
literal operator named _hash
. This makes it possible
for you to use switch statements for string tokens, like so:
using namespace smtk::string::literals; // for ""_token
smtk::string::Token car;
int hp; // horsepower
switch (car.id())
{
case "camaro"_hash: hp = 90; break;
case "mustang"_hash: hp = 86; break;
case "super beetle"_hash: hp = 48; break;
default: hp = -1; break;
}
String source data¶
Token IDs and their corresponding string source-data are stored in a class-static dictionary called the string Manager as mentioned above. This dictionary is what allows Tokens to return the original string data while only holding a token ID.
The dictionary can be serialized-to and deserialized-from JSON. Individual token instances are serialized simply as their integer identifier. Because platforms may tokenize strings differently, the dictionary provides a “fallback map” constructed during deserialization to translate hash codes from other platforms.
Token hashing algorithm¶
The hash algorithm will generate hashes of type std::size_t
but only supports 32- and 64-bit platforms at the moment.
Note that because the string manager uses a serialization helper
to translate serialized hash values (this was previously required
since std::hash_function<>
implementations varied), reading
tokens serialized by a 32-bit platform on a 64-bit platform will
not present problems. However, reading 64-bit hashes on a 32-bit
platform is not currently supported; it may be in a future release
but we do not foresee a need for it.
Debugging tips and tricks¶
Several design patterns in SMTK can complicate debugging:
SMTK allows asynchronous operations run in separate threads.
Shared pointers mean that resources and components may sometimes not be destroyed when you expect.
The included ParaView extensions have to deal with potentially separate client and server processes.
This section provides some tips on debugging to help mitigate the complexity.
Debugging observers¶
Observers are documented in the Observers section of the manual. The issues to be aware of when debugging observers are:
Most observers inherit smtk::common::Observers, which is templated on the signature of functions to be called when an event occurs.
The Observers class has a second template parameter (a boolean) that turns on debug printouts when true. The default is false.
Observers are forced to run on the main (GUI) thread in ParaView by the pqSMTKCallObserversOnMainThreadBehavior class. This means that observers are called asynchronously at some point after the operation has completed and potentially after other SMTK calls have been made. Because operation results may hold shared pointers to components and resources (thus keeping them alive), destructors may not be called when you expect.
An observer may remove itself from an Observers instance while it is being evaluated. The actual erasure will occur after all observers for the event have been invoked. When this occurs, it is noted in the debug printouts.
What the above means is that if you are having issues with observers of, for example, resource manager events, you will want to change smtk/resource/Observer.h from
typedef smtk::common::Observers<Observer> Observers
to
typedef smtk::common::Observers<Observer, /* debug */ true> Observers
and rebuild+install SMTK. Then, information about the lifecycle of each observer in the resource-manager event will be reported (its addition, invocation, and removal).
Administering SMTK¶
Previous sections covered the concepts and tools for using SMTK. This section is for system administrators who wish to make SMTK available to users
as a Python module and command-line utilities for end users of SMTK,
as a library for people developing SMTK-based applications, and/or
as a remote model and mesh server for end users and applications.
End-user tool installation¶
This type of installation should be as simple as downloading a binary package of SMTK and clicking install.
Todo
Expand on details of installation and configuration.
Developer installation¶
In addition to the binary installer, there should also be a development package that contains header and configuration files needed to build C++ applications using SMTK. Install this the same way you installed the binary above.
You can also download the source code from the git repostory and
follow the instructions for building and installing SMTK in the
toplevel ReadMe.md
file.
Todo
Expand on details of installation and configuration.
Configuration as a modeling and meshing server¶
SMTK uses Remus to start model and mesh workers. In order for SMTK to discover available workers, you must place Remus worker files somewhere that SMTK is configured to search for them. These worker files identify the executable to run when a user requests a modeling or meshing operation to be performed. Their format is covered further below, but first we focus on how the files are discovered.
Worker file search paths¶
The default locations that SMTK searches for these worker files varies by operating system:
- Linux
SMTK searches the current working directory of the process, followed by the
var/smtk/workers
subdirectory of the toplevel installation directory. For example, if SMTK is installed into/usr
with the worker at/usr/bin/smtk-model-worker
, then it will search/usr/var/smtk/workers
.If the
SMTK_WORKER_DIR
environment variable is set to a valid path, then it is searched as well.- Mac OS X
SMTK searches the current working directory of the process, followed by the
var/workers
subdirectory of the toplevel installation directory if SMTK is not part of a bundle. For example, if SMTK is installed into/usr
with the worker at/usr/bin/smtk-model-worker
, then it will search/usr/var/smtk/workers
.If an application built with SMTK is part of a bundle (such as an app), then SMTK will search the
Contents/Resources/workers
directory of the bundle.If the
SMTK_WORKER_DIR
environment variable is set to a valid path, then it is searched as well.- Windows
SMTK searches the current working directory of the process followed by the directory containing the process executable (when provided to SMTK by the application).
If the
SMTK_WORKER_DIR
environment variable is set to a valid path, then it is searched as well.
Creating a Remus worker file for solid modeling¶
When SMTK is built with Remus support enabled, it will include a
command-line utility named smtk-model-worker
.
This program can be run manually or directly by SMTK in order
to perform modeling operations in a different process.
It is also the program you can run to generate a worker file
that makes it discoverable to SMTK for direct use.
You can run
smtk-model-worker -help
to obtain reference information on the command-line arguments. It will also print a list of available modeling kernels.
Each model worker exposes a single modeling kernel (via smtk::session::remote::Session on the client, which talks to a RemusRPCWorker in the worker process). Normally, the model worker executable expects to be given the following command-line arguments:
A Remus server to connect to as its first argument, formatted as a URL (e.g.,
tcp://cadserver.kitware.com:50510
).A solid modeling kernel to advertise (e.g.,
-kernel=cgm
).A default modeling engine for the kernel to use (e.g.,
-engine=OpenCascade
).A Remus worker file to read (when invoked without
-generate
) or write (when invoked with-generate
).A directory in the filesystem to make available to users for reading and writing CAD model files (e.g.,
-root=/usr/local/var/smtk/data
). The root directory is not made available to end users for security purposes; all paths are relative to the root directory. SMTK does not currently prevent access to other portions of the filesystem but it will in the future.A “site name” describing the combination of host and/or filesystem made available to SMTK by the model worker. This label is presented to end users by applications so that users can differentiate between workers providing the same modeling kernels but on different machines.
A Remus worker file to read or write (e.g.,
-rwfile=/usr/local/var/smtk/workers/cgm-OCC.rw
).
If you pass the -generate
option to the model worker,
then it will generate a model worker file which you can then
customize.
When you generate a model worker file,
two files are normally written:
the first, specified by the -rwfile
argument is the
actual Remus worker file and is formatted as a JSON object.
The second has the same filename with a .requirements
suffix appended and is formatted as an XML attribute resource
describing the modeling operations available.
You should generate a separate Remus worker file for each combination of modeling kernel, engine, and root directory you wish to make available. Once these files have been generated, you can edit the worker file and change them to suit your site’s needs. You may specifically wish to change the WorkerName setting to be more descriptive. Be careful when editing the Tag data as it is used by SMTK to decide which engine and kernel combination may load a given file.
Contributing to SMTK¶
Contents
The first step to contributing to SMTK is to obtain the source code and build it. The top-level ReadMe.md file in the source code includes instructions for building SMTK. The rest of this section discusses how the source and documentation are organized and provides guidelines for how to match the SMTK style.
Source code organization¶
To a first approximation, SMTK’s directory structure mirrors the namespaces used:
classes in the smtk::attribute
namespace are mostly found in the
smtk/attribute
directory.
Exceptions occur where classes that belong in a namespace depend on third-party libraries
that should not be linked to SMTK’s core library.
For example, Qt widgets for attributes are in smtk/extensions/qt
, not in smtk/attribute
because they depend on Qt, which is optional when building SMTK (so that, for instance,
solid modeling kernels like Cubit may be supported without issue).
With that in mind:
smtk — this directory contains all of the source code for SMTK libraries and tests
common — source for classes used throughout the smtkCore library
resource — SMTK’s Resource System defines base resources (files) and components used elsewhere
attribute — source for SMTK’s Attribute Resource in the smtkCore library
model — source for SMTK’s Geometric Model Resource in the smtkCore library
mesh — source for SMTK’s Mesh Resource in the smtkCore library
operation — SMTK’s Operation System provides asynchronous operations that act on resources
simulation — aids to exporting simulation input decks in the smtkCore library
io — file and string I/O in the smtkCore library, a mix of XML and JSON
view — source for providing views (user presentations) of resources in the smtkCore library
session — source for additional libraries that session solid modeling kernels into SMTK
extensions — source for additional libraries that expose SMTK to other software
thirdparty
cJSON — used to serialize geometric model information
pugiXML — used to serialize attribute resources
utilities — scripts to aid in the development of SMTK. * encode – a C++ utility used to encode a file’s contents into a C++ function,
a C++ string literal, or a Python block-quote in order to embed it into a plugin or module.
Inside smtk/
, subdirectories, there are testing/
directories that
hold python/
and cxx/
directories for Python and C++ tests, respectively.
These are discussed more in smtk-testing-sys.
Adding data to SMTK¶
There are two tasks related to adding data to SMTK:
Storing data in the SMTK repository; and
Embedding data in SMTK libraries, plugins, and/or python modules.
Data in the repository¶
SMTK uses Git Large File Storage (LFS) to hold large files of any format, whether it be binary or ASCII. Using Git LFS is a way to reduce the amount of data developers must download to just that in active use by the branch of SMTK they are working on.
If you need to add a large file to SMTK,
Ask SMTK developers the best approach to hosting the data early in the process to avoid extra work.
If Git LFS is the proper way to add data, ensure that you have run
git lfs install
in your local SMTK clone and usegit lfs status
to verify that large files you are about to commit will be stored with LFS.
Embedding data in SMTK targets¶
SMTK provides a CMake macro, smtk_encode_file()
, that will
transcribe a repository file into a C++ literal, C++ function,
or Python block-quote. This generated file can be built into
targets such as libraries, plugins, or Python modules – allowing
you to load content without needing to locate where on a user’s
filesystem it might reside.
smtk_encode_file¶
This function adds a custom command that generates a C++ header or Python
source-file which encodes the contents of a file (<xml>
) from your
source directory. This is used frequently for operation sim-builder
template (SBT) files, icon (SVG) files, and other data you wish to
include in packages. By encoding the file, there is no need to rely on
code to search for it on the filesystem; it is either compiled into a
library (C++) or included as part of a python module (which uses Python’s
utilities for importing the data rather than requiring you to find it).
Because this function is frequently used to encode SMTK operation SBT
files, it provides a utility to replace XML <include href="…"/>
directives with the contents of the referenced file.
Query paths for included files are passed to this macro with the
flag INCLUDE_DIRS
.
By default, the output header or python files are placed in the binary
directory that corresponds to their source path, but with the input
content type (e.g., _xml
, _json
, _svg
) appended and the
appropriate extension (.h
or .py
).
If you wish to override this, pass an output filename to
the DESTINATION
option.
smtk_encode_file(
<xml>
INCLUDE_DIRS <path_to_include_directories>
EXT "py" (optional: defaults to "h" and C++; use "py" for python)
NAME "var" (optional: the name of the generated string literal or function)
TYPE "_json" (optional: defaults to "_xml", appended to file and var name)
HEADER_OUTPUT var (optional: filled in with full path of generated header)
DESTINATION var (optional: specify output relative to current build dir)
)
Note that if you use smtk_encode_file()
in a subdirectory relative to
the target which compiles its output, you must add the DESTINATION
(if
specified) or the value returned in HEADER_OUTPUT
to a custom_target
(see this FAQ for details on why).
TARGET_OUTPUT
can be used to generate a custom_target directly, but
it is not recommended because it causes long paths and extra targets
for make and VS projects on windows.
Note that if you are using smtk_encode_file
external to SMTK, you do
not need to include ${smtk_PREFIX_PATH}/${SMTK_OPERATION_INCLUDE_DIR}
in INCLUDE_DIRS
as it is automatically added; this allows your
external projects to reference the operation, result, and hint attribute
definitions that SMTK provides.
Currently, the following TYPE
options are recognized:
_py
, _json
, _svg
, _xml
, and _cpp
.
The cpp
option does not indicate the input-file’s content format
but rather that the output file should be an inline, anonymous-namespaced
C++ function. This option exists for large files (more than 16kiB), for which
MSVC compilers cannot produce a string-literal.
Extending SMTK¶
As with all software, it is important to understand where functionality you wish to add belongs: in your project, in SMTK’s core library, or in an upstream dependency of SMTK.
The tutorials provide in-depth guides on how to extend SMTK in certain obvious directions,
Writing an attribute resource template file to represent a solver’s input format.
Writing an exporter to support a new solver’s input format.
Adding a new solid-modeling operator
Bridging SMTK to a new solid-modeling kernel
These tasks are all examples of projects that might use SMTK as a dependency but not alter SMTK itself. On the other hand, if you are attempting to provide functionality that (a) does not introduce dependencies on new third-party libraries; (b) will be useful to most projects that use SMTK; and (c) cannot be easily be factored into a separate package, then it may be best to contribute these changes to SMTK itself. In that case, the rest of this section discusses how SMTK should be modified and changes submitted for consideration.
Exposing SMTK for use in external projects¶
SMTK generates a file named SMTKConfig.cmake
that allows other projects to find and use SMTK.
This file is installed to $CMAKE_INSTALL_PREFIX/lib/cmake/SMTK/
.
External projects can add SMTK with
find_package(SMTK)
Then, when building external projects, set CMake’s SMTK_DIR
to the directory containing SMTKConfig.cmake
.
Note that you may point external projects to the top level of an SMTK build directory or
an install tree’s lib/cmake/SMTK
directory; both contain an SMTKConfig.cmake
file
suitable for use by external projects.
The former is suitable for development, since you may be modifying both SMTK and a project
that depends on it — having to re-install SMTK after each change becomes tedious.
The latter is suitable for creating packages and distributing software.
If you add a new dependency to SMTK, CMake/smtkConfig.cmake.in
(which is used to create
SMTKConfig.cmake
) should be configured to find the dependent package so that consumers
of SMTK have access to it without additional work.
If you add a new option to SMTK, it should be exposed in CMake/Options.h.in
.
Code style¶
No tabs or trailing whitespace are allowed.
Indent blocks by 2 spaces.
Class names should be camel case, starting with an uppercase.
Class member variables should start with
m_
ors_
for per-instance or class-static variables, respectively.Class methods should be camel case starting with a lowercase character (except acronyms which should be all-uppercase).
Use shared pointers and a static
create()
method for classes that own significant storage or must be passed by reference to their superclass.
Documentation style¶
There are two types of documentation in SMTK: Doxygen documentation written as comments in C++ code and Sphinx documentation written in reStructuredText files (and optionally Python documentation strings). The former is used to create reference documentation; the latter is used for the user’s guide and tutorials.
The following rules apply to writing documentation:
Header files should contain the Doxygen documentation for the class as a whole plus any enums declared outside classes, however:
Implementation files should contain the Doxygen documentation for class methods. This keeps the documentation next to the implementation (making it easier to keep up-to-date). It also makes the headers easier to read.
If a class provides high-level functionality, consider writing some user-guide-style documentation in the User’s Guide (in
doc/userguide.rst
) or a tutorial (indoc/tutorials/
). Tutorials should include a working example that is run as a CTest test. The code in the example should be referenced indirectly by the tutorial so that the the exact code that is tested appears as the text of the tutorial.In reStructuredText documents, you should use the doxylinks module to link to the Doxygen documentation when appropriate. Examples:
:smtk:`UUID`
produces this link: UUID while the:smtk:`Resource <smtk::attribute::Resource>`
variant can produce links (Resource in this case) whose text varies from the classname or whose classnames are ambiguous because of namespaces. The leading:smtk:
names the tag file holding the class and function definitions; other third-party-library tag files may be added in the future.You will be tempted to make every word that is a classname into a Doxygen link; do not do this. Instead, provide a Doxygen link at the first occurrence of the classname in a topic’s discussion — or at most in a few key places. Otherwise the documentation becomes difficult to read due to conflicting text styles.
In reStructuredText, when you wish to show code in-line but it is inappropriate to link to Doxygen documentation, use the
:cxx:
role for C++ (e.g.,if (foo)
), the:file:
role for paths to files (e.g.,doc/index.rst
), and so on. See the documentation for roles in reStructuredText for more information.Note that the user’s guide and tutorials are both included in the top-level
doc/index.rst
file parsed by Sphinx. Several extensions to Sphinx are used and these are configured indoc/conf.py
.
To get started documenting your code, you should at least have doxygen and graphviz installed. These are available using Homebrew on Mac OS X, your Linux distribution’s package manager, or by binary installer from the source maintainer on Windows.
Additionally there are a number of Python packages that provide Sphinx, docutils, and other packages required to generate the user’s guide. These packages can all be installed with pip:
# The basic utilities for processing the user's guide:
sudo pip install docutils
sudo pip install Sphinx
# For linking to external Doxygen docs:
sudo pip install sphinxcontrib-doxylink
# For creating inline class docs from Doxygen XML:
sudo pip install breathe
# For the default theme:
sudo pip install sphinx-rtd-theme
# For syntax highlighting:
sudo pip install Pygments
# For activity diagrams:
sudo pip install sphinxcontrib-actdiag
If you are unfamiliar with the documentation packages here, see these links for examples of their use (or use SMTK by example):
Testing¶
Testing is important to keep SMTK functioning as development continues. All new functionality added to SMTK should include tests. When you are preparing to write tests, consider the following
Unit tests should be present to provide coverage of new classes and methods.
Build-failure tests provide coverage for template metaprogramming by attempting to build code that is expected to cause static assertions or other compilation failures.
Integration tests should be present to ensure features work in combination with one another as intended; these tests should model how users are expected to exercise SMTK in a typical workflow.
Regression tests should be added when users discover a problem in functionality not previously tested and which further development may reintroduce.
Contract tests should be added for downstream projects (i.e., those which depend on SMTK) which SMTK should not break through API or behavioral changes. A contract test works by cloning, building, and testing an external project; if the external project’s tests all succeed, then the contract test succeeds. Otherwise, the contract test fails.
Unit tests¶
SMTK provides a CMake macro named smtk_unit_tests
that you should use to create unit tests.
This macro will create a single executable that runs tests in multiple source files;
this reduces the number of executables in SMTK and makes tests more uniform.
Because there is a single executable, you should make your test a function whose name
matches the name of your source file (e.g., int TestResource(int, const char* [])
)
rather than int main(int, const char* [])
.
The CMake macro also allows a LABEL to be assigned to each of the tests in the executable;
this label can be used during development to run a subset of tests and during integration
to identify areas related to test failures or timings in CDash.
Build-failure tests¶
Build-failure tests verify that code which is expected to cause a compiler error
does actually cause an error.
A CMake macro named smtk_build_failure_tests
is
provided in CMake/SMTKTestingMacros.cmake
.
This macro generates tests that succeed when they fail to compile code you provide, as a way
to improve coverage of template metaprogramming code.
You can attempt to build the same source file multiple times; each time, a compiler macro named SMTK_FAILURE_INDEX is assigned an increasing integer so you can change what code is tested. Consider this example
int main()
{
#if SMTK_FAILURE_INDEX < 1
static_assert(false, "Failure mode 1");
#elif SMTK_FAILURE_INDEX < 2
static_assert(false, "Failure mode 2");
//...
#endif
return 0;
}
If this file was named simple.cxx, you could add tests for it with
smtk_build_failure_tests(
LABEL SomeLabel
TESTS
simple.cxx 2
LIBRARIES smtkCore
)
This would create 2 tests that each try to compile simple.cxx with different SMTK_FAILURE_INDEX values (0 and 1).
Contract tests¶
Contract tests can be added by appending a URL to the SMTK_PLUGIN_CONTRACT_FILE_URLS
CMake variable.
You can see an example of this in SMTK’s top-level CMakeLists.txt:
1 if (SMTK_ENABLE_PARAVIEW_SUPPORT)
2 set(local_url_prefix "file://")
3 if (WIN32)
4 string(APPEND local_url_prefix "/")
5 endif()
6 list(APPEND SMTK_PLUGIN_CONTRACT_FILE_URLS
7 # Example
8 # "${local_url_prefix}${CMAKE_CURRENT_SOURCE_DIR}/CMake/resource-manager-state.cmake"
9 )
10 endif()
Each URL listed in the CMake variable must be a CMake script; typically, this script will declare a new project consisting of an external project dependent on SMTK.
1# This file is provided with SMTK as an example of how to configure a plugin
2# contract file. In general, SMTK should not contain these files. Instead, the
3# dashboard computer that runs SMTK tests should be passed a list of URLs for
4# plugin contract test files during its configuration.
5
6cmake_minimum_required(VERSION 2.8)
7project(resource-manager-state)
8
9include(ExternalProject)
10
11ExternalProject_Add(resource-manager-state
12 GIT_REPOSITORY "https://gitlab.kitware.com/cmb/plugins/read-and-write-resource-manager-state.git"
13 GIT_TAG "origin/master"
14 PREFIX plugin
15 STAMP_DIR plugin/stamp
16 SOURCE_DIR plugin/src
17 BINARY_DIR plugin/build
18 CMAKE_ARGS
19 -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS}
20 -DENABLE_TESTING=ON
21 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
22 -Dsmtk_DIR=${smtk_DIR}
23 ${response_file}
24 INSTALL_COMMAND ""
25 TEST_BEFORE_INSTALL True
26)
When the script is run, the external project’s source is downloaded, the project configured, and tests are run. If they all complete with success, then the contract test succeeds.
This pattern of appending URLs was chosen so that machines used to test merge requests which happen to have access to closed-source downstream projects could be configured with additional URLs to contract tests. That way, even closed-source downstreams can cause test failures in SMTK; this informs developers of SMTK merge requests that additional work may be required on downstream projects before the merge can succeed.
To-do list¶
Finally, if you are looking for a way to contribute, helping with the documentation would be great. A list of incomplete documentation (or incomplete features) is below. You can also look on the SMTK issue tracker for things to do.
Todo
Expand on details of installation and configuration.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/administration.rst, line 27.)
Todo
Expand on details of installation and configuration.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/administration.rst, line 41.)
Todo
Describe attributes and how they are serialized
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/attribute/file-syntax.rst, line 1651.)
Todo
Describe <ItemsViews> children
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/attribute/file-syntax.rst, line 2032.)
Todo
Explain how to make this robust against re-runs of the script that generates bindings.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/bindings/customizing-pybind11-bindings.rst, line 38.)
Todo
Create runtime-configurable classes (ProgrammableResource, ProgrammableNode, ProgrammableArc) that can be Python-wrapped and configured via a JSON serialization.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/graph/concepts.rst, line 80.)
Todo
Add a section domain types, especially IdSpace for discrete geometry.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/markup/concepts.rst, line 145.)
Todo
Describe SMTK’s auto-init macros.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/model/operators.rst, line 16.)
Todo
Describe what blocks and sets are
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/model/session-exodus.rst, line 10.)
Todo
Describe how blocks and sets are mapped to models and groups
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/model/session-exodus.rst, line 11.)
Todo
Show simple example?
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/model/session-exodus.rst, line 12.)
Todo
Update how modeling sessions are exposed via plugins.
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/model/sessions.rst, line 53.)
Todo
Discuss Remus and ParaView client/server sessions
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/model/sessions.rst, line 58.)
Todo
Describe the base phrase model class
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/view/phrases.rst, line 43.)
Todo
Describe the base phrase content class and its children
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/view/phrases.rst, line 74.)
Todo
Describe the base subphrase generator class and its children
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/smtk/checkouts/v23.04.0/doc/userguide/view/phrases.rst, line 79.)
Tutorials¶
While the user’s guide covers the operational theory of SMTK in a meaningful way, sometimes it helps to have a more practical guide to getting a simple task done without reading the entire manual. These tutorials provide quick formulas for simple tasks without requiring much theory.
First steps in C++¶
To get started with SMTK, let’s create a project that does the bare minimum; we’ll just print the SMTK version number and exit.
Including SMTK headers and calling methods¶
The code to do this just calls a static method on smtk::common::Version:
1#include <iostream>
2
3#include "smtk/common/Version.h"
4
5int main()
6{
7 std::cout << "Compiled with SMTK version " << smtk::common::Version::number() << "\n";
8 return 0;
9}
All of SMTK’s headers are prefixed by “smtk,” and because the version number is useful across all of SMTK and not just particular subsystems, it is put in the “common” directory. Usually — but not always — the subdirectories indicate the C++ namespace that a class lives in. Exceptions to this rule are classes in the extension directory, which are grouped separately because they appear in other libraries that have additional dependencies.
Compiling the example¶
To compile the program above, we need to link to SMTK’s main library, named smtkCore.
The following CMakeLists.txt
will set up a project for us:
1cmake_minimum_required(VERSION 3.12)
2project(smtk_version)
3
4find_package(smtk)
5add_executable(print_version print_version.cxx)
6target_link_libraries(print_version smtkCore)
The find_package
directive tells CMake to find SMTK on your system
and import its settings.
SMTK provides settings in a file named SMTKConfig.cmake
and it is
usually stored in /usr/local/lib/cmake/SMTK
on Unix and Mac OS X
systems or C:\SMTK\lib\cmake\SMTK
on Windows.
When CMake asks for SMTK_DIR
, you should provide it
with the directory containing SMTKConfig.cmake
.
Then, the target_link_libraries
directive tells CMake not only
to link to smtkCore, but also to add compiler directives to all source
code in the executable specifying the location of header files.
This directive also adds any transitive dependencies of smtkCore to
the print_version
program.
First steps in Python¶
To demonstrate the basic workflow in Python, we’ll cover how to:
create attribute and model managers;
create an attribute resource to hold simulation information;
populate the attribute resource with definitions for a particular simulation run;
generate an ensemble of input decks whose attributes use these definitions with different values as part of a sensitivity study;
load a geometric model that has side sets used to hold boundary conditions;
relate the side sets to boundary condition attributes in the ensemble; and
write an input deck for each entry in the ensemble.
Our running example will be a fluid mechanics problem where we run an ensemble of simulations to characterize the sensitivity of pump work to viscosity and inlet velocity at our expected operating conditions.
Setup¶
The first part of our script imports SMTK and creates managers:
1
2ares = smtk.attribute.Resource.create()
3mmgr = smtk.model.Resource.create()
4
This will almost always be the first thing your Python scripts do.
Problem Definition¶
Now that the environment is set up, we can define the attribute system that our simulation expects as its problem definition.
Everything in this entire section is usually replaced by creating an XML file describing the problem definition. However, the purpose of this tutorial is to demonstrate how to implement your simulation workflow in Python and there can be times when you wish to programmatically create a problem definition, or template file.
The first thing we do is create attribute Definitions for the basic types of simulation inputs we must provide:
1bcDef = ares.createDefinition('boundary condition')
2icDef = ares.createDefinition('initial condition')
3matDef = ares.createDefinition('material properties')
4
5# Our material is defined by a viscosity
6viscosity = smtk.attribute.DoubleItemDefinition.New('viscosity')
7matDef.addItemDefinition(viscosity)
Once we’ve created the material definition, we can add all the individual properties required to specify a material. More complicated definitions that allow materials of widely different types — each with its own unique parameters — is a topic for another day. Here, we just indicate that viscosity is the only parameter required to define our fluid, perhaps because the simulation assumes incompressibility.
The definitions for boundary and initial conditions are more complex because we might have different combinations of Dirichlet, Neumann, or Cauchy conditions for different variables over different portions of the domain. Rather than try to use a single Definition for all of these, we can use SMTK’s inheritance system to create derived Definitions:
1# We have one initial condition for the interior of the domain:
2fluidICDef = ares.createDefinition('fluid ic', icDef)
3
4# The specific types of BCDefs our example must define:
5# At the outlet, pressure must be given.
6# At the inlet, fluid velocity and temperature must be given.
7# On the wall, heat flux must be specified.
8outletBCDef = ares.createDefinition('outlet bc', bcDef)
9inletBCDef = ares.createDefinition('inlet bc', bcDef)
10wallBCDef = ares.createDefinition('wall bc', bcDef)
11
12# Each ICDef/BCDef holds a different type of value:
13temperature = smtk.attribute.DoubleItemDefinition.New('temperature')
14pressure = smtk.attribute.DoubleItemDefinition.New('pressure')
15velocity = smtk.attribute.DoubleItemDefinition.New('velocity')
16heatflux = smtk.attribute.DoubleItemDefinition.New('heatflux')
17
18# Add the values to the appropriate BCDef:
19fluidICDef.addItemDefinition(temperature)
20fluidICDef.addItemDefinition(velocity)
21
22outletBCDef.addItemDefinition(pressure)
23
24inletBCDef.addItemDefinition(velocity)
25inletBCDef.addItemDefinition(temperature)
26
27wallBCDef.addItemDefinition(heatflux)
We could also have created a definition to hold global simulation parameters such as convergence criteria, halting criteria, and desired accuracy; but for simplicity, we will assume the simulation package has reasonable defaults.
Now that we have quantities of interest specified for each attribute definition, we can provide hints to make data entry less error-prone. For instance:
SMTK accepts units for items that store continuous or discrete numeric values. These are presented to the user during entry to avoid confusion.
Viscosity, temperature, and pressure have absolute minimum values. Items that store quantitative values accept minimum and maximum values; a boolean argument passed with the minimum or maximum indicates whether the limit value is attainable.
Items store a single value in their given primitive type by default. But arrays of values may be accepted and these arrays may be fixed in size (as our 2-dimensional velocity requires exactly 2 components) or extensible between a minimum and maximum array length.
It is also possible to mark an Item so that its entries should come from an enumerated set of acceptable values, but we do not illustrate that here.
1viscosity.setMinRange(0, True)
2viscosity.setUnits('Pa * s') # or Poise [P]
3
4temperature.setMinRange(-273.15, True)
5temperature.setUnits('deg C')
6
7pressure.setMinRange(0, True)
8pressure.setUnits('Pa')
9
10heatflux.setUnits('W / m^2 / K')
11
12velocity.setNumberOfRequiredValues(2)
13velocity.setUnits('m / s')
14velocity.setDefaultValue(0.)
Now that our item definitions are constrained, we can create attributes from the definitions.
Simulation Preparation¶
The previous steps prepared a template that we can now use to create input decks for multiple simulations. The first step in creating input decks is to instantiate attributes based on the definitions above:
1fluidIC = ares.createAttribute('fluidIC', fluidICDef)
2wallBC = ares.createAttribute('wallBC', wallBCDef)
3inletBC = ares.createAttribute('inletBC', inletBCDef)
4outletBC = ares.createAttribute('outletBC', outletBCDef)
5matProp = ares.createAttribute('fluid', matDef)
When you ask the Resource to create an Attribute, it provides an Item to match every ItemDefinition in the attribute’s underlying Definition. These items are initialized to their default values (when a default has been provided) and keep track of whether they have been changed or not. We will change the values of these attributes soon, but first let’s consider their relationship to the model geometry.
In the simplest workflow, you will already have a geometric model of the simulation domain(s) and the domain boundaries with the relevant groups of vertices, edges, faces, and/or volumes named for use in applying boundary and initial conditions. Here, we read in an SMTK model session assuming that these groups already exist with names that we know:
1# Read in an SMTK-native B-Rep model:
2# TODO: Replace with resource.readModel()
3jsonFile = open(modelFileName, 'r')
4json = jsonFile.read()
5smtk.model.SessionIOJSON.loadModelRecords(json, mmgr)
6
7# Now find groups corresponding to IC/BCs:
8models = mmgr.findEntitiesByProperty('name', 'Test Model')
9model = smtk.model.Model(models[0])
10groups = model.groups()
11if groups and len(groups):
12 wallGroup = next((g for g in groups if g.name() == 'wall'))
13 inletGroup = next((g for g in groups if g.name() == 'inlet'))
14 outletGroup = next((g for g in groups if g.name() == 'outlet'))
15 fluidGroup = next((g for g in groups if g.name() == 'fluid'))
16
17 fluidIC.associateEntity(fluidGroup)
18 outletBC.associateEntity(outletGroup)
19 inletBC.associateEntity(inletGroup)
20 wallBC.associateEntity(wallGroup)
Now we can loop over the parameters we wish to study and create an ensemble for sensitivity analysis.
1# FIXME: Actually put this inside a loop that exports input decks.
2matProp.findDouble('viscosity').setValue(1.002e-3) # [Pa * s]
3
4fluidIC.findDouble('temperature').setValue(25) # [C]
5fluidIC.findDouble('velocity').setValue(0, 0.) # [m / s]
6fluidIC.findDouble('velocity').setValue(1, 0.) # [m / s]
7
8outletBC.findDouble('pressure').setValue(101300) # [Pa]
9
10inletBC.findDouble('velocity').setValue(0, 0.25) # [m / s]
11inletBC.findDouble('velocity').setValue(1, 0.00) # [m / s]
12inletBC.findDouble('temperature').setValue(50) # [C]
13
14wallBC.findDouble('heatflux').setValue(1.0) # [W / m^2 / K]
Bridge a new modeling kernel¶
This tutorial covers how to session a solid modeling kernel to SMTK. The details will vary according to the capabilities of the modeling kernel you wish to use via SMTK, but the overall process of bridging the kernel involves
subclassing SMTK’s Session class — which is used as a session between the modeling kernel and SMTK’s model manager;
defining a map between your kernel’s modeling entities and SMTK UUIDs;
transcribing information about kernel modeling entities into an SMTK model manager; and
providing SMTK operators which perform modeling operations. The only mandatory operator is a “read” operator used to load a file native to your modeling kernel into your native kernel’s modeling session.
This tutorial will add a simplistic session type that presents an Exodus mesh as a model composed only of groups (element blocks, side sets, and node sets). A session like this is useful for cases where the geometry for a simulation has been completely prepared and SMTK is only being used to attach attributes to pre-existing subsets of the geometric model. The groups themselves have a tessellation (i.e., a graphical representation) based on the cells they contain but the groups do not expose any of these cells to SMTK.
Our example session uses VTK’s Exodus reader to load files and obtain a tessellation for each element block, side set, and node set.
Adding Entity UUIDs¶
The first step in adapting foreign modeling kernels to SMTK, which you should do before you start subclassing Session, is deciding how to assign UUIDs to entities in the foreign modeling kernel so that
entities can persist across SMTK modeling sessions and
SMTK can identify entities to the modeling kernel given only a UUID (for instance, to specify inputs to a modeling kernel operation).
There are two ways to go about storing and maintaining this bidirectional map between modeling kernel entities and SMTK UUIDs:
If the modeling kernel provides an attribute resource, then the UUIDs can be stored as attributes on entities. Note that it is important to verify that attributes can be stored on all the entities you wish SMTK to be able to track. For instance, OpenCASCADE does not provide a way to store attributes on “use” records or on loops and shells. This means we must use another method if we want to preserve UUIDs assigned to these entities.
If the modeling kernel provides a “stable” order for traversing model entities, the mapping can be reduced to storing one or more sequences of UUIDs in an SMTK model file. If you construct this file so that it also saves session information, it can be used to “restore” a modeling session so that the same files are loaded on the server and the UUIDs preserved.
The VTK session follows the first approach and
expects UUIDs to be saved as field-data
on each data object to be represented in SMTK.
To accelerate lookups of UUIDs,
the VTK session stores the UUID as a string value
on each vtkDataObject
’s information object using the SMTK_UUID_KEY
key.
It uses a method provided by SMTK’s VTK-based rendering
support to fetch and store UUIDs so that picking can be
coordinated with VTK.
smtk::common::UUID Session::uuidOfHandleObject(vtkDataObject* obj) const
{
smtk::common::UUID uid;
if (!obj)
{
return uid;
}
uid = vtkResourceMultiBlockSource::GetDataObjectUUID(obj->GetInformation());
if (!uid)
{ // We have not assigned a UUID yet. Do so now.
uid = smtk::common::UUIDGenerator::instance().random();
vtkResourceMultiBlockSource::SetDataObjectUUID(obj->GetInformation(), uid);
}
return uid;
}
The advantage to the first approach is that modeling kernels with attribute resources generally provide a way to preserve attributes across modeling operations. When using the latter method, there is no robust way to track entities across modeling operations that change the topology of the model.
Adding UUIDs by either technique¶
Regardless of the path you take above, your session should provide
public methods to map both directions.
The function mapping UUIDs to foreign entities will have a
return type that is specific to your modeling kernel,
as will the input parameter of the inverse method that
returns a UUID given a foreign entity;
for our example, we’ve created a new type named EntityHandle
.
/// The types of entities in a VTK "model"
enum EntityType
{
EXO_MODEL = 0x01, //!< An entire VTK dataset (a file).
EXO_BLOCK = 0x02, //!< VTK BLOCKs are groups of cells inside a MODEL.
EXO_SIDE_SET = 0x03, //!< VTK SIDE_SETs are groups of cell boundaries in a MODEL.
EXO_NODE_SET = 0x04, //!< VTK NODE_SETs are groups of points in a MODEL.
EXO_BLOCKS = 0x05, //!< A group of VTK BLOCKs.
EXO_SIDE_SETS = 0x06, //!< A group of VTK SIDE_SETs.
EXO_NODE_SETS = 0x07, //!< A group of VTK NODE_SETs.
EXO_LABEL_MAP = 0x08, //!< A dataset with a label-map array
EXO_LABEL = 0x09, //!< A dataset representing one label in a label-map array
EXO_INVALID = 0xff //!< The handle is invalid
};
SMTKVTKSESSION_EXPORT std::string EntityTypeNameString(EntityType etype);
/// A "handle" for a VTK entity (file, block, side set, or node set)
struct SMTKVTKSESSION_EXPORT EntityHandle
{
int m_modelNumber{
-1
}; //!< An offset in the vector of models (m_models) owned by the session, whose model owns m_object.
vtkSmartPointer<vtkDataObject> m_object; //!< The dataset being presented as this entity.
SessionPtr m_session; //!< The session owning this entity.
EntityHandle();
EntityHandle(int emod, vtkDataObject* obj, SessionPtr sess);
EntityHandle(
int emod,
vtkDataObject* obj,
vtkDataObject* parent,
int idxInParent,
SessionPtr sess);
bool isValid() const;
EntityType entityType() const;
std::string name() const;
int pedigree() const;
bool visible() const;
int modelNumber() const { return this->m_modelNumber; }
EntityHandle parent() const;
template<typename T>
T* object() const;
template<typename T>
T childrenAs(int depth) const
{
T container;
this->appendChildrenTo(container, depth);
return container;
}
template<typename T>
void appendChildrenTo(T& container, int depth) const;
bool operator==(const EntityHandle& other) const
{
return this->m_session == other.m_session && this->m_object == other.m_object &&
this->m_modelNumber == other.m_modelNumber;
}
bool operator!=(const EntityHandle& other) const
{
return this->m_session != other.m_session || this->m_object != other.m_object ||
this->m_modelNumber != other.m_modelNumber;
}
};
Each EntityHandle
instance stores information about a model
or group that should be presented in SMTK:
m_session
, a pointer to the session owning the model or group;m_modelNumber
, an integer offset into the session’s list of top-level models (this indicates the model that owns the data object); andm_object
, a pointer to a VTK dataset holding the corresponding model geometry.
The session also holds a map to identify the parent model or group of each VTK data object since multiblock datasets in VTK do not provide a way to discover the parent of a dataset (only its children).
Adding UUIDs as attributes¶
Although we do not provide example code in this tutorial, it should be straightforward to add UUIDs to a modeling kernel’s attribute resource either as a 16-byte binary blob or an ASCII string per entity.
For example, if we wished to make VTK points and cells available
via a session, we could store UUIDs on VTK grids as point and cell data arrays.
It would be more space-efficient to store these in a 2-component
vtkTypeUInt64Array
(2 components for a total of 128 bits per UUID),
but much easier to debug if we store UUIDs in vtkStringArray
instances (one for points, one for cells).
VTK provides the concept of pedigree IDs for mapping points and cells from inputs to corresponding outputs, so (for filters that support pedigree IDs) VTK behaves much like an attribute resource in a geometric modeling kernel.
If we ever expose individual points and cells in the SMTK VTK session, we could search for point- and cell-data arrays containing UUIDs. Currently, we only search for field data specifying the UUID of a dataset as shown above.
Although not required by this example, you should be aware that you may store information about a particular session instance in an SMTK JSON file by subclassing the SessionIOJSON class.
Adding UUIDs as sequences¶
If you must store UUIDs in an SMTK JSON file according to some stable traversal order, then you should
store the arrays in your session class in the proper order and use them to perform the lookups.
write from_json and to_json functions for your resource that serialize information beyond what native file formats can accommodate, including UUIDs. The VTK session illustrates how to do this but does not currently export any session information.
Summary¶
You should now have a way to map a foreign entity to its SMTK UUID and vice-versa. If there is any possibility that the model could change, then you are responsible for maintaining this mapping (or at least marking it dirty and rebuilding as required).
Note that with either approach it is possible to incrementally assign UUIDs to entities as the need arises (though not very efficiently when storing UUIDs by traversal order). Future versions of SMTK will focus on incremental transcription of model entities to avoid significant waits the first time a model is read.
Now that you understand how UUIDs will be stored and related to foreign modeling kernel entities, it is possible to subclass the SMTK model session class.
Creating a session subclass¶
Sessions exist to link foreign modeling entities to SMTK modeling entities, in a bidirectional way:
we transcribe foreign modeling entities into an SMTK model resource, and
we perform operations in SMTK that make changes in the foreign modeling kernel (and then result in more transcriptions to update SMTK’s model resource).
Only the first of these is needed for read-only access so we will cover it first and then describe the interactions between sessions and operators. Implementing operators is the topic of a separate tutorial.
The first thing you must do when creating your own session is to implement a subclass of smtk::model::Session:
In the example above, some methods override the base class in order to provide required functionality while others just illustrate useful ways to divide tasks that should be common to most session types.
The first block of methods near the top of the declaration are required in order for instances of the session to be created and introspected by SMTK.
The
typedef smtk::shared_ptr<Session> Ptr
is useful for referencing shared pointers to the session internally.The
typedef smtk::model::SessionInfoBits SessionInfoBits
is not required but will make implementing methods dealing with transcription of entities easier to type.The
smtkCreateMacro
macro is required in order for instances of the object to be created.The virtual destructor should always be implemented so that the base class destructor is called.
Finally, the
allSupportedInformation
method exists so that SMTK can discover what types of information the session can provide to SMTK. The returned integer should be a bitwise OR of entries from the SessionInformation enum. For now, it is fine to return SESSION_EVERYTHING.
The next step is to provide methods to access the maps between SMTK
and foreign entities (in this case, Exodus element blocks, side sets,
and node sets).
The toEntity
and toEntityRef
methods do this and will
be discussed in more detail in the next section.
Depending on your modeling kernel, you may use an existing type
from the foreign modeler (like the OpenCASCADE session does) or a new class
like the EntityHandle
class in our example.
Now that we have defined a mapping between UUIDs and model entities, the next step is to have the session transcribe information about foreign model entities into a model resource instance.
Transcribing model entities¶
The main purpose of the session is to provide the SMTK model Manager which owns it with information about the entities in some foreign modeler and the Session::transcribeInternal() method is where your session must do this.
The first thing you should do is verify that the entity being requested actually exists:
One trick you’ll see in most sessions is the construction of a “mutable” entityref
from the const version that passed to transcribeInternal
:
The const version does not provide access to methods that alter the model manager’s storage. Constructing a mutable version of the entityref is legitimate inside the session — we are pretending that the entity to be transcribed has existed in the manager ever since it existed in the foreign modeler. Since transcription should not change the entity in the foreign modeler, creating a mutable entityref is acceptable.
Once you know that the UUID has a matching entity in the foreign modeler, you should create an Entity instance in the model manager’s map from UUIDs to entities. Make sure that the entity’s flags properly define the type of the entity at this point but don’t worry about filling in the list of relations to other entities unless the SESSION_ENTITY_RELATIONS bit is set in the request for transcription.
If SESSION_ENTITY_RELATIONS is set, then you not only need to ensure that the relations are listed but that they are also at least partially transcribed. You are always welcome to transcribe more than is requested, but be aware that this can slow things down for large models. Because this is an example and because the Exodus session does not expose individual cells as first-class entities, we transcribe the entire model recursively.
Now you should check other bits in SessionInformation that are present in your Session::allSupportedInformation() method and ensure that information is transcribed properly before returning the bits which were actually transcribed for the given entity.
The Session::transcribe() method uses the return value to update its list of dangling (partially transcribed) entities.
Providing operators¶
Finally, if you wish to allow modeling operations, your session must provide Operator definitions that invoke the underlying modeling kernel.
Implementing an STMK operator¶
Contents
SMTK allows you to write operators that expose those provided by an underlying modeling kernel as well as operators that provide new functionality.
This tutorial will cover writing an operator that provides new functionality: specifically, we will create an operator that counts the number of top-level cells in a model. Given a non-default option, it may instead count the number of top-level groups in a model.
Operators in SMTK consist of 3 components:
a class that implements the action of the operator, as a subclass of smtk::model::Operator.
an SMTK attribute definition that describes the parameters the operator accepts (both required and optional)
an SMTK attribute definition that describes information returned by the operator.
The sections below detail each of these.
Subclassing smtk::model::Operator¶
We will name our example operator the CounterOperator and place it in the “ex” example namespace.
1namespace ex
2{
3
4class CounterOperation : public smtk::operation::XMLOperation
5{
6public:
7 smtkTypeMacro(ex::CounterOperation);
8 smtkCreateMacro(CounterOperation);
9 smtkSharedFromThisMacro(smtk::operation::XMLOperation);
10 smtkSuperclassMacro(Operation);
11 // ...
Our operator is a subclass of smtk::model::Operator, which
is managed using shared pointers (it uses the smtkEnableSharedPtr macro
to inherit enable_shared_from_this
),
so we use the smtkCreateMacro to provide a static method for
creating a shared pointer to a new instance and
the smtkSharedFromThisMacro to override the base
class’ shared_from_this()
method.
Beyond these basic requirements, an operator should implement two inherited virtual methods
ableToOperate which is an opportunity for an operator to perform checks on the validity of input parameters that cannot be easily encoded using the attribute resource; and
operateInternal which implements the actual behavior of the operator.
Because our operator is simple, we implement smtk::operation::Operation::ableToOperate() in the class header file. We only declare smtk::operation::Operation::operateInternal:
1protected:
2 Result operateInternal() override;
3 const char* xmlDescription() const override;
By calling Operator::ensureSpecification in Operator::ableToOperate, we force the attribute resource to build an attribute instance which holds specifications for each parameter of this instance of the Operator. Then in Operator::operateInternal we can refer to the specification without having to verify that it is non-null; an operator’s Operator::operateInternal method will never be called unless Operator::ableToOperate returns true.
The attribute constructed by Operator::ensureSpecification is built using XML definitions we provide. We will cover the format of the XML definitions immediately below and then continue with the implementation of the operation.
Defining operator input parameters¶
The XML we used to declare the operator’s parameters and results uses SMTK’s attribute definition system. All operators should be derived definitions whose base definition (BaseType) is “operator”:
1 <include href="smtk/operation/Operation.xml"/>
2 <AttDef Type="counter" BaseType="operation">
3 <ItemDefinitions>
4 <Component Name="model" NumberOfRequiredValues="1">
5 <Accepts><Resource Name="smtk::model::Resource" Filter="model|anydim"/></Accepts>
6 </Component>
7 <Int Name="count groups" NumberOfRequiredValues="1">
8 <DefaultValue>0</DefaultValue>
9 </Int>
10 </ItemDefinitions>
11 </AttDef>
Inheriting the base definition allows us to use to attribute system to easily enumerate the list of all operators.
Also, you can see in the example that our operator takes a single integer parameter named “count groups”. Its default value is 0, indicating that top-level cells (not groups) will be counted by default. We could also have made “count groups” a VoidItem and tested for whether the attribute was enabled instead of testing its value.
In the future, operators will have their “primary” operand expressed as an association: model topology to serve as the primary operand will be associated with an instance of the operator rather than declared as an item owned by the operator attribute. This will simplify use cases where an active selection exists and the user wishes to perform an operation on it; operators that can be associated with the selection will be enabled (while others will be disabled). Any further parameters may be specified after the user initiates the operation.
Defining operator output parameters¶
The XML description of an operator is not complete until both the input and output parameters have been specified. The output parameters are expressed as another smtk::attribute::Attribute instance, this time defined as by inheriting the “result” BaseType. The result base type includes an integer item named “outcome” use to store one of the values in the OperatorOutcome enum. The outcome indicates whether the operation was successful or not.
1 <!-- Result -->
2 <include href="smtk/operation/Result.xml"/>
3 <AttDef Type="result(counter)" BaseType="result">
4 <ItemDefinitions>
5 <Int Name="count" NumberOfRequiredValues="1">
6 </Int>
7 </ItemDefinitions>
8 </AttDef>
In addition to the outcome, our edge-counting operator also returns the number of edges it counted. Often, SMTK operators will return lists of new, modified, or removed model entities in one or more smtk::attribute::ModelEntityItem instances.
Both the input and output XML are typically maintained together in a single XML file.
Implementing the actual operation¶
Now that we have input and output parameters specified, the implementation of Operator::operateInternal can simply fetch items from an instance of an attribute defined by the XML:
1smtk::operation::XMLOperation::Result CounterOperation::operateInternal()
2{
3 // Get the attribute holding parameter values:
4 auto params = this->parameters();
5
6 // Get the input model to be processed:
7 Model model = params->findComponent("model")->valueAs<smtk::model::Entity>();
8
9 // Decide whether we should count cells or groups
10 // of the model:
11 int countGroups = params->findInt("count groups")->value();
12
13 // Create the attribute holding the results of
14 // our operation using a convenience method
15 // provided by the Operation base class.
16 // Our operation is simple; we always succeed.
17 auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED);
18
19 // Fetch the item to store our output:
20 smtk::attribute::IntItemPtr cellCount = result->findInt("count");
21
22 cellCount->setValue(
23 countGroups ? static_cast<int>(model.groups().size()) : static_cast<int>(model.cells().size()));
24
25 return result;
26}
Note that the base class provides a method to create a result attribute for you with the “outcome” parameter set to a value you specify.
In addition to implementing the operation, the only other thing you must do is register the operator with the proper session. This is done using the smtkImplementsModelOperator macro:
1// Implement virtual overrides from base class to handle registration.
2const char* CounterOperation::xmlDescription() const
3{
4 return implement_an_operator_xml;
5}
Warning
This macro must always be invoked in the global namespace and your operator’s class name fully qualified with the namespace in which it lives.
This macro is very important because it ties several names together:
the C++ class name (
ex::CounterOperator
),the names and XML descriptions of the attribute definitions for input parameters (“counter”) and result parameters “result(counter)”, and
the SMTK component name used by the autoinitialization facility to force components (such as this operator) to be registered when initializing static variables. In this case the component name “ex_counter” can be used with the smtkComponentInitMacro() by adding this line:
smtkComponentInitMacro(smtk_ex_counter_operator)
to a compilation unit containing code that will be run by your application. Whenever the compilation unit’s static variables are initialized, the operator will be registered with the session class and any sessions constructed afterwards will provide the operator.
Note that “smtk_” and “_operator” wrap the name you pass to the smtkImplementsModelOperator macro.
To make maintaining the XML simpler, SMTK provides a macro for
encoding an XML file as a variable in a C++ header.
The variable implement_an_operator_xml
is generated in
CMakeLists.txt by
and then used by including the resulting file in your C++ implementation:
1// Include the encoded XML describing the operator class.
2// This is generated by CMake.
3#include "implement_an_operator_xml.h"
Create a basic project¶
This example describes how to create a basic SMTK project, add
an attribute resource to it, and write the project to disk.
A complete/working source file is included in the SMTK source repository,
as file smtk/doc/tutorials/create_a_project/create_a_project.cxx
.
Snippets from that source file are described below.
Initialize SMTK Managers¶
To use SMTK projects, you first create SMTK resource manager,
operation manager, and project manager instances, and then
register the various SMTK features. In this example, we will be
creating an attribute resource, so we also register the SMTK
attribute
feature to the resource and operation managers.
1 // Initialize SMTK managers
2 smtk::resource::ManagerPtr resManager = smtk::resource::Manager::create();
3 smtk::project::Registrar::registerTo(resManager);
4
5 smtk::operation::ManagerPtr opManager = smtk::operation::Manager::create();
6 smtk::operation::Registrar::registerTo(opManager);
7 opManager->registerResourceManager(resManager);
8
9 smtk::project::ManagerPtr projManager = smtk::project::Manager::create(resManager, opManager);
10 smtk::project::Registrar::registerTo(projManager);
11
12 // Register SMTK attribute feature
13 smtk::attribute::Registrar::registerTo(resManager);
14 smtk::attribute::Registrar::registerTo(opManager);
Register and Create “basic” Project¶
SMTK projects are registered by a type (string) that is registered
to the project manager. In the simplest case, you can register a project
type with just a string, but you can also register the string plus a
subclass of smtk::project::Project
for more advanced applications.
Although any project type can be used, for this example we deliberately
chose to register “basic” because the CMB modelbuilder application also
registers that type by default. As a result, the project saved in this
example can be loaded into modelbuilder. Other project types require
updating modelbuilder, typically via an SMTK plugin, to register the
project type.
1 // Create project with type "basic", a generic type that can be loaded into modelbuilder by default.
2 projManager->registerProject("basic");
3 smtk::project::ProjectPtr project = projManager->create("basic");
Create Attribute Resource¶
Next we create a simple attribute resource for the project contents. Standard practice would use the SMTK ImportResource or ReadResource operations to load attribute and other resources, but in order to implement this example as a single source file, an inline string is used for the attribute template. Because the template is manually generated, we also check the error flag returned when the string is read.
In the last line of this snippet, we also create a single attribute instance to display in the instanced view. Refer to the attribute resource documentation for more information about create attribute templates and instances.
1 // Create a small attribute resource
2 smtk::attribute::ResourcePtr attResource = resManager->create<smtk::attribute::Resource>();
3 const std::string attTemplate =
4 "<SMTK_AttributeResource Version=\"4\">"
5 " <Definitions>"
6 " <AttDef Type=\"Example\">"
7 " <ItemDefinitions>"
8 " <String Name=\"String Item\">"
9 " <DefaultValue>Yellow denotes default value</DefaultValue>"
10 " </String>"
11 " <Int Name=\"Integer Item\">"
12 " <DefaultValue>42</DefaultValue>"
13 " </Int>"
14 " <Double Name=\"Double Item\">"
15 " <DefaultValue>3.14159</DefaultValue>"
16 " </Double>"
17 " </ItemDefinitions>"
18 " </AttDef>"
19 " </Definitions>"
20 " <Views>"
21 " <View Type=\"Instanced\" Title=\"Example\" TopLevel=\"true\""
22 " FilterByAdvanceLevel=\"false\" FilterByCategory=\"false\">"
23 " <InstancedAttributes>"
24 " <Att Type=\"Example\" Name=\"example1\" />"
25 " </InstancedAttributes>"
26 " </View>"
27 " </Views>"
28 "</SMTK_AttributeResource>";
29
30 smtk::io::AttributeReader attReader;
31 smtk::io::Logger logger;
32 bool err = attReader.readContents(attResource, attTemplate, logger);
33 if (err)
34 {
35 std::cerr << "ERROR: " << logger.convertToString() << std::endl;
36 return -1;
37 }
38
39 // Create the example attribute instance
40 attResource->createAttribute("example1", "Example");
Add Attribute Resource to Project¶
Adding a resource to a project is a single API call, passing in then resource (shared pointer) and a string identified called “role”. For example, a casting simulation project might have mulitple attribute resources with roles such as “heatup specification”, “pour”, “solidifcation”, and mesh resources with roles such as “heat transfer mesh”, “induction heating mesh”, and “fluid flow mesh”.
1 // Add the attribute resource to the project, stting its project role to "attributes".
2 const std::string role = "attributes";
3 project->resources().add(attResource, role);
Write Project Resource¶
Because the SMTK project class is a subclass of the SMTK resource class,
The standard SMTK WriteResource operation can be used to serialize the
project to the file system. To do this, specify a project filename
with the standard “.smtk” extension. In this example, the operation
writes 2 files to disk, the specified project file (the filename
basic-project.smtk
is used in the code snippet) and a separate file
for the attribute resource that was added to the project. Project
resource files are written to a resources
subfolder. The filename
used for the attribute resource file is its rolename plus the “.smtk”
extension, in this case, attributes.smtk
. So for this example,
the outputs to the filesystem are:
working directory
|-- basic-project.smtk
|-- resources/
|---- attributes.smtk
The code to do this is the same as writing any SMTK resource.
1 // Write the project to the filesystem using the default WriteResource operation.
2 smtk::operation::WriteResource::Ptr writeOp = opManager->create<smtk::operation::WriteResource>();
3 writeOp->parameters()->associate(project);
4 writeOp->parameters()->findFile("filename")->setIsEnabled(true);
5 writeOp->parameters()->findFile("filename")->setValue("basic-project.smtk");
6 smtk::operation::Operation::Result writeResult = writeOp->operate();
7 int writeOutcome = writeResult->findInt("outcome")->value();
8
9 if (writeOutcome == static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED))
10 {
11 std::cout << "Wrote project to the current directory.\n"
12 << " This consists of file \"basic-example.smtk\" in the current directory\n"
13 << " which is the project's resource file, and file \"resources/attributes.smtk\"\n"
14 << " which is the attribute resource." << std::endl;
15 }
16 else
17 {
18 std::cout << "Failed to write project file, with outcome " << writeOutcome << ".\n";
19 std::cout << logger.convertToString() << std::endl;
20 return -1;
21 }
22
23 return 0;
Open in modelbuilder Application¶
As noted above, the CMB modelbuilder application is configured by default
to register SMTK projects of type “basic”. So you can use the modelbuilder
File => Open
menu to load the basic-project.smtk
file which loads
the entire project which, in this case, is just the single attribute
resource. When you do this, enable the “Attribute Editor” and “Projects”
views. An example the resulting display is show here. The “Projects” view,
in the lower part, shows a tree view of the projects currently loaded, in
this case, our basic project example. Above that is the “Attribute Editor”
for the simple attribute resource we created and included in the project.

Going Further¶
For extra credit, you can obtain this source file at
smtk/doc/tutorials/create_a_project/create_a_project.cxx
and
continue adding features, for example:
Replace the inline attribute with an external .sbt file.
Use the SMTK ReadResource operation to create the attribute resource from the .sbt file.
Load a model file and add that to the project.
Extend the attribute template to include model associations.
Release notes¶
SMTK 23.04 Release Notes¶
See also SMTK 23.01 Release Notes for previous changes.
Changes to SMTK’s Task Subsystem¶
User Interface for Task-based Workflows (Preview)¶
SMTK now provides a ParaView plugin which adds a task panel similar to ParaView’s node editor. Each task is shown as a node in a graph and may be made active, marked complete (or incomplete), and manually placed by the user. Each task may have a collection of style keywords associated with it and the task manager holds settings for these keywords that affect the application state when matching tasks are made active.
In particular, SMTK’s attribute-editor panel can be directed to display any view held by attribute resource when a related smtk::task::FillOutAttributes task becomes active.
This functionality is a preview and still under development; you should expect changes to user-interface classes that may break backwards compatibility while this development takes place.
Operation Hint for Switching the Active Task¶
Any operation can now request the active task be switched
by providing a hint on its result attribute.
Use the smtk::operation::addActivateTaskHint()
function
to add the hint before your operation completes.
Then, the pqSMTKOperationHintsBehavior object will
observe when the operation has completed and process the
hint and attempt to switch to the matching task.
See smtk::project::Read
for an example.
Task Management Changes¶
The task manager is no longer considered part of an application’s state. Instead of expecting an application’s Managers instance to hold a single task manager, each Project owns its own task manager.
As part of this change, the project read and write operations now include a serialization of the project’s task manager. This means that the task system JSON state is now properly serialized.
Another part of this change removes the smtk::task::json::jsonManager structure
with its serialize()
and deserialize()
methods. You should replace calls to
these methods with calls to to_json()
and from_json()
, respectively.
Furthermore, you are responsible for pushing an instance of the
task helper before these calls and popping the instance
afterward.
See the task tests for examples of this.
Finally, because smtk::common::Managers no longer contains an application-wide instance of a smtk::task::Manager, the signature for Task constructors is changed to additionally accept a parent task manager. The old signatures will generate compile- and run-time warnings. The constructors still accept a smtk::common::Managers since tasks may wish to monitor the application to determine their state.
SMTK 23.01 Release Notes¶
See also SMTK 22.11 Release Notes for previous changes.
SMTK Platform and Software Process Changes¶
CMake string encoding¶
The way SMTK encodes the contents of files into its libraries has changed in several regards.
Newly deprecated functions¶
The CMake functions in CMake/EncodeStringFunctions.cmake
(used by smtk_encode_file()
)
have been replaced by a C++ executable utilities/encode/smtk_encode_file.cxx
to allow
processing of large files efficiently. These functions are now deprecated.
If you use the functions in CMake/EncodeStringFunctions.cmake
, be aware that these
are deprecated and will be removed in a future version. If you have small files and
wish to use C++11 literals, be aware that you can no longer use the generated
headers with a C or C++ compiler unless it supports C++11 raw string literals.
Refactored function¶
The smtk_encode_file()
CMake macro now calls a C++ binary of the same name (whose source
is located in utilities/encode/
). The new utility can generate files containing
a python block quote (as before);
a C++11 raw string literal (rather than the previous, escaped C98 string constant); or
a C++11 function that returns a
std::string
(all new).
The latter change (C++11 function output) was made to accommodate encoding files larger than 64kiB on windows, which prohibits arbitrary-length literals. The CMake implementation of string splitting was rejected for performance reasons (CMake would deplete its heap space trying to process files more than a few megabytes).
The new macro and executable also introduce a change in behavior;
previously, if you invoked smtk_encode_file("foo/bar.xml" …)
inside the
subdirectory baz/xyzzy
of your project’s source, the generated file would
be at baz/xyzzy/bar_xml.h
. Now the path to the generated file will be
baz/xyzzy/foo/bar_xml.h
.
This pattern is used by SMTK for operations and icons; if your external
library provides these, you will need to adjust your include directives.
Removal of previously deprecated functions¶
Finally, the deprecated CMake functions smtk_operation_xml
and smtk_pyoperation_xml
from CMake/SMTKOperationXML.cmake
have been removed.
SMTK Common Changes¶
More path helper functions¶
The smtk::common::Paths class now provides two additional helper functions:
areEquivalent()
to test whether two paths resolve to the same location on the filesystem; andcanonical()
to return the absolute, canonical (free of symbolic links,.
, and..
) version of a path on the filesystem.
SMTK Attribute Resource Changes¶
SMTK Graph Resource Changes¶
SMTK I/O Changes¶
Added the ability to “Export” ItemBlocks in Attribute Template Files¶
Previously ItemBlocks were file-scope only. This change extends ItemBlocks so that an ItemBlock defined in one file can be consumed by another file that includes it. To maintain backward compatibility, an ItemBlock that is to be exported must include the Export XML attribute and be set to true. In order to better organize ItemBlocks, SMTK now supports the concept of Namespaces.
Note Namespaces are only used w/r ItemBlocks and can not be nested. To indicate an ItemBlock is defined with an specific Namespace NS, you will need to use the Namespace XML attribute. As a shortcut, if all of the ItemBlocks in a file are to belong to the same Namespace, you can specify the Namespace XML attribute at the ItemBlocks level as well. If no Namespace is specified, SMTK assumes the ItemBlock belongs to the global Namespace represented by “”.
SMTK Operation Changes¶
SMTK Markup Resource Changes¶
Markup resource ontology support¶
A new smtk::markup::ontology::Source class supports registration
of ontologies; plugins can invoke its static registerOntology()
method and pass in a simple object that provides ontology metadata.
This is done rather than providing operations which create nodes for
all the identifiers in the ontology since some ontologies can be
quite large (Uberon is a 90+ MiB OWL file as of 2022-12-01) and
frequently only a few identifiers from the ontology will be used
by any particular resource.
The smtk::markup::TagIndividual operation has significant new functionality for editing tags, including querying any registered ontology object for identifiers by name and creating instances of them. The operation can now both add and remove ontology identifiers.
Finally, there is now a Qt item-widget – smtk::extension::qtOntologyItem used by the the updated TagIndividual operation to provide identifier completion and description information. If your application has a single ontology registered, the TagIndividual operation will automatically autocomplete with identifiers from this ontology. Otherwise you will need to subclass the operation to specify the name of the ontology to use for auto-completion.
Resource read bugs¶
The smtk::resource::json::jsonResource method had a bug where a resource’s location would be assigned its previous location on the filesystem (i.e., where it was written to rather than where it was read from). This behavior has been changed so that the location is only assigned when the resource’s pre-existing location is an empty string.
Read operations are expected to set the resource’s location to match the filename provided to them. The smtk::markup::Read operation in particular has been fixed to do this so that relative paths to geometric data are now resolved properly.
Finding markup components by name¶
Finding components in a markup resource by name is now supported.
This also implicitly supports searches by both type and name since the output container type is templated and objects will be cast to the container’s value-type before insertion.
Currently, output containers must hold raw pointers to nodes rather than weak or shared pointers to nodes. This may change in future versions.
SMTK 3D Widget Changes¶
Removal of Deprecated Code¶
All code that was deprecated before prior to SMTK 22.11 has been removed.
Important Bug Fixes¶
Fixing qtGroupItem::updateItemData Method¶
Method was not properly setting the Contents Frame visibility properly. This has now been corrected.
Fixes for ReferenceItem widgets¶
Neither smtk::extension::qtReferenceItem
nor smtk::extension::qtReferenceTree properly called
their owning-view’s valueChanged()
method when modified.
Now they do. If you had to work around this issue before, be
aware that you may have to undo those workarounds to avoid
double-signaling.
Sort newly-created phrases¶
The default PhraseModel implementation
of handleCreated()
did not sort phrases to be inserted. This has been
corrected, but if your subphrase generator does not expect phrases to be
sorted by object-type and then title, this change will introduce improper
behavior and you will need to subclass this method to respond appropriately.
SMTK 22.11 Release Notes¶
See also SMTK 22.08 Release Notes for previous changes.
SMTK Platform and Software Process Changes¶
Type-name now reported consistently across platforms¶
The smtk::common::typeName() templated-function now returns
a string that is consistent across platforms. Previously, MSVC
compilers would prepend “class ``" (already fixed) or "``struct ``"
(newly fixed), unlike other platforms. They also report anonmyous
namespaces differently (
`anonymous namespace’`` vs (anonymous namespace)
.
These are now adjusted so that type names are consistent.
This is required for python bindings for the graph-resource, which
use the reported type names of arcs (which may be structs and may
live in anonymous namespaces) when editing the resource.
If you previously had code that special-cased MSVC type names, you should remove it.
Regex header wrapper¶
Add a new header to wrap the STL/Boost regex headers to ensure consistent usage of the STL or Boost regex libraries. To force using Boost::Regex the cmake configuration parameter SMTK_USE_BOOST_REGEX can be used.
SMTK Common Changes¶
Type container now uses string tokens as keys¶
Previously, smtk::common::TypeContainer indexed an
object of type Type
by typeid(Type`).hash_code()
.
However, on macos this caused issues where the hash code
varied depending on the library calling methods on the
type container. To fix this, the type container now
uses smtk::string::Hash ids (as computed by
constructing a string Token) of the typename. This is an
internal change and does not affect the public API of the
template class; no developer action should be required.
String tokens are now constexpr¶
The smtk::string::Token class, which stores the hash
of a string rather than its value, now includes a constexpr
hash function (the algorithm used is a popular one named “fnv1a”)
so that strings can be tokenized at compile time. Previously,
the platform-specific std::hash_function<std::string>
was
used.
This change makes it possible for you to use switch statements for string tokens, like so:
using namespace smtk::string::literals; // for ""_hash
smtk::string::Token car;
int hp; // horsepower
switch (car.id())
{
case "camaro"_hash: hp = 90; break;
case "mustang"_hash: hp = 86; break;
case "super beetle"_hash: hp = 48; break;
default: hp = -1; break;
}
The hash algorithm will generate hashes of type std::size_t
but only supports 32- and 64-bit platforms at the moment.
Note that because the string manager uses a serialization helper
to translate serialized hash values (this was previously required
since std::hash_function<>
implementations varied), reading
tokens serialized by a 32-bit platform on a 64-bit platform will
not present problems. However, reading 64-bit hashes on a 32-bit
platform is not currently supported; it may be in a future release
but we do not foresee a need for it.
Note that if a string is tokenized at compile time (i.e., by
using "string"_hash
instead of smtk::string::Token
’s
constructor), its string value will not be added to the
smtk::string::Manager instance and can thus not be
retrieved at run time unless some other piece of code adds it.
Instead a std::invalid_argument
exception will be thrown.
SMTK Attribute Resource Changes¶
Changes to Copying Attributes and Assigning Attributes and Items¶
The old smtk::attribute::Resource::copyAttribute method has been deprecated by a more flexible version that takes in three parameters:
The Attribute to be copied
A CopyAndAssignmentOption instance (this is a new class)
A smtk::io::Logger instance
Much of the attribute “assignment” logic has been moved from the method to the new smtk::attribute::Attribute::assign(…) method which as the same signature as the copyAttribute method.
Similarly, the original smtk::attribute::Item::assign method has also been deprecated by a version that takes in the following parameters:
The SourceItem whose values are to be assigned to the target Item
A CopyAndAssignmentOption instance (this is a new class)
A smtk::io::Logger instance
CopyAssignmentOptions class¶
This class represents three classes of Options:
Copy Options controlling how an Attribute gets copied
Attribute Assignment Options controlling how attribute values are assigned to another
Item Assignment Options controlling how item values are assigned to another.
copyUUID - If set, this indicates that copied attributes should have the same UUID as the original. Note : the copying process will fail if the copied attribute would reside in the same resource as the original.
copyDefinition - If set, this indicates that if the source attribute’s definition (by typename) does not exist in the resource making the copy, then copy the definition as well. This can recursively cause other definitions to be copied. Note : the copying process will fail if this option is not set and the source attribute definition’s typename does not exist in the targeted resource.
ignoreMissingItems - If set, this indicates that not all of the source attribute’s items must exist in the target attribute. This can occur if the target attribute’s definition is a variation of the source attribute’s. Note : the assignment process will fail if this option is not set and if not all of the source attribute’s items are not present in the target.
copyAssociations - If set, this indicates that the source attribute’s associations should be copied to the target attribute which will also take into consideration allowPartialAssociations and doNotValidateAssociations options.
allowPartialAssociations - Assuming that copyAssociations option is set, if the allowPartialAssociations ** is not set ** then all of the source’s associations must be associated to the target attribute, else the assignment process will return failure.
doNotValidateAssociations - Assuming that copyAssociations option is set, the doNotValidateAssociations hint indicates that if it possible to assign the association information without accessing the corresponding persistent object, then do so without validation.
ignoreMissingChildren - If set, this indicates that not all of the source item’s children items must exist in the target item. This can occur if the target item’s definition is a variation of the source item’s. Note : the assignment process will fail if this option is not set and if not all of the source item’s children items are not present in the target.
allowPartialValues - If set, this indicates that not all of the source item’s values must be copied to the target item. If this option ** is not set ** then all of the source item’s values must be copied, else the assignment process will return failure.
ignoreExpressions - If set, this indicates that if a source Value item that have been assigned an expression attribute, it’s corresponding target item should be left unset.
ignoreReferenceValues - If set, this indicates that a target Reference item should not be assigned the values of the corresponding source item.
doNotValidateReferenceInfo - The doNotValidateReferenceInfo hint indicates that if it possible to assign a source Reference item’s values to a target item without accessing the corresponding persistent object, then do so without validation.
disableCopyAttributes - If set, this indicates that no attributes should be created when doing item assignments. An item assignment can cause an attribute to be created in two situations.
First - A source Value item is set to an expression attribute that resides in the same resource and the target item resides in a different one. In this case the default behavior is to also copy the expression attribute to the target item’s resource and assign the copied attribute to the target item.
Second - A source Reference item refers to an attribute that resides in the same resource and the target item resides in a different one. In this case the default behavior is to also copy the referenced attribute to the target item’s resource and assign the copied attribute to the target item.
Attribute update manager¶
The attribute system now has an update manager to aid you in migrating resources from one schema version (i.e., template) to another. See the Update factory update factory documentation for the basic pattern used to register handlers for items, attributes, or even whole resource types.
As part of this change, each attribute resource now has
a templateType()
and a templateVersion()
method
to identify the schema from which it is derived.
The values are provided by the SimBuilder Template (SBT) file
used as the prototype for the resource.
Workflow designers should update template files with
a template type and version in order to support future
migration.
Reference item API addition¶
Now smtk::attribute::ReferenceItem provides a numberOfSetValues()
method
that returns the number of non-null items.
Reference Item¶
A bug in smtk::attribute::ReferenceItem’s setNumberOfValues()
method
sometimes modified an internal reference to the next unset but allocated value
pointing to a location that was unallocated. The next call to append a value
would then fail as the default append location was out of range. This has been
fixed and no action is necessary on your part. If you were observing failed
calls to append items to a ReferenceItem (or ComponentItem/ResourceItem), this
may have been the reason.
Changes to ValueItem’s Expressions¶
Improving the support of Value Items whose Expression are in a different Resource.
Assigning a ValueItem to another now properly deals with this case
Copying a ValueItem Definition will also now properly support this use case.
SMTK Markup Resource¶
SMTK now provides a resource based on the smtk::graph::Resource for performing annotation tasks. It is not fully functional yet, but can import image and unstructured data, create analytic shapes, group geometric objects together, mark objects as instances in an ontology, and more. The objective of this resource is to allow more flexible conceptual models compared to the smtk::model::Resource. We are working to extend the ontology editing capability to allow users to edit arbitrary relationship arcs between objects in the resource. Please see SMTK’s Markup (Annotation) Resource for more details.
SMTK Graph Resource Changes¶
Graph resource arc-evaluators¶
The graph resource’s evaluateArcs<>()
method has
changed the way it invokes evaluators in two ways.
It will always pass a pointer (or const-pointer, depending on whether the resource is mutable) as an argument to your evaluator. The resource pointer is passed just before any forwarded arguments you provide to
evaluateArcs<>()
and is included when invoking your functor’sbegin()
andend()
methods. Note that the resource pointer will appear after the arc implementation object passed to your functor’s parenthesis operator.The graph resource now accepts functors with no
begin()
orend()
method; you need only provide a functor with a parenthesis operator.
In order to migrate to new versions of SMTK, you must change your
functors to accept this resource pointer. If you were already passing
the resource in, you may wish to remove the redundant argument and
modify places where you invoke evaluateArcs<>()
to avoid passing it.
Graph-resource Filter Grammar¶
The query-filter string-parser for the graph-resource had a bug where parsing would succeed with some incorrect grammars because the parser was not forced to consume the entire string to obtain a match; a partial match would succeed but not produce a functor that evaluated graph nodes properly. This has been fixed, so error messages should now be emitted when a filter-string is ill-formed.
SMTK Operation Changes¶
Operation-system Changes¶
We continue to identify and eliminate inconsistent behavior in asynchronous operations. At some point in the future, smtk::operation::Operation::operate() will either be removed or see its signature modified to no longer return an operation Result; the expected pattern will be for all users to launch operations at that point and use an observer or handler functor to examine the result (while resource locks are held by the operation).
In the meantime, we have introduced a new way to invoke an operation: rather than
calling operate()
, which returns a result, you should either launch an operation
or invoke safeOperate()
which accepts a functor that will be evaluated on the
result. The method itself returns only the operation’s outcome. This is done to prevent
crashes or undefined behavior (that can occur if you inspect the result without holding
locks on all the involved resources). The handler (if you pass one) is invoked before
the operation releases resource locks. Note that safeOperate()
blocks until the
operation is complete and should not be invoked on the main thread of a GUI application
since it can cause deadlocks.
Operation Hints¶
Operations can now provide hints to the application in their results instead of directly interacting with application state. Any operation-manager observers then have access to the hints and can choose how (or whether) to process them based on application state and user preferences. See the Operation Hints documentation for more details.
Coordinate transform editor¶
SMTK now provides an operation for editing coordinate transforms to be applied to any component with renderable geometry. It works by setting a smtk::resource::properties::CoordinateTransform property named smtk.geometry.transform on these components. The SMTK ParaView representation recognizes this property and renders components transformed.
The operation is named smtk::operation::CoordinateTransform and has a custom view that allows you to select a pair of coordinate frames (the “from” and “to” location and orientation of a rigid-body transform) that are already present as property values on any component. You can also create and edit transforms in place, although edited coordinate frame values are not currently saved – only the resulting transform is. In the future, the “from” and “to” coordinate frames will be saved along with the resulting transform to allow iterative editing across sessions.
Assign colors operation¶
Previously, the AssignColors operation lived in the smtk::model
namespace and could only be associated to model entities as it used
an API specific to model entities to set each component’s color.
This operation has been generalized to use the base resource’s
property API to set color and can thus be associated to any
persistent object. It now lives in the smtk::operation
namespace.
New Operation Groups for Grouping and Ungrouping¶
The operation subsystem of SMTK now has a smtk::operation::GroupingGroup and a smtk::operation::UngroupingGroup. Operations registered to these groups are expected to associate to group members or groups, respectively.
These groups will be used by ParaView extensions in the future to create and destroy groups visually rather than via the operation toolbox.
Property editor operation¶
SMTK now provides a freeform property editor named smtk::operation::EditProperties you can use to both inspect and edit integer, string, floating-point, and coordinate-frame properties on any component.
The custom operation view monitors the SMTK selection provided to the Qt UI manager for changes and updates the set of components being inspected/edited.
SMTK 3D Widget Changes¶
3-D ParaView Widget API Change¶
Two virtual methods in pqSMTKAttributeItemWidget now
return a bool
instead of void
:
updateItemFromWidgetInternal()
and updateWidgetFromItemInternal()
.
The boolean should indicate whether the method made any changes
(to the item or the widget, respectively).
In the former case, the return value determines whether to emit a modified()
signal (indicating the item has changed).
In the latter case, the return value determines whether to cause
the active view to be re-rendered (so that the widget is redrawn).
Previously, the methods above were expected to perform these tasks themselves
but now the base class uses this return value to eliminate redundant code.
Several 3-D widgets were missing an implementation
for updateItemFromWidgetInternal()
; this has been corrected.
Finally, when deleting 3-D widget items, a re-render of the active view is forced. You may have noticed some occasions where widgets were visible after deletion until the first redraw – that should no longer occur.
Cone-widget¶
The point-normal arrays created by the vtkConeFrustum did not have names; now they are named “normals”. If you had problems being unable to find these arrays by name before, it should now be fixed.
SMTK 22.08 Release Notes¶
See also SMTK 22.07 Release Notes for previous changes.
SMTK Operation Changes¶
Remove Resources via Operation¶
The Result attribute of Operation now includes a resourcesToExpunge item that tells the Operation base class to remove resources from the resource manager after operation observers have run, and before releasing resource locks, to avoid conflicts. The RemoveResource operation now uses this mechanism, and File .. Close Resource uses this operation instead of closing resources directly. The Result attribute also includes a removeAssociations optional item which can be enabled to remove the resource’s links to other resources, to prevent re-loading.
Any operations that remove resources should switch to this new pattern.
Deprecate ResourceManagerOperation¶
The ResourceManagerOperation sub-class is now redundant, replace any occurrences with XMLOperation.
SMTK Software Process Changes¶
Continuous integration¶
SMTK uses MSVC 2022 to test merge requests rather than MSVC 2019.
SMTK 22.07 Release Notes¶
See also SMTK 22.05 Release Notes for previous changes.
SMTK Common Changes¶
RGBA color conversion¶
Now smtk::common::Color includes methods to convert 4-tuples of floating-point numbers into hexadecimal color specifiers (in addition to the 3-tuples it already handles).
SMTK Attribute Resource Changes¶
Expanding SMTK Attribute Category Mechanism¶
Category Modeling in SMTK Attribute Resources has been enhanced to now support specialization as well as generalization. Previously, an Attribute or Item Definition’s local categories could only expand (make more general) the categories it was inheriting (or choose to ignore them all together). With this release, SMTK now supports specializing (or making the category constraint more restrictive).
Previously category inheritance was controlled by using the Definition’s setIsOkToInherit method which would either Or its local categories with those it was inheriting or Replace them. This method (as well as isOkToInherit method) has been deprecated. The new methods for setting and retrieving category inheritance are:
setCategoryInheritanceMode
categoryInheritanceMode
The values that categoryInheritanceMode can be set to are:
smtk::attribute::Categories::CombinationMode::Or
smtk::attribute::Categories::CombinationMode::And
smtk::attribute::Categories::CombinationMode::LocalOnly
Setting the mode to Or is the same as the previous setIsOkToInherit(true) - it will or the Definition’s local categories with those that it is inheriting. Setting the mode to And will now and the Definition’s local categories with those that it is inheriting. Setting the mode to LocalOnly will ignore the categories that the Definition is inheriting and is the same as the previous setIsOkToInherit(false).
Changing the Default Category Inheritance¶
Previously the default mode was isOkToInherit = true which now corresponds to smtk::attribute::Categories::CombinationMode::Or. Upon discussing the new combination support of And, it was decided that the new default will be smtk::attribute::Categories::CombinationMode::And, meaning that a Definition will be more specialized category-wise than it’s parent Definition.
Change on Enum Categories¶
Previously, categories on a Discrete Value Item Definition’s Enums would add their categories to the Definition. With this release it is assumed that the enums’ categories will be and’d with it’s Definition, there is no longer a reason for the enum categories to be combined with the Definition’s local categories.
File Version Changes¶
Supporting these changes did require a new format for both Attribute XML and JSON files. The latest versions for both is now 6. Older file formats will load in properly and should work based on the previous category rules.
Developer changes¶
The following methods and enums have been deprecated:
smtk::attributeCategories::Set::CombinationMode::Any -> please use smtk::attributeCategories::Set::CombinationMode::Or
smtk::attributeCategories::Set::CombinationMode::All -> please use smtk::attributeCategories::Set::CombinationMode::And
smtk::attribute::Definition::setIsOkToInherit -> please use smtk::attribute::Definition::setCategoryInheritanceMode
smtk::attribute::Definition::isOkToInherit -> please use smtk::attribute::Definition::categoryInheritanceMode
smtk::attribute::ItemDefinition::setIsOkToInherit -> please use smtk::attribute::ItemDefinition::setCategoryInheritanceMode
smtk::attribute::ItemDefinition::isOkToInherit -> please use smtk::attribute::ItemDefinition::categoryInheritanceMode
A new class for supporting the new combination modes has been developed called smtk::attribute::Categories::Stack which represents the category expression formed when combining inherited and local categories since we now need to maintain the order in which they are combined.
The method smtk::attribute::ValueItem:relevantEnums(bool includeCategories, bool includeReadAccess, unsigned int readAccessLevel) const was added in order to return the set of enums that passed the activce category and advance level checks (if specified).
SMTK QT Changes¶
qtItem changes¶
Several changes were made to the qtItem subclasses.
1. The new code hides the QLabel instance for an item if its label is set to a (nonempty) blank string. Template writers use a whitespace character to hide the label, however, a QFrame is still displayed and takes up horizontal space. The new code essentially removes that unwanted space.
2. The new code changes the Qt alignment for horizontal child-item layouts from vertical-center to top, for aesthetic reasons.
3. The new code updates the logic for setting the layout direction (horizontal or vertical) in qtInputsItem for various situations.
For ordinary value items, the default layout is horizontal, but can be overridden by an ItemView <View Layout=”Vertical”>.
For extensible items, the layout is always vertical, and cannot be overridden by an ItemView.
For discrete value items with children items, the layout is either:
horizontal - if each discrete value is assigned no child items or a single child item having a blank string for its label
vertical (otherwise)
The discrete item layout can be overridden by an item view, either <View Layout=”Horizontal”> or <View Layout=”Vertical”>.
Changes to qtInputsItem¶
A qtInputsItem instance will now call the Signal Operator when creating a new expression attribute.
SMTK ParaView Extensions Changes¶
Panels separated from DockWidgets¶
Previously, several Panel classes derived directly from QDockWidget, so they could be docked by the user in whatever arrangement was desired.
To allow re-use and rearrangement, these Panel classes now derive from QWidget, and are placed inside a pqSMTKDock<T> which derives from QDockWidget. pqSMTKDock<T> has a template parameter to allow it to create the child Panel of the correct type. Panel classes must now implement void setTitle(QString title) to provide the pqSMTKDock<T> with the correct title, and use setWindowTitle() to provide the initial dock window title.
ParaView resource panel¶
The pqSMTKResourcePanel class now asks any smtk::view::ApplicationConfiguration present for view configuration before using a default. This makes it simpler for applications to provide a custom phrase model, subphrase generator, or set of badges. (Before this change, applications would have to wait for the panel to become ready and then reconfigure it.)
Coloring renderable geometry¶
Before the property system was in wide use, the geometry subsystem expected component colors to be passed via a single-tuple field-data array on the renderable geometry. Support for this was broken when support for coloring by a floating-point property was added.
This commit fixes an issue (properly scaling floating-point colors when generating an integer-valued color array) and re-enables support for passing color by field-data. Support for passing color by entity property is preserved, but field-data arrays are preferred if present (because presumably the geometry backend added this array).
This feature is being revived to support components inheriting color from other components.
Resource panel search bar¶
A search bar has been added to the resource panel by default. Users may search for resource or components using a wildcard text-based search. The search bar is specified in the JSON config by adding “SearchBar”: true to the Attributes group.
Selection filtering for graph resources¶
A bug in the pqSMTKSelectionFilterBehavior class has been fixed. It prevented the default selection-responder operation from adding graph nodes to the selection (basically, anything that could not be cast to a model entity was rejected).
Now the selection filter toolbar buttons only apply to model and mesh entities; graph-resource components will always be passed through.
SMTK Graph Session Changes¶
Graph-resource Dump improvements¶
While smtk::graph::Resource’s dump() method is unchanged,
if you use resource->evaluateArcs<Dump>()
explicitly it is
now possible to set a color per arc type and to enable a
transparent background for the graph.
(This applies only to graphviz-formatted output.)
SMTK 22.05 Release Notes¶
See also SMTK 22.04 Release Notes for previous changes.
SMTK Resource Changes¶
New methods on smtk::resource::Resource¶
You can now ask a resource for raw pointers to components
via the components()
and the (templated) componentsAs<X>()
methods.
Use these methods only when
you have a read or write lock on the resource (i.e., you are inside an operation) so that no other thread can remove the component; and
you are sure that the component will not be removed from the resource for the duration of your use of the pointer (in the case where a write lock is held and components may be removed).
Note for developers¶
If you subclass smtk::resource::Resource, you should
consider overriding the components()
method to provide an
efficient implementation as it can improve performance as the
number of components grows large.
You may also wish to consider changing any subclasses of
smtk::resource::Component to refer back to their parent
resource using a smtk::common::WeakReferenceWrapper
and providing a method to fetch the raw pointer to the parent
resource from the weak-reference-wrapper’s cache.
This will improve performance when many components must be asked
for their parent resource as the overhead of locking a weak pointer
and copying a shared pointer can be significant.
However, note that any method returning a raw pointer will not
be thread-safe. This method is intended solely for cases where
a read- or write-lock is held on the resource and the algorithm
can guarantee a shared pointer is held elsewhere for the duration.
If you maintain an operation that needs to be performant with large numbers of components, consider using these methods to avoid the overhead of shared-pointer construction.
SMTK Attribute Resource Changes¶
Allowing Designers to Set the Default Name Separator for Attribute Resources¶
Added an API to set, get and reset the Default Name Separator used by the Attribute Resource when creating unique names for a new Attribute. Also added support in Version 5 XML Attribute Files as well as in JSON representations to save and retrieve the separator.
New C++ API Changes for smtk::attribute::Resource¶
/**\brief Get the separator used for new Attributes whose names are not unique
*/
const std::string &defaultNameSeparator();
/**\brief Reset the separator used for new Attributes whose names are not unique to to the default which is '-'.
*/
void resetDefaultNameSeparator();
/**\brief Set the separator used for new Attributes whose names are not unique
*/
bool setDefaultNameSeparator(const std::string& separator);
XML Formatting Additions¶
This example sets the Name Separator to ::
<SMTK_AttributeResource Version=”5” ID=”504c3ea1-0aa4-459f-8267-2ba973d786ad” NameSeparator=”::”> </SMTK_AttributeResource>
Attribute itemPath Method¶
Added “smtk::attribute::Attribute::itemPath” method that will return the full path string to the “item” parameter within the attribute. The default separator is “/” and can be changed as needed.
SMTK View Changes¶
Descriptive Phrase Model Change in Subphrase Generation¶
Previously, subphrases generation was done on demand as a performance optimization. Unfortunately, this made certain potential functionality such as ternary visibility difficult to implement at best. By forcing the Phrase Model to build all that it can in terms of its subphrases, it will now be possible to calculate and maintain a ternary visibility state.
SMTK QT Changes¶
Saving Collapsible Group View Box State¶
The View now stores its open/close state within its configuration so when it is rebuilt it knows if it should be open or closed.
Designers can also use this to set the initial state of the Group View.
The XML Attribute that controls this is called Open and is set to true or false.
Qt subsystem: badge click actions¶
Previously, the smtk::extension::qtDescriptivePhraseDelegate
generated badge clicks inside its editorEvent()
method.
However, that method is not provided with access to the view in
which the click occurred and thus could not access the view’s
QSelectionModel.
Now, each widget that uses the descriptive-phrase delegate
(smtk::extension::qtResourceBrowser,
smtk::extension::qtReferenceItem,
smtk::extension::qtReferenceTree) installs an
event filter on its view’s viewport-widget and passes
mouse clicks to qtDescriptivePhraseDelegate::processBadgeClick()
.
User-facing changes¶
Users will now see that:
clicks on badges in the widgets above will not change the Qt selection as they did previously,
clicking on the badge of a selected item in the widgets above will act upon all the selected items, and
clicking on the badge of an unselected item in the widgets above will only act on that item.
This behavior should be significantly more intuitive than before.
Developer notes¶
If you use qtDescriptivePhraseDelegate in any of your own classes, you cannot rely on it to handle badge clicks itself; instead you must install an event filter on your view-widget’s viewport (not the view widget itself) and pass mouse clicks to the delegate along with the view widget.
Added onDefinitionChange Signal to qtAttributeView¶
Added a signal called “onDefinitionChange” that will emitted when the user selects a definition from the definition combo box. Classes that inherit qtAttributeView can connect to this signal in order control the widget’s behavior based on the type of definition the user has selected.
qtGroupItem’s Modified Children¶
When the user modifies an item that is a child of a group item from the gui, the full path to that item is included in the result of the resulting Signal operation that will be run by the view.
qtInputsItem children displayed on right¶
The children of a discrete qtInputsItem widget are now displayed to the right of the combobox, rather than underneath the combobox. This change was made to decrease wasted space in the Attribute Panel, but also to remove the primary example case of a Qt-related bug that could squish the child input widgets when the parent was extensible.
Developer changes¶
This makes the Attribute Panel wider than it was before when a qtInputsItem with children is present (but also shorter). Although this isn’t deemed to be a problem, it’s something to be aware of.
User-facing changes¶
When a discrete value combobox had children, they used to display beneath the combobox. With this change, the children will now display to the right of the combobox. See here for more details.
SMTK ParaView Extensions Changes¶
Reorganization of extensions/paraview/appcomponents¶
To benefit client apps of smtk, the sources and resources in extensions/paraview/appcomponents/plugin-core have all been moved to the appcomponents directory and into the smtkPQComponentsExt library, so they are accessible outside smtk. The plugin has been split into three, with plugin-core retaining the auto-start and behaviors, and plugin-gui containing all the toolbars, and panels, and plugin-readers containing the importers and readers.
SMTK Graph Session Changes¶
Breaking changes to graph-resource arcs¶
The way SMTK represents arcs has changed. This is a breaking change to accommodate new functionality: the ability to provide or request indexing of both the “forward” arc direction (from, to) and the “reverse” arc direction (to, from).
Previously, the ArcMap class held arcs
in a TypeMap (a map of maps from
arc type-name to UUID to arc API object).
Now the ArcMap is a TypeContainer
(a simple map from arc typeid
hash code to arc API object).
The arc classes have also changed. If you previously did not inherit any of SMTK’s arc classes, you will need to adapt your own arc classes to expose new methods. See the graph-session documentation for the new method(s) you must provide. If you previously inherited Arc, Arcs, or OrderedArcs in your resource, these classes are removed. Instead, if you do not provide implementations of methods for accessing and manipulating arcs, the implementation will provide them for you by applying the ExplicitArcs template to your traits class.
For more details, see the updated documentation for the graph subsystem.
Plugin Changes¶
Make QT_NO_KEYWORDS optional for external plugins¶
External plugins can add the option ALLOW_QT_KEYWORDS to their use of smtk_add_plugin to skip the enforcement of QT_NO_KEYWORDS for the plugin. For example:
- smtk_add_plugin(
smtkAEVASessionPlugin ALLOW_QT_KEYWORDS PARAVIEW_PLUGIN_ARGS VERSION “1.0” …)
General Build Changes¶
Support for old hash namespace conventions dropped¶
SMTK now requires compilers to support specializations
of hash inside the std
namespace.
Previously, SMTK accepted specializations in non-standard
locations such as the global namespace, in the std::tr1
namespace, and others.
Developer notes¶
If your repository uses any of the following macros, use the replacements as described:
SMTK_HASH_NS
should be replaced withstd
SMTK_HASH_NS_BEGIN
should be replaced withnamespace std {
SMTK_HASH_NS_END
should be replaced with}
Any code enabled by
SMTK_HASH_SPECIALIZATION
should be unconditionally enabled and any code disabled by this macro should be removed.
SMTK 22.04 Release Notes¶
See also SMTK 22.02 Release Notes for previous changes.
SMTK Common Changes¶
SMTK QT Changes¶
Enable QT_NO_KEYWORDS builds¶
SMTK compile definitions now include QT_NO_KEYWORDS, which means all uses of emit, foreach, signals, and slots has been replaced with Q_EMIT, Q_FOREACH, Q_SIGNALS, and Q_SLOTS, and must be updated for any SMTK plugins that use the smtk_add_plugin cmake macro. This is to avoid conflicts with 3rd-party libraries, like TBB, which use the old keywords.
Added names to qt UI objects to allow for more repeatable automatic testing¶
Automated tests were not giving repeatable results due to unpredicatble Qt implicit naming of UI widgets. To avoid this, all UI widgets were manually assigned names using QObject::setObjectName()
Developer changes¶
It would be good to use QObject::setObjectName() for any new Items to avoid breaking this new “fully named” status.
User-facing changes¶
N/A
SMTK ParaView Extensions Changes¶
Added a Mechanism to hide labels for pqSMTKAttributeItemWidget¶
You can now suppress the item’s label from being displayed by simply setting the label value to be ” ” (note a single blank and not the empty string). As long as the item is not optional, its label will not be displayed.
3D Widget Visibility¶
3D widgets have an application-controlled setting that allows them to remain visible even if their Qt controls loose focus or are hidden. This overrides the ParaView default behavior of only allowing one 3D widget to be visible at a time.
Developer changes¶
By default, widget visibility behavior is not changed. To enable for an application, call pqSMTKAttributeItemWidget::setHideWidgetWhenInactive(false); on startup
User-facing changes¶
When this setting is enabled, multiple 3D widgets can appear in a renderview. Widgets that might overlap need a user control to hide the widget, to allow easier interaction. The last widget to be shown will receive precendence for mouse and keyboard events.
SMTK Representation Changes¶
The ParaView representation now renders components with coordinate-frame
transforms applied.
This is implemented using a new vtkApplyTransforms filter;
for resources without any transforms, this simplifies to an inexpensive
copy of the input blocks.
When transforms exists, VTK’s vtkTransformFilter
is used which can
be expensive for large geometry.
In the future, this cost may be avoided using a custom mapper.
Added the Ability to Render Geometry with Solid Cells¶
vtkResourceMultiBlockSource will now put Components with Solid Cells under its component Component Block
vtkApplyTransfors will now also do a surface extraction for components with solid cells
SMTK Project Changes¶
Made projects portable between different file systems and paths¶
Changed absolute file paths used in the project relative paths where necessary to allow the project folder to moved to a new location, or even another machine.
Developer changes¶
No new absolute paths should be added to the project. Any new paths that are saved should be saved as relative to the project main folder. Ideally, all files and folders that are part of a project should be contained within the main project folder.
User-facing changes¶
With this change in plase, projects can be freely moved on a given machine, or shared to a completely new machine in a seemless fashion.
SMTK Resource Changes¶
Expanded SMTK Properties to include sets of ints¶
You can now create a property that is represented as a set of integers
Resource subclasses and the move constructor¶
Because smtk’s smtk::common::DerivedFrom template, used when
inheriting Resource, declares a move constructor with the noexcept
attribute, all subclasses of Resource must explicitly declare a
move constructor with the noexcept
attribute. Modern versions of
clang are strict about checking this.
Improved smtk::resource::Metadata
¶
The smtk::resource::Metadata class constructor now requires create, read, and write functors which take an smtk::common::Managers instance as input so that creating, reading, and writing resources can make use of any available application-provided manager objects.
If you had any resource subclasses that provided these functors, you must update to the new signature. This is a breaking change.
Be aware that the operation and operation manager classes now accept an smtk::common::Managers instance. If provided to the operation manager, all operations it creates will have the managers object set (for use by operations). This is the preferred way for applications to pass information to operations. Using this method allows operations to be used in several applications with minimal dependencies on application-specific methods and structures.
Finally, methods on the vtkSMSMTKWrapperProxy
, vtkSMTKWrapper
, and
pqSMTKWrapper
classes that returned a TypeContainer&
have been
deprecated in favor of methods that return smtk::common::Managers::Ptr
so that operations can make use of the type-container’s contents without
copy-constructing a new Managers instance.
Coordinate Frame Property¶
SMTK’s resource system now provides resources with a property to store coordinate frames (an origin plus 3 orthonormal vectors specifying a change of basis). Besides the change of basis, each frame also holds an optional UUID of a parent component. If present and the parent also holds a matching coordinate frame, the two transforms may be concatenated.
Because properties are named, coordinate frames may take on
different roles.
SMTK currently only deals with one role:
when a coordinate frame is stored under the name transform
or smtk.geometry.transform
, it is taken to be a transformation
that should be applied to its associated component.
If the parent is unspecified, the coordinate frame transforms
the component’s geometry into world coordinates.
If a parent is specified, then it transforms the component’s
geometry into its parent’s coordinates (which may or may not
be further transformed).
Future alternative uses for coordinate frames include
Short alternative |
Formal alternative |
📦 |
Purpose |
---|---|---|---|
|
|
A coordinate frame relative to another component (or if none given, the world). |
|
|
|
The geometric center of a component. This may not always be identical to pca below if the component is defined as a surface discretization bounding a volume. |
|
|
|
The center of mass and principal axes (from computation of moments of inertia) that take a component’s volumetric density field into account. Because density is involved, this is not always identical to centroid. It is in some cases, such as if the density is uniform. |
|
|
|
The results of (p)rincipal (c)omponents (a)nalysis on the component’s geometry. This is not always identical to centroid (when the component’s geometry is a boundary representation) or center (when density is not uniform). PCA may also include “robust PCA,” in which case it may warrant a distinct property name. |
|
|
|
📦 |
A point of interest that also has directions of interest. |
|
|
📦 |
A coordinate system used as a reference in analysis or geometry construction. This is distinct from landmarks, which are intrinsic features of geometry as opposed to datum frames used to construct geometry. |
|
|
📦 |
An (o)riented (b)ounding (b)ox – non-unit-length vectors, but orthogonal. Axis lengths indicate bounds along each axis. |
|
|
📦 |
Denavit–Hartenberg parameters for screw transforms. Either z-axis length indicates screw pitch (non-unit length) or an additional property or subclass would need be required for the screw pitch. Note that there is an alternate formulation of DH parameters called “modified DH parameters” which might deserve an additional distinct name. |
|
|
📦 |
The location and orientation of a vector-field criticality (such as a source, sink, or saddle point), perhaps with additional information in other properties such as the rotational components about each axis. |
|
|
📦 |
A frame used to describe symmetric boundary conditions, solution periodicity, etc. Some extensions would be required to annotate more complex symmetries (cylindrical/spherical coordinate systems or n-way symmetry about an axis with n other than 2 or 4. |
The table uses
smtk.geometry for formal names of properties that might conceivably interact with the geometric data layer in SMTK (i.e., change what is rendered) and
smtk.markup for formal names of properties that are annotations that might have illustrations but not affect the underlying geometric representation of the component itself.
The “📦” column indicates whether the property would be used to hold a single coordinate frame or a collection of some sort (a fixed-size array, variable-length vector, set, map, etc.).
SMTK Graph Session Changes¶
Graph Arcs now support inverse relationships¶
Arcs may implement inverse relationships via the Inverse handler to seamlessly add/remove arcs as coupled pairs.
Developer changes¶
- Arc(s) Required APIs:
insert(ToType&,bool)
erase(ToType&,bool)
begin()
end()
Note, insert and erase take a second boolean argument that is used by the Inverse handler to break recursive insertion/removal of inverses.
The default behavior of operations for each type of arc type when inserted as the inverse are as follows.
Self Arc Type |
Inverse Arc Type |
Assign |
Insert |
Erase |
---|---|---|---|---|
Arc |
Arc |
Overwrite self, remove current inverse, insert new inverse. |
Insert inverse if valid to insert self, insert self if inverse successfully inserted. |
Unset self and erase inverse. |
Arc |
Arcs |
Overwrite self, remove current inverses, insert new inverse. |
Insert inverse if valid to insert self, insert self if inverse successfully inserted. |
Unset self and erase inverse. |
Arcs |
Arc |
Overwrite self, remove current inverses, insert new inverses. |
Insert self, if successful insert inverse, if inverse failed remove self and report failure |
Erase inverse and self. |
Arcs |
Arcs |
Overwrite self, remove current inverses, insert new inverses |
Insert self, if successful insert inverse, if inverse failed remove self and report failure. |
Erase inverse and self. |
OrderedArcs |
Arc |
Overwrite self, remove current inverses, insert new inverses. |
Insert inverse, if successful insert self. |
Erase inverse and self. |
OrderedArcs |
Arcs |
Overwrite self, remove current inverses, insert new inverses. |
Insert inverse, if successful insert self. |
Erase inverse and self. |
Arc or Arcs |
OrderedArcs |
Throw std::logic_error exception. |
Throw std::logic_error exception. |
Erase self and first matching inverse. |
OrderedArcs |
OrderedArcs |
Won’t compile. |
Won’t compile. |
Won’t compile. |
All behaviors can be overwritten by providing a specialization of smtk::graph::Inverse.
User-facing changes¶
The user should not see any major changes. Management of arc types with an inverse is now handled automatically so there may be some improvements to arc consistency.
SMTK View Changes¶
Fix Editing Descriptive Phrase Titles¶
When editing the title of a Descriptive Phrase of a Resource using the QT UI, the user is presented with what was being displayed which included the Resource’s name as well as with Resource’s location (in the case of the Resource Panel) or the Resource’s Project role and name (in the case of the Project Panel).
If the user didn’t initially clear the title, the original title (including the role or location information) was set as the Resource’s new name instead of just the name component.
This has been corrected so that when editing the title, the user is only presented with the Resource’s name. When editing is completed, the location/role information will be added properly.
Plugin Changes¶
ParaView Plugins¶
SMTK’s ParaView appcomponents
plugin has been split into 2 plugins and 2 new plugins have been added.
Where previously the smtkPQComponentsPlugin
held all functionality,
the operation panel has been moved into smtkPQLegacyOperationsPlugin
so that
applications based on SMTK may exclude it.
Likewise, the new operation toolbox and parameter-editor panels are in a new smtkPQOperationsPanelPlugin
.
Applications may include any combination of the operation-panel plugins.
Finally, a new ApplicationConfiguration
interface-class has been provided.
The toolbox panel waits to configure itself until an subclass instance is registered
with ParaView, at which point it is queried for a view-information object.
Use of this pattern is likely to expand in the future as panels add more flexibility
to their configuration.
SMTK 22.02 Release Notes¶
See also SMTK 21.12 Release Notes for previous changes.
SMTK Attribute Resource Changes¶
Added API to Disable Category Inheritance for Definitions¶
Added the ability to turn off inheriting category information from the Base Definition. The mechanism to control this is identical to that used at the Item Definition level via the method setIsOkToInherit. This information is persistent and stored in both XML and JSON formats.
Developer changes¶
New API:
bool isOkToInherit() const;
void setIsOkToInherit(bool isOkToInheritValue);
Attribute itemAtPath() extended for groups¶
The attribute resource’s method itemAtPath() has been extended to recognize N as a path component representing sub-group number, where N is the index of the sub-group. Python operation tracing will generate paths using this notation. Sub-group items whose names are integers must be preceded by the sub-group index to be retrieved correctly.
SMTK QT Changes¶
Base attribute view¶
An unused method, qtBaseAttributeView::requestModelEntityAssociation()
,
has been removed.
Qt attribute class¶
Allow a qtAttribute’s association item-widget to be customized.
Currently, this is accomplished by assigning the <AssociationsDef>
tag a Name
attribute and referring to it in the attribute’s
<ItemViews>
section. This may change in the future to eliminate any
possible naming conflict with other item names.
Qt attribute item-information¶
The qtAttributeItemInfo class now accepts shared pointers by const-reference rather than by reference so that automatic dynamic-pointer-casts to superclass-pointers will work.
New reference-tree item widget¶
Add a new qtReferenceTree widget that subclasses qtItem. Combined with the change to qtAttribute described above, this widget provides an alternative to qtReferenceItem for attribute association; it takes more screen space but provides better visual feedback.
Removal of qtActiveObjects¶
The qtActiveObjects
class is no longer in use anywhere and has been removed.
If you used it in a third-party extension, you’ll need to create a new object
to hold an smtk::view::Selection::Ptr
and an active smtk::model::Model
.
SMTK ParaView Extensions Changes¶
ParaView and Qt operation panels and views¶
The SMTK operation panel is now a “legacy” panel. It is not deprecated, but it is not preferred. Instead, consider using the operation toolbox and parameter-editor panels. By having two panels that split functionality from the previous one, users with small displays do not have to share limited vertical space among the operation list and operation parameters. There are other improvements below.
SMTK provides two new operation panels: + An “operation tool box” panel that shows a list of
operation push-buttons in a grid layout (as opposed to the previous flat text list).
An “operation parameter editor” panel than contains a tabbed set of operations. By using tabs, multiple operations can be edited simultaneously.
Because most applications will want to choose between either the legacy panel or the new panels, there are now separate plugins:
The
smtkPQLegacyOperationsPlugin
exposes the legacy operations panel in applications.The
smtkPQOperationsPanelPlugin
exposes the toolbox and parameter-editor operations panels in applications.
Your application can include or omit any combination of these plugins as needed.
See the SMTK user’s guide for more information about the panels.
Fixed Crash when loading in multiple attribute resources¶
Closing a CMB application that has more than one attribute resource loaded would cause the application to crash (even if all but one of the attribute resources were closed manually). This commit fixes the problem by checking to make sure the attribute panel has not been deleted when observing changes.
Added the ability to set the View and Attribute Resource¶
Both displayResource and displayResourceOnServer methods now take in optional view and advancedLevel parameters. If the view is set, then it will be used to display the resource and the advance level in the UI Manager is set to advancedLevel, else the resource’s top level view will be used and the advacedLevel is ignored.
3-D widgets¶
The pqSMTKPlaneItemWidget has been updated to use ParaView’s new display-sized plane widget. The advantage of this widget is that it does not require an input dataset with non-empty geometric bounds in order to display properly (i.e., the plane can be thought of as independent construction geometry rather than attached to any particular component).
A new pqSMTKFrameItemWidget is available for accepting orthonormal, right-handed coordinate frames from users. It requires 4 double-valued items, each holding a 3-vector: an origin, an x-axis, a y-axis, and a z-axis.
A new pqSMTKSliceItemWidget is available for displaying crinkle-slices of data. This widget uses the new ParaView plane widget and adds ParaView filters to render crinkle- slices of a user-controlled subset of components. This widget uses the new qtReferenceTree along with a modified MembershipBadge (both described below).
A new pqSlicePropertyWidget class is used by the slice-item widget above.
All of the existing 3-d widgets have been moved out
of the plugin
subdirectory and their symbols are now
exported.
This change was made so the slice-widget could reference
methods on the plane-widget.
A new smtkMeshInspectorView class has been added; it is a custom attribute view for the mesh-inspector operation described below.
SMTK VTK Extension Changes¶
Mesh inspection operation¶
Add a MeshInspector “operation” for inspecting meshes to SMTK’s VTK extensions. The operation does nothing; its parameters will have custom widgets that add mesh inspection to the operation panel. It can be applied to any component with geometry.
SMTK StringUtils Changes¶
Added toBoolean to StringUtils¶
The new method will convert a string to a boolean. If the method was successful it will return true else it will return false. All surround white-space is ignored and case is ignored.
Current valid values for true are: t, true, yes, 1 Current valid values for false are: f, false, no, 0
SMTK Graph Session Changes¶
Graph Arcs/OrderedArcs use WeakReferenceWrapper¶
The type used for storing arc ToTypes
has been changed from a
std::reference_wrapper<ToType>
to a new type
smtk::common::WeakReferenceWrapper<ToType>
in order to communicate to the
arc that the node component of the edge has been removed. This allows nodes to
be removed and edges to update lazily.
Developer changes¶
Plugins using SMTK graph infrastructure will need to rebuild and fix type errors
to match the new implementationn in SMTK. They will also need to make sure they
are checking if to
nodes in an arc are valid using the
smtk::common::WeakReferenceWrapper<ToType>::expired()
API to dectect if the
node is expired or not before accessing it.
Previously, if a node was removed, access via a to
node in an arc would
silently fail or sefault. Now, invalid access will result in a
std::bad_weak_ptr
exception when attempting to access the expired data.
GraphTraits allows customized NodeContainer¶
Allow implementor to override the default container used for storing node data.
NodeContainer API¶
Developers may now implement custom node storage as an option in GraphTraits.
Implementations of NodeContainer
must implement a minimal API to access and modify
the underlying node storage. Additional public APIs will be inherited by the
smtk::graph::Resource
.
class MinimalNodeContainer
{
public:
// Implement APIs inherited from smtk::resource::Resource.
/** Call a visitor function on each node in the graph.
*/
void visit(std::function<void(smtk::resource::ComponentPtr>>& visitor) const;
/** Find the node with a given uuid, if it is not found return a nullptr.
*/
smtk::resource::ComponentPtr find(const smtk::common::UUID& uuid) const;
protected:
// Implement protected APIs required by smtk::graph::Resouce and smtk::graph::Component.
/** Erase all of the nodes from the \a node storage without updating the arcs.
*
* This is an internal method used for temporary removal, modification, and
* re-insertion in cases where \a node data that is indexed must be changed.
* In that case, arcs must not be modified.
*
* Returns the number of nodes removed. Usually this is either 0 or 1, however the
* implementation may define removal of > 1 node but this may cause unintended behavior.
*/
std::size_t eraseNodes(const smtk::graph::ComponentPtr& node);
/** Unconditionally insert the given \a node into the container.
*
* Do not check against NodeTypes to see whether the node type is
* allowed; this has already been done.
*
* Returns whether or not the insertion was successful.
*/
bool insertNode(const smtk::graph::ComponentPtr& node);
};
Developer changes¶
The Graph API no longer accepts storing nodes that are not derived from
smtk::graph::Component
. This is enforced by the APIs required from the
NodeContainer.
Using the smtk::graph::ResourceBase::nodes()
API is no longer available unless
the NodeContainer
implements it.
The default NodeContainer
, smtk::graph::NodeSet
, provides an API for nodes
and
is implemented using std::set<smtk::resource::ComponentPtr, CompareComponentID>
as
the underlying container. This is consistent with the previous implementation and will
be automatically selected if no NodeContainer
is specified in GraphTraits.
Note, a smtk::resource::ComponentPtr
is used in the underlying storage to prevent having
to cast pointers for APIs inherited from smtk::resource::Resource
.
SMTK I/O Changes¶
Logger severity rename¶
The smtk::io::Logger severity levels have been renamed:
DEBUG is now Debug
INFO is now Info
WARNING is now Warning
ERROR is now Error
FATAL is now Fatal
The existing names have been deprecated.
Adding Property Support in Attribute XML Files¶
You can now set Properties on the Attribute Resource and on an Attribute via an XML file.
Property Section for the Attribute Resource¶
This is an optional section describing a set of properties that should be added to the Attribute Resource. The Property section is defined by a XML Properties node which is composed of a set of children Property nodes as shown below:
<Properties>
<Property Name="pi" Type="Int"> 42 </Property>
<Property Name="pd" Type="double"> 3.141 </Property>
<Property Name="ps" Type="STRING">Test string</Property>
<Property Name="pb" Type="bool"> YES </Property>
</Properties>
You can also look at data/attribute/attribute_collection/propertiesExample.rst and smtk/attribute/testing/cxx/unitXmlReaderProperties.cxx for a sample XML file and test.
The following table shows the XML Attributes that can be included in <Property> Element.
XML Attribute |
Description |
---|---|
Name |
String value representing the name of the property to be set. |
Type |
String value representing the type of the property to be set. Note that the value is case insensitive. |
The values that the Type attribute can be set to are:
int for an integer property
double for a double property
string for a string property
bool for a boolean property
The node’s value is the value of the property being set.
Supported Values for Boolean Properties¶
The following are supported values for true:
t
true
yes
1
The following are supported values for false:
f
false
no
0
Note that boolean values are case insensitive and any surrounding white-space will be ignored.
Properties and Include Files¶
If you include a Attribute XML file that also assigns Resource Properties, the include file’s Properties are assigned first. Meaning that the file suing the include file can override the Properties set by the include file.
Note - the ability to unset a Property is currently not supported.
Note - Properties are currently not saved if you write out an Attribute Resource that contains properties in XML format.
SMTK View Changes¶
Eliminating the need to call repopulateRoot¶
Previously, handling new persistent objects forced the Component Phrase Model to call repopulateRoot which can be very expensive for large models. The new approach identifies those Resource Components that would be part of the Phrase Model’s root subphrases and properly inserts them in sorted order, eliminating the repopulateRoot() call.
Resource lock badge¶
There is now a “lock” badge that indicates when an SMTK resource is locked by an operation. No visual distinction is made between read or write locking. You may use this badge in any view that displays resource phrases.
Membership badge¶
The membership badge has been extended to be pickier; it is now possible to specify whether the badge should apply to objects, resources, or components; whether they must have geometry or not; and whether components match a query-filter.
The purpose of these configuration options is to accommodate operations that wish to pass geometry for several components to be processed.
Other Changes¶
Plugin initialization based on ParaView version¶
Allow SMTK to create plugins for older versions of ParaView (e.g. v5.9) that rely on Qt interfaces to invoke initialziation. These changes should not require any changes to existing plugins using the smtk_add_plugin interface. SMTK plugins that manually implement ParaView plugins via the paraview_add_plugin CMake API should switch to using the SMTK wrapper for creating SMTK plugins.
Generated Plugin Source:
Generated sources for older versions or ParaView use an Autostart plugin
and are named using the scheme pq@PluginName@AutoStart.{h,cxx}. This will include the Qt interfaces required for the ParavView Autostart plugin.
Generated sources for newer versions of ParaView use an initializer
function, similar to VTK, and are named using the scheme smtkPluginInitializer@PluginName@.{h,cxx}. This includes a function that is namespace’d smtk::plugin::init::@PluginName@ which is called by ParaView.
SMTK 21.12 Release Notes¶
See also SMTK 21.11 Release Notes for previous changes.
SMTK Common Changes¶
Changes to smtk::common::Instances¶
Made the visit method const.
SMTK Attribute Resource Changes¶
Added Definition::isRevelvant Method¶
This method will return true if the Definition is considered relevant. If includeCategories is true then the attribute resource’s active categories must pass the Definition’s category check. If includeReadAccess is true then the Definition’s advance level < the read access level provided.
Expanded attribute’s Signal Operation to include category changes¶
The Signal operation now includes a resource item called categoriesModified both as a parameter and as a result. If set, it indicates the attribute resources whose categories have been modified.
Change to Attribute::isRelevant Method¶
The parameter readAccessLevel has been changed from int to unsigned int to match the signature of Item::isRelevant.
SMTK Qt Changes¶
qtAttributeView now uses smtk::attribute::Definition::isRelevant method¶
Removed qtAttributeViewInternals::removeAdvancedDefs since this logic is now part of the Definition’s isRelevant method.
Similarly, qtAttributeViewInternals::getCurrentDefs has been simplified by using Definition’s isRelevant method.
qtAttributeViewInternals::getCurrentDefs removed attribute resource parameter. It was no longer needed.
SMTK View Changes¶
Changes to Descriptive Phrase Functionality¶
PhraseModel Changes¶
The deprecated signature returned a boolean. The support form now returns a smtk::common::Visit Enum.
// Deprecated Form
using SourceVisitor = std::function<bool(
const smtk::resource::ManagerPtr&,
const smtk::operation::ManagerPtr&,
const smtk::view::ManagerPtr&,
const smtk::view::SelectionPtr&)>;
// New Form
using SourceVisitorFunction = std::function<smtk::common::Visit(
const smtk::resource::ManagerPtr&,
const smtk::operation::ManagerPtr&,
const smtk::view::ManagerPtr&,
const smtk::view::SelectionPtr&)>;
PhraseModel now maintains a map a Persistent Object’s UUIDs to a set of Descriptive Phrases that contains that Persistent Object. PhraseModel::trigger method now maintains the map on Persistent Object insertion or removal.
The method PhraseModel::uuidPhraseMap returns the map.
Also added a protected method PhraseModel::removeFromMap to remove Descriptive Phrases from the map.
Previously, this method would result in a trigger per Phrase in the model. Now it will only trigger for the root of the model.
DescriptivePhrase Changes¶
Added a hasChildren method to indicate if the Phrase has / will have sub-phrases. This is more efficient than requiring the Phrase’s sub-phrases to be built in order to determine if the Phrase has children.
ObjectGroupPhraseContent Changes¶
Added a hasChildren method to indicate if the Phrase Content has / will have children Descriptive Phrases. This is more efficient than requiring the Phrase’s sub-phrases to be built in order to determine if the Phrase has children.
Subphrase Generator Changes¶
Added hasChildren method to indicate if the generator would create children sub-phrases for a given Descriptive Phrase.
Added SubphraseGenerator::parentObjects method to return the Descriptive Phrases that would be parents of new phrases created for a Persistent Object. Note due to performance issues with ModelResource::BordantEntities method, this method has some performance issues.
Added SubphraseGenerator::createSubPhrase method that uses the above method to directly insert a new Phrase instead of doing a tree traversal. Performance analysis showed that this approach is slower than the original method due to Model Resource’s issues, but the approach seems sound so its been left in so that non-ModelResource Phrase Generators could use it.
API Change
SubphraseGenerator::indexOfObjectInParent now takes in a const smtk::view::DescriptivePhrasePtr& actualParent instead of a smtk::view::DescriptivePhrasePtr& actualParent
qrDescriptivePhraseModel Changes¶
The class no longer creates sub-phases when trying to determine if a Qt Item should indicate if it has children. This speeds things up when dealing with large Phrase Models.
componentVisibilityChanged no longer triggers the Phase Model. The original trigger was not needed and caused a performance issue on large Phrase Models.
SMTK Task Changes¶
Changed FillOutAttribute JSON Logic¶
Code now uses find instead of contains. This avoids doing double searches.
Task now supports Instance criteria¶
In addition to providing a list of Definitions names, you can now also specify a list of Attribute Names that also must be valid for the task to be considered Completable.
Task now processes category changes on resources¶
A Signal operation identifying category changes now will trigger the task to recalculate its internal state.
Fixed Task State Calculations¶
If there are no resources found for the task, the state is now Unavailable
If all of the task’s definitions and instances are irrelevant then the task state is Irrelevant
If any of the attributes required by the task are invalid, then the task is Incomplete
Else the task is Completable
Added the following methods¶
hasRelevantInfomation - returns true if it finds a relevant attribute or definition related to the task. It will also determine if it found any attribute resources required by the task.
testValidity - tests to see if an attribute is not currently in a ResourceAttributes entry. If it is not then it’s validity is tested, it is inserted into the appropriate set, and the method returns true, else it returns false.
Added smtk::tasks::Instances::findByTitle method¶
Returns the tasks with a given title.
Improved TestTaskGroup¶
The test now validates the states of the tasks after each modification.
Other Changes¶
GDAL dependency has been removed¶
When building SMTK with VTK and/or ParaView support enabled, we no longer require them to include support for GDAL. Functionality has not been removed from SMTK, simply put behind an option that is off by default.
SMTK 21.11 Release Notes¶
See also SMTK 21.10 Release Notes for previous changes.
SMTK Attribute Resource Changes¶
Display hint for attribute resources¶
The attribute read operation and XML import operations now
mark old resources (version 4 or older) with a property,
smtk.attribute_panel.display_hint
, upon load.
Newer files (version 5 or newer) must explicitly contain this hint.
The ParaView attribute panel uses (and other UI elements may use)
this property determine whether a loaded attribute should be immediately displayed
in the panel upon being added to a resource manager.
Furthermore, when the attribute panel displays a resource, it removes the property from any prior resource and adds it to the new resource to be displayed. Thus saving a project with several resources will resume upon reload with the same attribute resource displayed in the editor.
This change to SMTK also removes the default behavior that displays attribute resources when the ParaView pipeline browser’s selection changes. This change was made because the pipeline-browser’s selection changes when (a) a user or python script modifies the active pipeline source or (b) a new pipeline source is created. The second case was causing many updates to the attribute panel when projects with many resources are loaded/created. Now, this behavior is only enabled in “ParaView mode” (i.e., when post-processing is enabled). To reduce the impact of this change, we have also added a (default) option to pqSMTKPipelineSelectionBehavior that will display attribute resources as they are selected (specifically: when the SMTK selection holds a single entry in its primary selection and that entry is an attribute resource marked as displayable).
Finally, this change corrects the sense of smtk::attribute::Resource::isPrivate(), which was previously reversed.
Developer changes¶
File version numbers have increased to prevent breaking changes in behavior. Old files (XML version 4 or JSON version) will always mark this property to preserve existing behavior. New files must enable the hint explicitly.
If you used smtk::attribute::Resource::isPrivate() or smtk::attribute::Resource::setIsPrivate() in your code, you should reverse the sense of values obtained-from/passed-to these methods. It now returns true when a resource is private (i.e., should not be shown) and false when a resource is public.
User-facing changes¶
New files – depending on how they were created – may not appear immediately in the attribute editor. Also, files saved to the new format will “remember” which one was showing in the attribute editor.
Attribute writer now has exclusions¶
The smtk::io::AttributeWriter class now accepts definitions to include (pre-existing) and exclude (new functionality). Exclusions are processed after inclusions, so it is possible to include a base Definition and exclude some subset of its children Definitions for more exact pruning of which definitions and instances should be output by the writer.
Changing the default behavior of smtk::attribute::ReferenceItem::appendValue()¶
The original default behavior was to uniquely append values and to do an “normal” append when explicitly requested. Based on how this method is used and the fact that doing an append unique does incur a performance hit (order N-squared), the new default behavior does a normal append. The developer will now need to explicitly indicate that a unique append is requested.
SMTK 21.10 Release Notes¶
See also SMTK 21.09 Release Notes for previous changes.
SMTK Common Changes¶
Observers and debugging¶
Now smtk::common::Observers takes a second template parameter: a boolean indicating whether to print debug messages or not. This does away with the previous macro (which printed messages for all observers – resource, operation, etc.) in favor of messages that can be enabled for a single specialization. See the Debugging tips and tricks section of the user’s manual for more information.
Attribute Resource Changes¶
Expanding Attribute Resource’s Query Grammar¶
Attribute Resource’s queryOperation now uses a Pegtl-based approach which now provides the following benefits:
In addition to querying on Attribute Type, you can now also query based on Property and Property Value
Type Query can now include Regular Expressions.
See the `attribute filter documentation`_ for more details and examples.
Qt UI Changes¶
New qtComponentAttributeView Replacing qtModelEntityAttributeView¶
This new class expands the capabilities of the old class by now supporting Components of any Resources not just Model Resources.
Also there is no longer a need to specifier the filter type. The View will look at the association rule associated with the Attribute Definition specified in the view configuration. If more than one Definition is specified, then the first one is chosen.
As a result the new constraints to using this View are: * The Attribute Definition(s) in the configuration must contain the association rule to be used to determine which Resource Components need to be attributed * If more than one Attribute Definition is listed, then the first one’s association rule will be used.
In addition the following improvements have been made:
Selection of the Type of Attribute via the ComboBox no longer requires any button press after the selection has been made
Also addressed a bug related to the View being updated when selecting a different attribute to be assigned to the SMTK component. The symptom was that instead of accepting the requested type, the View would instead display “Please Select” indicating that the component had no attribute associated with it. The reason was due to the fact that the View was being updated via the call to removeAttribute prior to the new attribute being created. The fix was to postpone calling that method until after the new attribute was in place.
How to refer to qtComponentAttributeViews in a ViewConfiguration¶
Setting the “Type” to ComponentAttributeView will cause the creation of a qtComponentAttributeView. Note The “ModelEntity” Type will still be supported and will result in the creation of a qtComponentAttributeView.
<Views>
<View Type="ComponentAttribute" Title="Surface Properties">
<AttributeTypes>
<Att Type="SurfaceProperty" />
</AttributeTypes>
</View>
</Views>
Improving qtAssociation2ColumnWidget’s Operation Handler¶
The original code would cause a large number of refreshes due to the fact it merely would see if the were any resource changes. The new logic is a bit more intelligent and will only cause a refresh if any of the following conditions happen:
A modified component is either the widget’s attribute or can be associated with the widget’s attribute
A deleted component could have been associated with the widget’s attribute
A created component could be associated with the widget’s attribute
The result should be a more responsive UI.
SMTK’s New String Token Support¶
String token classes¶
SMTK now provides a smtk::string::Token
class for working with tokenized strings.
See the user’s guide section on string tokens for more information.
SMTK Software Process Changes¶
Dynamic initialization in plugins¶
The CMake macro SMTK provides to create plugins now uses a new feature in ParaView to ensure that plugins are initialized properly in both Qt-based GUI applications and python interpreters.
This should not require any changes to your code; it simply changes the code generated by the smtk_add_plugin() macro. Instead of generating a ParaView plugin with an automatically-generated pqAutoStartInterface class, it generates a ParaView plugin with a vtkPVDynamicInitializerPluginInterface class which calls a free function to initialize the plugin’s registrars.
SMTK 21.09 Release Notes¶
See also SMTK 21.07 Release Notes for previous changes.
SMTK Resource and Component Changes¶
Supporting MarkedForRemoval¶
Resources can now be markedForRemoval indicating that the resource will be removed from memory (as apposed to deletion which also means it is being deleted from storage as well). This can be used in the UI to determine if a View needs to worry about keeping its contents up to date if the reason it is using is going to be removed. This also gets around a current issue with the Resource Links system which will cause a resource to be pulled back into memory even though the resources that its associated with is going to be removed.
Another benefit is to potentially optimize the removal of components when its resource is targeted for removal.
Developer changes¶
Added the following API to smtk::resource::Resource * setMarkedForRemoval - sets the resource’s marked for removal state * isMarkedForRemoval - returns the resource’s marked for removal state
UI class changes * All Attribute Related Qt classes that handle operations will now check to see if the resource is marked for removal.
Attribute Resource Changes¶
Changing Expression Format¶
JSON will now store a ValueItem’s Expression in ComponentItem format using the key “ExpressionReference” instead of 2 keys called “Expression” and “ExpressionName”. This no only simplifies things format wise but will also support expressions stored in different resources.
Note The older format is still supported so this change is backward compatible. Note The XML format is still using the older style.
Supporting “External” Expressions¶
There are use cases where the workflow may want to store expressions in a separate Attribute Resource. The core of SMTK already supported this but the UI system assumed that the Attribute Resource which owned the ValueItem was also the source for expressions. This is no longer the case.
qtInstancedView can now optionally take in an Attribute Resource instead of solely relying on the one associated with the UI Manager. This allows classes like the qtAttributeEditor to supply the Attribute Resource.
Added a new query function called: findResourceContainingDefinition that will return an Attribute Resource that contains an Attribute Definition referred to by its typename. If the optional Attribute Resource provided to the function also contains the Definition, it is returned immediately without doing any additional searching. This maintains the original use case where the expressions are stored in the same resource.
qtInputItem no longer assumes the Item’s Attribute Resource is the one being used as a source for expressions.
Added two template files that can be used to demo the functionality.
data/attribute/attribute_collection/externalExpressionsPt1.sbt - Contains an Attribute Definition with an Item that can use an expression that is not defined in that template
data/attribute/attribute_collection/externalExpressionsPt2.sbt - Contains an Attribute Definition that represents the expressions used in Pt1.
Qt UI Changes¶
Changes to qtBaseAttributeView¶
In the past classes derived from qtBaseAttributeView relied on the qtUIManager to get access to the Attribute Resource they depended on. This is no longer the case. The Attribute Resource is now part of the information used to defined the instance.
Deprecation of qtUIManager::attResource() method¶
This method is no longer needed for qtBaseAttributeView derived classes.
Added qtItem::attributeResource() method¶
This is a convenience method for accessing the qtItem’s underlying Attribute Resource
Fixing qtAttributeView handling of Operations¶
In qtAttributeView::handleOperationEvent, the code had the following issues:
It assumed that an Attribute that is in the operation’s result must be based on the View’s current definition. This is only true if the View only contained one Definition. In reality, the attribute’s Definition needs to be tested against the Definitions that defined the View and if it matches any of them it needs to be processed.
It always assumed that there was a Current Attribute selected. This would result in a crash if there wasn’t any.
There was a bug in qtAttributeView::getItemFromAttribute, it was getting items from column 0 instead of the name column. This resulted in names not being properly updated
Added qtAttributeView::matchesDefinitions¶
This method tests a Definition against the Definitions that define the View and returns true if it matches or is derived from any in the list.
Replacing qtBaseView::getObject()¶
This method returns the instance’s view::Configuration but the name didn’t reflect that. Also the getObject method returned a smtk::view::ConfigurationPtr which means that a new shared pointer was always being created and as a result, incrementing its reference count. The new configuration() method returns a const smtk::view::ConfigurationPtr& instead which does not effect the shared point reference count.
Developer changes¶
qtBaseView::getObject() method is now deprecated.
Added Ability to Set Attribute Editor Panel’s Title¶
The Attribute Editor Panel name can now be configure by a smtk::view::Configuration.
If the Configuration is Top Level then the following Configuration Attributes can be used:
AttributePanelTitle - defines the base name of the Panel. If not specified it defaults to Attribute Editor.
IncludeResourceNameInPanel - if specified and set to true, the Panel’s title will include the name of the resource in ()
SimpleAttribute.sbt contains an example:
<Views>
<View Type="Attribute" Title="External Expression Test Pt - Source" TopLevel="true" DisableTopButtons="false"
AttributePanelTitle="SMTK Test" IncludeResourceNameInPanel="t">
<AttributeTypes>
<Att Type="B-expressions"/>
</AttributeTypes>
</View>
</Views>
Developer changes¶
pqSMTKAttributePanel::updateTitle() now takes in a const smtk::view::ConfigurationPtr& argument.
SMTK Task Subsystem (Preview)¶
New Task Classes¶
The task subsystem now provides more task types, task-adaptor classes for configuring tasks as they change state, and additional tests. See the task class documentation for details.
Tasks now include “style” strings that will be used to configure application state when the task becomes active.
Tasks now include references to dependencies and dependents, children and a parent. These are used to provide workflow observers that user interfaces can use to monitor when tasks are added-to and removed-from a pipeline.
Task serialization/deserialization¶
The task classes and task manager now support serialization and deserialization (to/from JSON). See the TestTaskJSON test and user guide for more details.
Other SMTK Core Changes¶
Allow connecting to remote server using multi-servers¶
Add support for connecting SMTK to remote server without initializing SMTK managers on the remote server. Added as part of 21.09.1.
Using TypeContainers instead of ViewInfo¶
In order to make the View System more flexible and to work with the new Task System, the following changes were made:
smtk::view::Information is now derived from TypeContainer and is no longer an abstract class. As a result it can now do the job that ViewInfo and OperationViewInfo does
ViewInfo and OperationViewInfo are no longer needed.
qtBaseView’s m_viewInfo is now an instance of smtk::view::Information and not ViewInfo
Developer changes¶
Unless the qtView is directly accessing m_viewInfo, there should be no required changes.
When dealing with smtk::view::information, it is important that the type you insert into the container exactly matches the type you use to get information from the container. For example if you insert a QPushButton* into the container and attempt to get a QWidget* back, it will fail and throw an exception.
So it is recommended you explicitly state the template type instead of having the compiler determine it. In the above example you would need to do an insert<QWidget*>(myQtPushButton) in order to get a QWidget* back.
smtk::external::ViewInfo and smtk::external::OperatorViewInfo are no longer needed and have been removed. smtk::view::Information object should be used instead.
Visibility badge improvements¶
The ParaView visibility-badge extension had an issue when large numbers of phrase-model instances existed and a resource was closed: the visibility was updated by completely rebuilding the map of visible entities which is slow. This is now fixed.
SMTK 21.07 Release Notes¶
See also SMTK 21.05 Release Notes for previous changes.
Registration Changes¶
Manager/registry tracking¶
Tracking of registrations between registrars and managers should now only be
done through a held Registry
instance as returned by the
Registrar::addToManagers
method. In the future, direct access to
Registrar::registerTo
and Registrar::unregisterFrom
will be disabled
and the Registry
RAII object will be the only way to manage registrations.
This ensures that registrations are accurately tracked.
SMTK Resource and Component Changes¶
Expanding Default Property Types on SMTK Resources/Components¶
The default set of Properties now include int, bool, std::vector<int> and std::vector<bool>.
Attribute Resource Changes¶
Preserve State Information¶
SMTK will now preserve state information that pertains to Attribute Resource views. The information persists even after the resource has been closed, assuming the resource was saved before it was closed.
Information Preserved:
The active advance level.
The active attributes in attribute views.
The active tab in a group views.
Fix isValid Check w/r to an Attribute’s Associations¶
If an attribute had the following conditions:
Its association item set to have its NumberOfRequiredValues > 0
Its Resource had active categories
All of its items where validity set with the exception of its association item
Then its isValid() would mistakenly return true, due to the fact that its association item (which does not have categories set), would be excluded from the check.
Now the association item is forced to be by turning off category checking for that item. By doing this, we are saying that if the attribute itself passes its category checks, then so does its association item.
Added the ability to ignore an item¶
There are times when a workflow may consider an item no longer relevant based on choices the user has made. In order model this behavior two methods have been added to Item:
void setIsIgnored(bool val);
bool isIgnored() const;
If setIsIgnored is passed true, then the item’s isRelevant() method will return false, regardless of any other facts. This value is persistent and is supported in both JSON and XML formats.
Changes to Attribute and Item isRelevant Methods¶
Both Attribute::isRelevant and Item::isRelevant have been modified to optional do advance level checking.
bool isRelevant(bool includeCategoryChecks = true, bool includeReadAccess = false,
int readAccessLevel = 0) const;
If includeCategoryChecks is set to true, then the Attribute or Item must pass their category checks based on the resource’s Active Category Settings.
If includeReadAccess is set to true then an Attribute is relevant iff at least one of it’s children has a read access level <= readAccessLevel.
In the case of an Item, if includeReadAccess is true then it must have it’s read access level <= readAccessLevel
Note that this modification does not require any code change in order to preserve previous functionality.
Added hasRelevantChildren method to GroupItem¶
GroupItem now has a method to test if at least on of its children items passes their category checks and read access checks passed on the caller’s requirements.
bool hasRelevantChildren(bool includeCategoryChecks = true, bool includeReadAccess = false,
int readAccessLevel = 0) const;
Changing ReferenceItem::AppendValue to Support Append Non-Unique¶
Added a nonUnique parameter that defaults to false
This avoids unnecessarily having to scan the entire item when duplicates are allowed
Item now also tracks the location of the first unset value in order to speed up the append process
Model Resource Changes¶
Model resource transcription¶
SMTK now provides a way to avoid an O(n^2) performance
issue when embedding many cells into a model;
previously, each insertion would perform a linear search
of pre-existing relationships. However, many operations
(especially those in the importer group) will not attempt
to re-insert existing relationships. The Model::addCell()
and EntityRefArrangementOps::addSimpleRelationship()
methods now accept a boolean indicating whether to bypass
the linear-time check.
The VTK session provides a static method,
Session::setEnableTranscriptionChecks()
, for operations
to enable/disable this behavior during transcription.
SMTK Project Changes¶
Changes to smtk::project::ResourceContainer API¶
Changes to the smtk::project::ResourceContainer
API to allow for non-unique roles
to be assigned to Resources in a project.
Deprecated version >= 21.6¶
smtk::project::ResourceContainer::getByRole -> smtk::resource::ResourcePtr
New API¶
smtk::project::ResourceContainer::findByRole -> std::set<smtk::resource::ResourcePtr>
Other SMTK Core Changes¶
Visitors¶
SMTK now provides an enumeration, smtk::common::Visit
, that visitor lambdas
may return to indicate whether visitation should continue (smtk::common::Visit::Continue
)
or stop (smtk::common::Visit::Halt
).
This enum is much easier to use than simply returning a bool
as developers
frequently have trouble remembering which value (true or false) corresponds to
which iteration behaviour.
This is currently only used by smtk::task::Task::visit()
, but will be
used more widely in the future.
Task subsystem¶
SMTK now provides a task subsystem for representing user-actionable tasks in a workflow. See the task-system documentation for more information about how to use this subsystem.
Qt UI Changes¶
Removing Empty Frames in qtGroupItem¶
Using GroupItem’s hasRelevantChildren method, qtGroupItem will now hide it’s frame if there are no children to be displayed.
Filter Advance Attribute Definitions¶
Attribute views will now hide any attribute definitions that have an advance level that is higher than the user’s active advance level. This enables the Attribute View to hide itself if all its definitions should be hidden from the user.
Hide disabled attribute resources¶
The Attribute Editor panel will now first check to see if an Attribute Resource is enabled before attempting to display it. Telling the Attribute Editor panel to display a disabled Attribute Resource will be the equivalent to telling the panel to display a nullptr. The panel will be reset if it was currently display any widgets.
Improving UI handling of Signal Operations¶
Originally the qtAttributeView class would ignore the Signal Operation since typically it would be the only Qt UI element that would be creating, removing, and changing the Attributes it is displaying. However, this prevented the UI designer from having AttributeViews that displayed the same information from being used in Selector Views or have different AttributeViews overlap their contents (for example one View could be displaying Fluid Boundary Conditions, while another was displaying all Boundary Conditions)
This change now encodes the address of the View that initiated the change so that we can avoid a View from being updated via a Signal Operation that it itself initiated.
qtAttributeView has now been updated to only ignore Signal Operations that it triggered.
Supporting smtk.extensions.attribute_view.name_read_only in qtAttributeViews¶
You can now indicate that an Attribute’s name should not be modified by creating a bool Property on the Attribute called: smtk.extensions.attribute_view.name_read_only and setting its value to true.
Observing Operations¶
qtAttributeView will now properly examine modified attributes to see if they have smtk.extensions.attribute_view.name_read_only property or if their names had been changed.
Changes to Polygon Session¶
ImportPPG Operation¶
An ImportPPG
operation has been added to the polygon session
for creating model resources from a simple text file input.
The “ppg” (Planar PolyGon) file format is a simple data format
that specifies 2-D geometry as a list of vertex coordinates and
polygon face definitions, with vertices connected implicitly by
straight-line model edges. Documentation is in the “Session: Polygon”
section of the SMTK user guide.
The ImportPPG
operation is provided as a convenience for exploring
CMB’s many capabilities as well as for testing, debug, and demonstration.
To use this feature from modelbuilder, the “File -> New Resource” menu
item now includes an option “Polygon -> Planar Polygon Model from PPG
File”.
Removed Previously Deprecated API¶
The following deprecated methods have been removed:
Categories::Set::mode has been replaced with Categories::Set::inclusionMode
Categories::Set::setMode has been replaced with Categories::Set::setInclusionMode
Categories::Set::categoryNames has been replaced with Categories::Set::includedCategoryNames
Categories::Set::set has been replaced with Categories::Set::setInclusions
Categories::Set::insert has been replaced with Categories::Set::insertInclusion
Categories::Set::erase has been replaced with Categories::Set::eraseInclusion
Categories::Set::size has been replaced with Categories::Set::inclusionSize
ReferenceItem::objectValue has been replaced with ReferenceItem::value
ReferenceItem::setObjectValue has been replaced with ReferenceItem::setValue
ReferenceItem::appendObjectValue has been replaced with ReferenceItem::appendValue
ReferenceItem::setObjectValues has been replaced with ReferenceItem::setValues
ReferenceItem::appendObjectValues has been replaced with ReferenceItem::appendValues
PhraseModel::addSource now accepts const smtk::common::TypeContainer&
PhraseModel::removeSource now accepts const smtk::common::TypeContainer&
Software Process Changes¶
CMake Policies¶
Because of CMake policy CMP0115 (source file extensions must be explicit),
when passing test names to the smtk_add_test
macro, be sure to include
the filename extension (such as .cxx
).
Deprecate Python 2.x support¶
Python 2.x reached its end of life in January 2020. SMTK has deprecated its Python 2 support and will remove it in a future release.
SMTK 21.05 Release Notes¶
See also SMTK 21.04 Release Notes for previous changes.
Changes to Project Subsystem¶
A major revision was undertaken to the smtk::project
namespace that is not
backward compatible. The previous Project class was hard-coded to a use single
attribute resource and one or two model resources. The new Project class is an
SMTK resource that can support any number of resources including custom ones,
with assigned roles. The new Project class can optionally be scoped to a list
of available operations and resource types. New Project instances store and
retrieve their contents as atomic operations with the file system.
Projects can now be described entirely using Python, making it
easier to implement custom project types for different solvers.
More information is available in the SMTK user guide and there is also a new create_a_project tutorial.
Graph Resource Changes¶
Graph-resource ArcMap¶
The graph resource now provides a custom subclass of TypeMap named smtk::graph::ArcMap. This subclass deletes the copy and assignment constructors to prevent modifications to accidental copies of arcs rather than to the resource’s arc container.
External changes¶
smtk::graph::ResourceBase::ArcSet type-alias was removed and the ArcMap class can now be used directly.
If you maintain external code that depends on smtk::graph::Resource, you will need to replace the ResourceBase::ArcSet type-alias with ArcMap (which lives in smtk::graph not smtk::graph::ResourceBase).
Software Process Changes¶
CMake package directory¶
The CMake package directory for SMTK is now in a location that CMake searches
by default. This removes the need to do -Dsmtk_DIR
and instead the install
prefix can be given in the CMAKE_PREFIX_PATH
variable.