Concepts¶
There are several basic concepts that CRIMSON uses to allow specification of the simulation setup:
- Vessel tree
- Geometric model
- Simulation mesh
- Solver setup manager
- Boundary condition
- Boundary condition set
- Material
- Solver parameters
- Solver study
Vessel tree¶
A vessel tree contains the information about the vessels and which vessels form connected components.
A vessel tree is passed to the writeSolverSetup
method of a solver study and has the following API:
getActiveConnectedComponentsMap()
- Returns a
dict
object mapping from UIDs of vessels (as used byface identifiers
) to the connected component index. This means that all vessels having the same connected component index form a single connected simulation domain.
getClosestPoint(faceIdentifier, x, y, z)
- Returns the distance to arc length along the vessel path defined by the
faceIdentifier
to the point at coordinates(x, y, z)
. getVesselPathCoordinateFrame(faceIdentifier, x, y, z)
Returns the coordinate frame defined by the vessel path given the coordinates
(x, y, z)
. The return value is a list of 9 floating point values:- [0:3] - the coordinates of the point on the vessel path closest to
(x, y, z)
- [4:6] - the tangent vector
- [7:9] - the normal vector
The binormal vector can be obtained by taking a cross product of normal and tangent vectors.
- [0:3] - the coordinates of the point on the vessel path closest to
Geometric model¶
Geometric model describes the boundary of the simulation domain. Currently, the geometric model in CRIMSON is described using
a set of planar or B-spline faces that are created during model building. Each face has a face identifier
defining it.
The face identifiers for the models created in CRIMSON (i.e. not imported from external solid modelling tool) are preserved
through model modification, e.g. re-lofting, changing the fillet size, or adding extra vessels will not affect the face identifiers and
boundary conditions attached to these faces.
Note, that multiple physical model faces may share the same logical face identifier. For example, in the image below, two physical faces in yellow share the same face identifier because they were created from the wall of the same vessel.
A geometric model is passed to the writeSolverSetup
method of a solver study and has the following API:
getModelFacesForFaceIdentifier(faceIdentifier)
- Returns a list of model face indices that share the face identifier
faceIdentifier
. getFaceIdentifierForModelFace(index)
- Returns the face identifier for the model face index
index
. getNumberOfModelFaces()
- Returns the total number of model faces.
getNumberOfFaceIdentifiers()
- Returns the total number of face identifiers.
faceIdentifierIndex(faceIdentifier)
- Returns an index of a face identifier in a list of unique face identifiers. The returned value is in range
0 <= index < getNumberOfFaceIdentifiers()
. Note, that the returned indices may change after model modification. getFaceIdentifier(index)
- Returns the face identifier that has the index
index
in the list of unique face identifiers. getFaceNormal(faceIdentifier)
- Returns a list of three
float``s representing the normal for a face associated with the ``faceIdentifier
. Note, that the return value will only be valid for flat (inflow/outflow) faces. For wall faces, returns[0, 0, 0]
. getDistanceToFaceEdge(faceIdentifier, x, y, z)
- Returns the distance to the edge of the face associated with the
faceIdentifier
to point at position[x, y, z]
. The result will only be correct for flat (inflow/outflow) faces that have not been split into multiple model faces (i.e.getModelFacesForFaceIdentifier(faceIdentifier)
returns a list of length 1).
Simulation mesh¶
Simulation mesh describes the subdivision of the simulation domain into small discrete elements suitable for finite element simulation.
A 3D simulation mesh consists of nodes (or vertices), edges, faces and elements.
A simulation mesh is passed to the writeSolverSetup
method of a solver study and has the following API:
getNNodes()
- Returns the number of nodes in the mesh.
getNEdges()
- Returns the number of edges in the mesh.
getNFaces()
- Returns the number of faces in the mesh.
getNElements()
- Returns the number of elements in the mesh.
getNodeCoordinates(nodeIndex)
- Returns a list of three
float``s representing the node position. ``nodeIndex
should be in the range0 <= nodeIndex < getNNodes()
. getElementNodeIds(elementIndex)
- Returns a list of node indices for element with index
elementIndex
.elementIndex
should be in the range0 <= elementIndex < getNElements()
. getAdjacentElements(elementIndex)
- Returns a list of element indices that the element with index
elementIndex
shares a face with.elementIndex
should be in the range0 <= elementIndex < getNElements()
. getNodeIdsForFace(faceIdentifier)
- Returns a list of node indices for all model faces with face identifier
faceIdentifier
. Note, that node indices on the face edges are shared for edges between different faces belonging to the wall (FaceType.ftWall
). However, for the edges between cap faces (FaceType.ftCapInflow
andFaceType.ftCapOutflow
) and wall faces, nodes at the edge always belong to the wall (i.e. for flow faces, the nodes at the edge will not be included in the list returned by this function). getMeshFaceInfoForFace(faceIdentifier)
Returns a list of face information lists for all model faces with face identifier
faceIdentifier
. Each element of the returned list contains the following information:[0] elementIndex - element index that the mesh face belongs to [1] faceIndex - face index [2] nodeIndex - index of the first node [3] nodeIndex - index of the second node [4] nodeIndex - index of the third node
Solver setup manager¶
Solver setup manager contains the information about other entities necessary for solver setup, their human-readable names, as well as the ways to create these entities.
A solver setup manager class is expected to implement the following interface:
getBoundaryConditionSetNames()
- Return a list of strings containing the names of types of boundary condition sets that the user can choose from.
createBoundaryConditionSet(name)
- Return an object of boundary condition set class for the boundary condition set type
name
.name
will be chosen from the list returned bygetBoundaryConditionSetNames()
. getBoundaryConditionNames()
- Return a list of strings containing the names of types of boundary conditions that the user can choose from.
createBoundaryCondition(name)
- Return an object of boundary condition class for the boundary condition type
name
.name
will be chosen from the list returned bygetBoundaryConditionNames()
. getSolverParametersNames()
- Return a list of strings containing the names of types of solver parameters that the user can choose from.
createSolverParameters(name)
- Return an object of solver parameters class for the solver parameters type
name
.name
will be chosen from the list returned bygetSolverParametersNames()
. getSolverStudyNames()
- Return a list of strings containing the names of types of solver studies that the user can choose from.
createSolverStudy(name)
- Return an object of solver study class for the solver study type
name
.name
will be chosen from the list returned bygetSolverStudyNames()
. getMaterialNames()
- Return a list of strings containing the names of types of materials that the user can choose from.
createMaterial(name)
- Return an object of material class for the boundary condition type
name
.name
will be chosen from the list returned bygetBoundaryConditionNames()
.
Boundary condition¶
Boundary condition represents a single boundary condition applied to the part of the simulation domain boundary.
It is recommended to inherit the specific boundary condition classes from CRIMSONCore.FaceData
and fill
in the properties in the boundary condition class’ constructor.
In addition to the properties exposed to the user through the CRIMSONCore.PropertyStorage
,
the boundary condition can also define a method createCustomEditorWidget. This function will be called
by the C++ code when the user intends to edit the boundary condition. It should return a Qt widget
which can be created using the PythonQt.QtGui
module. Note, that the ownership of the widget belongs to
the Python code and thus should be stored as a member of the boundary condition instance. However, it should not
be saved to the scene and thus the __getstate__
and __setstate__
methods should be overriden to avoid this.
Please see the implementation of CRIMSONSolver.BoundaryConditions.PrescribedVelocities
class for a reference
implementation.
Furthermore, the boundary condition class must define a class-scope member variables unique
and applicableFaceTypes
.
The applicableFaceTypes
is a list of PythonQt.CRIMSON.FaceType
the boundary condition can be applied to.
An example implementation of a boundary condition:
from CRIMSONCore.FaceData import FaceData
from PythonQt.CRIMSON import FaceType
class RCR(FaceData):
unique = False
humanReadableName = "RCR"
applicableFaceTypes = [FaceType.ftCapInflow, FaceType.ftCapOutflow]
def __init__(self):
FaceData.__init__(self)
resistancePropertyAttributes = {"suffix": u" g/(mm\u2074\u00B7s)", "minimum": 0.0}
capacitancePropertyAttributes = {"suffix": u" mm\u2074\u00B7s\u00B2/g", "minimum": 0.0}
self.properties = [
{
"Proximal resistance": 100.0,
"attributes": resistancePropertyAttributes
},
{
"Capacitance": 1e-5,
"attributes": capacitancePropertyAttributes
},
{
"Distal resistance": 1000.0,
"attributes": resistancePropertyAttributes
}
]
Material¶
Material objects represent a single type of material applied to the part of the simulation domain boundary.
The faces that the material is applied to, its properties, the unique
flag and custom editor widget behaviour are
identical to those of the boundary condition classes.
An example implementation of a material:
from CRIMSONCore.FaceData import FaceData
from PythonQt.CRIMSON import FaceType
class DeformableWallMaterial(FaceData):
unique = False
humanReadableName = "Deformable wall material"
applicableFaceTypes = [FaceType.ftWall]
def __init__(self):
self.properties = [
{
"Young's modulus": 4661000.0,
"attributes": {"suffix": u" g/(mm\u00B7s\u00B2)", "minimum": 0.0}
},
{
"Thickness": 1.0,
"attributes": {"suffix": u" mm", "minimum": 0.0}
},
]
Boundary condition set¶
Boundary condition set represents a set of logically related boundary conditions. This logical grouping facilitates reuse of boundary conditions to simulate difference scenarios. Currently only used for boundary condition grouping in the GUI. Thus, this class may be empty:
class BoundaryConditionSet(object):
def __init__(self):
pass
Solver parameters¶
Solver parameters data contains solver-specific values necessary to complete the simulation setup along with boundary conditions and the simulation mesh. For example, these may include the number and size of a time step for the simulation or the output configuration.
It is recommended to inherit the specific solver parameters classes from CRIMSONCore.PropertyStorage
.
An example implementation of a solver parameters:
from CRIMSONCore.PropertyStorage import PropertyStorage
class SimpleSolverParameters(PropertyStorage):
def __init__(self):
PropertyStorage.__init__(self)
self.properties = [
{
"Time parameters": [
{
"Number of time steps": 200,
"attributes": {"minimum": 1}
},
{
"Time step size": 0.01,
"attributes": {"minimum": 0.0, "suffix": " s"}
}
]
},
{
"Fluid parameters": [
{
"Viscosity": 0.004,
"attributes": {"minimum": 0.0, "suffix": u" g/(mm\u00B7s)"}
},
{
"Density": 0.00106,
"attributes": {"minimum": 0.0, "suffix": u" g/mm\u00B3"}
}
]
},
]
Solver study¶
Solver study contains information about associated simulation mesh, one or more boundary condition sets, solver parameters, and materials and is responsible for preparing the input files to be used by the solver. In addition, it is responsible for translating the resulting output of the simulation to the format used by CRIMSON.
A solver study class is expected to implement the following interface:
setMeshNodeUID(uid)
- Store the node uid of the simulation mesh (
string
). getMeshNodeUID()
- Return the stored node uid of the simulation mesh (
string
). setSolverParametersNodeUID(uid)
- Store the node uid of the solver parameters (
string
). getSolverSetupParametersUID()
- Return the stored node uid of the solver parameters (
string
). setBoundaryConditionSetNodeUIDs(uids)
- Store the node uids of the boundary condition sets (
list(string)
). getBoundaryConditionSetNodeUIDs()
- Return the stored node uids of the boundary condition sets (
list(string)
). setMaterialNodeUIDs(uids)
- Store the node uids of the materials (
list(string)
). getMaterialNodeUIDs()
- Return the stored node uids of the materials (
list(string)
). writeSolverSetup(vesselTreeData, geometricModelData, meshData, solverParameters, boundaryConditions, vesselPathNames, solutionStorage)
Write the setup for the solver. The parameters are as follows:
vesselTreeData
:a vessel tree data object (only present for models built in CRIMSON) geometricModelData
:a geometric model data object. meshData
:a simulation mesh data object. solverParameters
:a solver parameters object. boundaryConditions
:a list of boundary condition objects. vesselPathNames
:a dictionary mapping the UID’s used by face identifiers
to the names that user has assigned to vessel paths that the user assigned in the GUI (e.g. Aorta, Left carotid, etc.).solutionStorage
:a solution storage
object containing the solution that has been loaded usingloadSolution
or transferred to the new mesh during mesh adaptation.loadSolution()
- Load the result of simulation. Should return an instance of
CRIMSONCore.SolultionStorage
mapping mesh node indices to data values. computeMaterials()
- Compute the material values. Should return an instance of
CRIMSONCore.SolultionStorage
mapping mesh face indices to material values.