Refactoring itk::FEM framework - V4: Difference between revisions
(153 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
This page outlines the proposed changes to the itk::FEM framework | This page outlines the proposed changes to the itk::FEM framework. A number of these changes will break backwards compatibility. | ||
= | =Submission Of Code= | ||
==itk:: | |||
http://review.source.kitware.com/#change,1993 | |||
=Authors= | |||
The proposed changes for the itk::FEM framework are being proposed by the University of Iowa. This group is made up of investigators from the Departments of Radiology, Biomedical Engineering, and Psychiatry. Questions and comments regarding these changes should be sent to | |||
*'''Vincent Magnotta (vincent-magnotta -at- uiowa.edu)''' - [http://mri.radiology.uiowa.edu Radiology] | |||
Other team members include | |||
*Nicole Grosland - [http://www.bme.engineering.uiowa.edu/ Biomedical Engineering], [http://www.ccad.uiowa.edu/mimx MIMX] | |||
*Kiran Shivanna - [http://www.ccad.uiowa.edu/mimx MIMX] | |||
# | *Hans Johnson - [http://www.psychiatry.uiowa.edu Psychiatry] | ||
#* | *Kent Williams - [http://www.psychiatry.uiowa.edu Psychiatry] | ||
#* | |||
= Changes to the FEM Framework Classes= | |||
#* | |||
==itk::fem::FEMLightObject== | |||
This class has been updated to remove public access to the class variables. All fem classes derive from this class. This class was also converted to support smart pointers. Support for the old method where the pointers were not smart pointers has been removed. The ITK framework should now behave similar to the rest of ITK in regards to object pointers. Public access to class member variables has been eliminated in this and all derived classes. The use should now use public membership functions to get or set the value of internal class member variables. These are major changes to the class are outlined here: | |||
*Moved the following class variables from public to protected: | |||
#* | **'''GN''' - This variable was renamed to m_GlobalNumber | ||
#* | *Removed the following class functions: | ||
#* | **'''Clone'''() | ||
#* | **'''ClassID'''() | ||
#* | **'''Read'''() | ||
#* | **'''Write'''() | ||
#* | **'''CreateFromStream'''() | ||
#* | **'''SkipWhiteSpace'''() | ||
#* | *The following member functions were added to support setting the global number for fem objects | ||
#* | **void '''SetGlobalNumber'''(int) | ||
#* | **int '''GetGlobalNumber'''() | ||
#* | |||
#* | In addition, we have removed all of the graphics capabilities from the fem classes. In addition, I/O was removed from the classes as well. The user must now use the Spatial Objects to support I/O for FEM. | ||
#* | |||
==itk::FEMObject== | |||
In the prior versions of ITK (< 4.0) the itk::fem framework offers a complicated interface for specifying mesh to be used for analysis. The defintion of the FEM problem (nodes, elements, materials, and loads) was done in the Solver. We have created a new ITK object called itk::FEMObject that will define the entire FE problem. The itk::FEMObject inherits from itk::DataObject. This object allows the user to completely specify the Nodes, Elements, Loads, and Materials for the problem. This object was based on the decision to make a parallel structure to itk::Mesh without a complete refactoring of the mesh data structure. This also removes the specification of the FE simulation from the solver. | |||
In order to support input/output, we have decided to use SpatialObjects. To support the new FEMObject, a new FEMObjectSpatialObject was created. This also required that additions be added to the metaIO library to support this new spatial object type. The minor disadvantage of this implementation is that new element, load, and material types will require additions in multiple locations (ie both in the fem framework as well as the metaIO library). We have decided to use the same FEM file format for the model specification that was used previously, but it is now encoded in a spatial object. | |||
=== Migration Guide === | |||
The [http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html itk::fem::FEMObject] was created to provide support for a first class object in ITK that will define the finite element problem being used for analysis. This used to be defined only in the itk::fem::Solver. All support for file I/O was removed from this class. The user now defines a FEMObject object and use this an input into the solver. Here is an outline on how to define a new problem. | |||
<source lang="cpp"> | |||
itk::fem::FEMObject::Pointer fem = itk::fem::FEMObject::New(); | |||
. | |||
. | |||
. | |||
itk::fem::Solver::Pointer solver = itk::fem::Solver::New(); | |||
solver->SetInput( fem ); | |||
solver->Update( ); | |||
itk::fem::FEMObject::Pointer result = solver->GetOutput(); | |||
</source> | |||
The FEMObject has interfaces to define the nodes, elements, materials, and loads used to define the finite element problem. Here are the public member functions used to manipulate the various portions of the FEM problem. | |||
*'''Nodes:''' | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods AddNextNode (Element::Node::Pointer e)] - Add another node to the FEM problem. This will be added as the last node in the node container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods InsertElement (Element::Pointer e, ElementIdentifier index)] - Add another node at the specified position in the node container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNode (NodeIdentifier index)] - Get the node at the specified index of the node container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNodeWithGlobalNumber (int globalNumber)] - Get the node with the specified global number. This is independent of the order in the node container, and is defined by the user when defining the FEM problem. | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNumberOfElements (void) ] - Get number of nodes in the FEM problem | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNodeContainer ()] - Get the entire node container | |||
*'''Elements:''' | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods AddNextElement (Element::Pointer e)] - Add another element to the FEM problem. This will be added as the last element in the element container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetElementContainer ()] - Get the entire element container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNumberOfElements (void)] - Get number of elements in the FEM problem | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods InsertElement (Element::Pointer e, ElementIdentifier index)] - Add another element at the specified position in the element container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetElement (ElementIdentifier index)] - Get the element at the specified index of the element container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetElementWithGlobalNumber (int globalNumber)] - Get the element with the specified global number. This is independent of the order in the element container, and is defined by the user when defining the FEM problem. | |||
*'''Loads:''' | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods AddNextLoad()] - Add another load to the FEM problem. This will be added as the last load in the load container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods InsertLoad()] - Add another load at the specified position in the load container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNumberOfLoads()] - Get number of loads in the FEM problem | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetLoadContainer()] - Get the entire load container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetLoadWithGlobalNumber()] - Get the load with the specified global number. This is independent of the order in the load container, and is defined by the user when defining the FEM problem. | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetLoad()] - Get the load at the specified index in the load container. | |||
*'''Materials:''' | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetMaterialContainer ()] - Get the entire material container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods AddNextMaterial (MaterialLinearElasticity::Pointer mat)] - Add another material at the specified position in the material container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNumberOfMaterials (void)] - Get number of materials in the FEM problem | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods InsertMaterial (Material::Pointer e, MaterialIdentifier index)] - Add another material at the specified position in the material container | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetMaterial (MaterialIdentifier index)] | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetMaterialWithGlobalNumber (int globalNumber)] - Get the material with the specified global number. This is independent of the order in the material container, and is defined by the user when defining the FEM problem. | |||
*'''Other Public Membership Functions:''' | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods Clear ()] - Initialize the FEMObject | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods DeepCopy (FEMObject *Copy)] - Copy the FEMObject into the current FEMObject | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods FinalizeMesh ()] - Finalize the mesh. This '''must''' be called before using this object in the itk::fem::Solver | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNumberOfDegreesOfFreedom (void)] - Get the degrees of freedom for the FEMObject | |||
*#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1FEMObject.html#pub-methods GetNumberOfMultiFreedomConstraints (void)] - Get the number of constraints for the FEMObject | |||
==itk::fem::Node== | |||
This class is defined in itkFEMElementBase.h and defines the degree of freedom and coordinate for the node. A node is essentially a point in space. | |||
*'''Public Member Functions:''' | |||
**VectorType & '''GetCoordinates(void)''' - get the spatial coordinates for the node | |||
**void '''SetCoordinates(const VectorType & coords)''' - Set the spatial coordinates of the node | |||
**DegreeOfFreedomIDType '''GetDegreeOfFreedom(unsigned int i)''' - Get the degree of freedom for the spatial coordinate (x,y,z) | |||
**void '''SetDegreeOfFreedom(unsigned int i, DegreeOfFreedomIDType dof)''' - Set the degree of freedom for the spatial coordinate | |||
**void '''ClearDegreesOfFreedom(void)''' - Remove all of the degrees of freedom | |||
*Removed these Member Functions | |||
**'''Read()''' | |||
**'''Write()''' | |||
==itk::fem::Element== | |||
This is the [http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element.html base class] and specific element types are defined below. This class did not change from its implementation in previous versions of ITK. The following changes were made to each of the specific element types. | |||
#The following variables have been moved from public to protected: | |||
#*'''mat''' - This variable is now called m_mat | |||
#The following public member functions were added: | |||
#*Material::ConstPointer '''GetMaterial'''(void) - Get the material property used for the element | |||
#*void '''SetMaterial'''(Material::ConstPointer mat_) - Set the material property for the element | |||
#*'''CreateAnother''' - Duplicate the specified object | |||
#The following public membership functions have been removed: | |||
#*void '''Read'''(std::istream &, void *info) | |||
#*void '''Write'''(std::ostream & f) | |||
#*void '''Draw'''(CDC* pDC, Solution::ConstPointer sol) | |||
The specific element types available are defined below: | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC1Beam.html Element2DC1Beam] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0LinearQuadrilateralStress.html Element2DC0LinearQuadrilateralStress] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0LinearQuadrilateralStrain.html Element2DC0LinearQuadrilateralStrain] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0LinearQuadrilateralMembrane.html Element2DC0LinearQuadrilateralMembrane] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0LinearTriangularStress.html Element2DC0LinearTriangularStress] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0LinearTriangularStrain.html Element2DC0LinearTriangularStrain] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0LinearTriangularMembrane.html Element2DC0LinearTriangularMembrane] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0QuadraticTriangularStress.html Element2DC0QuadraticTriangularStress] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element2DC0QuadraticTriangularStrain.html Element2DC0QuadraticTriangularStrain] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element3DC0LinearHexahedronMembrane.html Element3DC0LinearHexahedronMembrane] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element3DC0LinearHexahedronStrain.html Element3DC0LinearHexahedronStrain] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element3DC0LinearTetrahedronMembrane.html Element3DC0LinearTetrahedronMembrane] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element3DC0LinearTetrahedronStrain.html Element3DC0LinearTetrahedronStrain] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element3DC0LinearTriangularLaplaceBeltrami.html Element3DC0LinearTriangularLaplaceBeltrami] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Element3DC0LinearTriangularMembrane.html Element3DC0LinearTriangularMembrane] | |||
==itk::fem::MaterialLinearElasticity== | |||
[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1MaterialLinearElasticity.html MaterialLinearElasticity] is the only material type that currently exists in ITK. Several modifications were made to this class during the ITKv4 refactoring. | |||
#Moved the following class variables were moved from public to protected signatures: | |||
#*'''A''' - renamed to m_CrossSectionalArea | |||
#*'''E''' - renamed m_YoungModulus | |||
#*'''h''' - renamed m_Thickness | |||
#*'''I''' - renamed m_MomentOfInertia | |||
#*'''nu''' - renamed m_PoissonRatio | |||
#*'''RhoC''' - renamed m_DensityHeatCapacity | |||
#Add the following public class member functions to get/set the class variables. | |||
#*void '''SetCrossSectionalArea'''(double) – Set the cross sectional area | |||
#*void '''SetYoungsModulus'''(double) – Set the Youngs Modulus | |||
#*void '''SetThickness'''(double) – Set the cross sectional thickness | |||
#*void '''SetMomentOfInertia'''(double) – Set the Moment of Inertia | |||
#*void '''SetPoissonsRatio'''(double) – Set the Poisson’s ratio | |||
#*void '''SetDensityHeatProduct'''(double) – Set the Density - Heat Capacity product | |||
#*double '''GetCrossSectionalArea'''() – Get the cross sectional area | |||
#*double '''GetYoungsModulus'''() – Get the Youngs Modulus | |||
#*double '''GetThickness'''() – Get the cross sectional thickness | |||
#*double '''GetMomentOfInertia'''() – Get the Moment of Inertia | |||
#*double '''GetPoissonsRatio'''() – Get the Poisson’s ratio | |||
#*double '''GetDensityHeatProduct'''() – Get the Density - Heat Capacity product | |||
#*'''CreateAnother''' - Duplicate the specified object | |||
#Removed these public class member functions | |||
#*virtual void '''Read'''(std::istream& f, void* info) | |||
#*virtual void '''Write'''(std::ostream& f ) | |||
==itk::fem::Load== | ==itk::fem::Load== | ||
This is the [http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Load.html load base class] for all specific implementations used in the itk::fem framework. This class used to use a Visitor Dispatcher pattern to apply the loads to elements. While this provided a dynamic and general interface, it was very complicated and we had never taken advantage of its flexibility. Therefore, this patter was eliminated and concrete Load classes now must define a '''ApplyLoad''' method that will apply the load to the element specified. | |||
===itk::fem::LoadBC=== | ===itk::fem::LoadBC=== | ||
# | #The following class variables were moved from public to protected: | ||
#*'''m_dof''' | #*'''m_dof''' - renamed to m_DegreeOfFreedom | ||
#*'''m_element''' | #*'''m_element''' - renamed to m_Element | ||
#*'''m_value''' | #*'''m_value''' - renamed to m_Value | ||
#The following class variable was removed | |||
#*'''CLID''' | #*'''CLID''' | ||
#The following class functions | #The following class functions were added, allowing the user to get and set the class variables: | ||
#*void ''' | #*void '''SetDegreeOfFreedom'''(unsigned int dof) – Set the degree of freedom for the local element for which the boundary condition is being applied. | ||
#*void '''SetElement'''(Element::ConstPointer element) – Set the element for which the boundary condition is being applied. | #*void '''SetElement'''(Element::ConstPointer element) – Set the element for which the boundary condition is being applied. | ||
#*void '''SetValue'''(vnl_vector< Element::Float > value) – Set the boundary condition using a vector representation. This can be used to restrict motion of an element in a particular direction. | #*void '''SetValue'''(vnl_vector< Element::Float > value) – Set the boundary condition using a vector representation. This can be used to restrict motion of an element in a particular direction. | ||
#*unsigned int ''' | #*unsigned int '''GetDegreeOfFreedom'''() – Returns the local degree of freedom for the element on which the boundary condition is being applied. | ||
#*Element::ConstPointer '''GetElement'''() – Returns the element on which the boundary condition is being applied. | #*Element::ConstPointer '''GetElement'''() – Returns the element on which the boundary condition is being applied. | ||
#*vnl_vector< Element::Float > '''GetValue'''() – Returns the assigned boundary condition. | #*vnl_vector< Element::Float > '''GetValue'''() – Returns the assigned boundary condition. | ||
===itk::fem::LoadBCMFC=== | ===itk::fem::LoadBCMFC=== | ||
# | #The following class variables were moved from public to protected: | ||
#*'''Index''' | #*'''Index''' - renamed to m_Index | ||
#*'''lhs''' | #*'''lhs''' - renamed to m_LeftHandSide | ||
#*'''rhs''' | #*'''rhs''' - renamed to m_RightHandSide | ||
#Add class methods to get and set the class variables. | #Add class methods to get and set the class variables. | ||
#*void '''SetIndex'''(int) – Set the index variable for the multi freedom displacement constraint. This is used internally by the itk::Fem::Solver. | #*void '''SetIndex'''(int) – Set the index variable for the multi freedom displacement constraint. This is used internally by the itk::Fem::Solver. | ||
Line 71: | Line 202: | ||
#*int '''GetNumberOfRightHandSideTerms'''()– Returns the number of terms used to define the left hand side of the multi freedom displacement constraint. | #*int '''GetNumberOfRightHandSideTerms'''()– Returns the number of terms used to define the left hand side of the multi freedom displacement constraint. | ||
#*Element::Float '''GetRightHandSideTerm'''(int index) – Returns the specified right hand side term. | #*Element::Float '''GetRightHandSideTerm'''(int index) – Returns the specified right hand side term. | ||
#*std::vector< MFCTerm >& '''GetLeftHandSideArray'''() - Get the left hand side of the FEM system | |||
#*vnl_vector< Element::Float >& '''GetRightHandSideArray'''() - Get the right hand side of the FEM system | |||
===itk::fem::LoadEdge=== | ===itk::fem::LoadEdge=== | ||
# | #The following member variables were moved from public to protected signatures: | ||
#*'''m_Edge''' | #*'''m_Edge''' | ||
#*'''m_Force''' | #*'''m_Force''' | ||
# | #The following public membership functions are now provided to get/set class variables | ||
#*void '''SetEdge'''(int) – Set the edge to apply the desired force. | #*void '''SetEdge'''(int) – Set the edge to apply the desired force. | ||
#*void '''SetForce'''(vnl_matrix< Float >) – Set the force to be applied to an edge. | #*void '''SetForce'''(vnl_matrix< Float >) – Set the force to be applied to an edge. | ||
#*int '''GetEdge'''() – Get the edge for the applied force. | #*int '''GetEdge'''() – Get the edge for the applied force. | ||
#*vnl_matrix< Float > '''GetForce'''() – Get the force applied. | #*vnl_matrix< Float > '''GetForce'''() – Get the force applied. | ||
#*void '''ApplyLoad'''(Element::ConstPointer element, Element::VectorType & Fe) - Apply the load to the elements on which it is applied. | |||
===itk::fem::LoadGravConst=== | ===itk::fem::LoadGravConst=== | ||
#The following member variables were moved from public to protected signatures: | |||
#*'''Fg_value''' - renamed m_GravityForce | |||
void SetForce(vnl_vector< Float >) – Set the constant force | #The following public membership functions are now provided to get/set class variables: | ||
vector that exists for every point in space. | #*void '''SetForce'''(vnl_vector< Float >) – Set the constant force vector that exists for every point in space. | ||
vnl_vector< Float > GetForce() – Return the constant force | #*vnl_vector< Float > '''GetForce'''() – Return the constant force vector that exists for every point in space. | ||
vector that exists for every point in space. | #*vnl_vector< Float > '''GetGravitationalForceAtPoint'''(vnl_vector< Float > ) - Return the force at the specified location. The same value is returned at every point. | ||
#*void '''ApplyLoad'''(Element::ConstPointer element, Element::VectorType & Fe) - Apply the load to the specified element. | |||
===itk::fem::LoadLandmark=== | ===itk::fem::LoadLandmark=== | ||
#The following member variables were moved from public to protected signatures: | |||
m_Target | #*'''eta''' - renamed to m_Eta | ||
#*'''m_force''' - renamed to m_Force | |||
void SetEta(double) – Set the square root of the variance. | #*'''m_pt''' - renamed to m_Point | ||
double GetEta(double) – Get the square root of the variance. | #*'''m_Solution''' | ||
#*'''m_Source''' | |||
#*'''m_Target''' | |||
#The following public membership functions are now provided to get/set class variables: | |||
#*void '''SetEta'''(double) – Set the square root of the variance. | |||
#*double '''GetEta'''(double) – Get the square root of the variance. | |||
#*void '''ApplyLoad'''(Element::ConstPointer element, Element::VectorType & Fe) - Apply load the specified element | |||
#*itk::LightObject::Pointer '''CreateAnother'''(void) - Create a copy of the load | |||
===itk::fem::LoadNode=== | ===itk::fem::LoadNode=== | ||
#The following member variables were moved from public to protected signatures: | |||
#*'''F''' - renamed m_Force | |||
void SetForce(vnl_vector< Float >) – Set the applied force to | #*'''m_element''' - renamed m_Element | ||
the node. | #*'''m_pt''' - renamed m_Point | ||
void SetElement(Element::ConstPointer) – Set the element in | #The following public membership functions are now provided to get/set class variables: | ||
the system that contains the degrees of freedom on which | #*void '''SetForce'''(vnl_vector< Float >) – Set the applied force to the node. | ||
the force is applied. | #*void '''SetElement'''(Element::ConstPointer) – Set the element in the system that contains the degrees of freedom on which the force is applied. | ||
void SetNode(unsigned int) – Set the point on which the force | #*void '''SetNode'''(unsigned int) – Set the point on which the force is being applied. | ||
is being applied. | #*vnl_vector< Float > &'''GetForce'''() – Get the applied force. | ||
vnl_vector< Float > &GetForce() – Get the applied force. | #*Element::ConstPointer '''GetElement'''() – Get the element in the system that contains the degrees of freedom on which the force is applied. | ||
Element::ConstPointer GetElement() – Get the element in the | #*Unsigned int '''GetNode'''() – Get the point on which the force is being applied. | ||
system that contains the degrees of freedom on which the | #*itk::LightObject::Pointer '''CreateAnother'''(void) - Create a copy of the load | ||
force is applied. | #*void '''ApplyLoad'''(Element::ConstPointer element, Element::VectorType & Fe) - Apply load the specified element | ||
Unsigned int GetNode() – Get the point on which the force is | |||
being applied. | |||
===itk::fem::LoadPoint=== | ===itk::fem::LoadPoint=== | ||
#Moved the following class variables from public to protected signatures: | |||
#*'''Fp''' - renamed m_Point | |||
#*'''point''' -renamed m_ForcePoint | |||
void SetForce(vnl_vector<Float>) – Set the force to be applied | #The following public membership functions are now provided to get/set class variables: | ||
to the specified point location. | #*void '''SetForce'''(vnl_vector<Float>) – Set the force to be applied to the specified point location. | ||
void SetPoint(vnl_vector<Float>) – Set the point where the | #*void '''SetPoint'''(vnl_vector<Float>) – Set the point where the force is applied in global coordinates. | ||
force is applied in global coordinates. | #*vnl_vector<Float> & '''GetForce'''(vnl_vector<Float>) – Get the applied force. | ||
vnl_vector<Float> & GetForce(vnl_vector<Float>) – Get the | #*vnl_vector<Float> & '''GetPoint''' (vnl_vector<Float>) – Get the point where the force is applied. | ||
applied force. | #*itk::LightObject::Pointer '''CreateAnother'''(void) - Create a copy of the load | ||
vnl_vector<Float> & GetPoint (vnl_vector<Float>) – Get the | #*void '''ApplyLoad'''(Element::ConstPointer element, Element::VectorType & Fe) - Apply load the specified element | ||
point where the force is applied. | |||
==itk::Solver== | |||
The [http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Solver.html itk::fem::Solver] is a templated class over the problem dimension. This is now a proper ITK filter that inherits from [http://www.itk.org/Doxygen/html/classitk_1_1ProcessObject.html ProcessObject]. The filter takes as input a FEMObject and will generate an output FEMObject. To get the Solver to generate a solution, the '''Update()''' method needs to be called. | |||
#The following class variables were removed: | |||
#*'''node''' | |||
#*'''el''' | |||
#*'''load''' | |||
#*'''mat''' | |||
#The following class functions were removed: | |||
#*'''Read'''() | |||
#*'''Write'''() | |||
#*'''Clear'''() | |||
#The following class functions were moved from public to protected | |||
#*'''AssembleK'''() | |||
#*'''GenerateGFN'''() | |||
#*'''InitializeMatrixForAssembly'''() | |||
#*'''FinalizeMatrixAfterAssembly'''() | |||
#*'''AssembleElementMatrix'''() | |||
#*'''AssembleLandmarkContribution'''() | |||
#*'''ApplyBC'''() | |||
#*'''AssembleF'''() | |||
#*'''DecomposeK'''() | |||
#*'''RunSolver'''() | |||
#*'''UpdateDisplacements'''() | |||
#*'''InitializeLinearSystemWrapper'''() | |||
#The following public membership functions were added | |||
#*'''SetInput'''() | |||
#*'''GetInput'''() | |||
#*'''GetOutput'''() | |||
#*'''MakeOutput'''() | |||
#*'''PrintSelf'''() | |||
==itk::FEMObjectSpatialObject== | |||
To support I/O we have developed a new spatial object, [http://www.itk.org/Doxygen/html/classitk_1_1FEMObjectSpatialObject.html FEMObjectSpatialObject], was created. A fairly simple conversion is used to convert from a FEMObjectSpatialObject to FEMObject and vice-versa. The public class methods provides this interface. | |||
#void '''SetFEMObject'''( FEMObjectType * femobject ) | |||
#FEMObjectType * '''GetFEMObject'''( void ) | |||
The FEM objects can then be read and written using the [http://www.itk.org/Doxygen/html/classitk_1_1SpatialObjectReader.html itk::SpatialObjectReader] and [http://www.itk.org/Doxygen/html/classitk_1_1SpatialObjectWriter.html itk::SpatialObjectWriter] respectively. | |||
==File Formats== | |||
The only file format that is currently supported for FEM objects is the [http://www.vtk.org/Wiki/MetaIO/Documentation MetaIO file format]. This file format supports the Spatial Objects used in ITK and VTK. The complete information regarding this file format can be found on the [http://www.vtk.org/Wiki/MetaIO/Documentation VTK Wiki]. We has extended the MetaIO format to support the FEM spatial objects that have been added to ITK. Here we outline some of the basic information regarding the file format and its extension to support FEM spatial objects. | |||
The file format has header information that defines the object(s) stored within the file as well as other information such as the dimension, how the data is stored, etc. Comments are allowed in the file format. Any characters that follow a '''%''' symbol are defined as comments. The header contains the following elements: | |||
*'''ObjectType =''' %Defines the type of object stored (e.g. Scene or FEMObject) | |||
*'''NDims =''' %Defines the dimension for the stored object | |||
*'''NObjects =''' %Defines the number of objects if the object is a Scene | |||
*'''BinaryData =''' %Defines if the data is binary or not. Binary data is not yet supported | |||
*'''Offset =''' %Defines the position offset - Currently ignored for FEMObject | |||
*'''CenterOfRotation =''' %Defines center of rotation - Currently ignored for FEMObject | |||
*'''ElementSpacing =''' %Defines element spacing - Currently ignored for FEMObject | |||
*'''ElementDataFile =''' %Defines where the data is stored ('''LOCAL''' means the data is stored within the same file. | |||
If you enable ITK Testing, then you will download several examples of file format that are used for testing. The data will be downloaded into the build directory ''ExternalData/Modules/Numerics/FEM/test/Input''. | |||
Below the header the user defines the FEM Object using the following format. Typically the user will define the Nodes, followed by Materials, Elements, and Loads. | |||
===Nodes=== | |||
Each node is denoted with a '''<Node>''' marker and then the global node number is specified followed by its position. The dimension of the position is defined by the '''Ndims''' field in the META spatial object header. After all of the nodes are defined there should be a '''<END>''' marker. Here is an example. | |||
<Node> | |||
0 % Global node number | |||
2 0 0 % Nodal coordinates | |||
<Node> | |||
1 % Global node number | |||
2 1 0 % Nodal coordinates | |||
<Node> | |||
2 % Global node number | |||
2 1 2 % Nodal coordinates | |||
<END> % End of nodes | |||
===Materials=== | |||
Similar to Nodes, Materials are specified by a maker that defines the type of material being defined. Presently, there is only one material property type, '''MaterialLinearElasticity'''. The material marker is followed by all of the parameters that are used to define the material (Global Number, Young modulus, Cross-sectional area, Moment of inertia, Poisson's ratio, thickness, and Density Heat Capacity). Here is an example | |||
<MaterialLinearElasticity> | |||
0 % Global material number | |||
E : 30000000 % Young modulus | |||
A : 0 % Crossection area | |||
I : 0 % Moment of inertia | |||
nu : 0.3 % Poisson's ratio | |||
h : 1 % Thickness | |||
RhoC : 1 % Density Heat Capacity | |||
END: % End of current material definition | |||
<END> % End of materials | |||
===Elements=== | |||
Elements define the type of element, the global number, the global number of the nodes used to define the element, and the global number for the material. The following element types are supported | |||
*Element2DC1Beam | |||
*Element2DC0LinearQuadrilateralStress | |||
*Element2DC0LinearQuadrilateralStrain | |||
*Element2DC0LinearQuadrilateralMembrane | |||
*Element2DC0LinearTriangularStress | |||
*Element2DC0LinearTriangularStrain | |||
*Element2DC0LinearTriangularMembrane | |||
*Element2DC0QuadraticTriangularStress | |||
*Element2DC0QuadraticTriangularStrain | |||
*Element3DC0LinearHexahedronMembrane | |||
*Element3DC0LinearHexahedronStrain | |||
*Element3DC0LinearTetrahedronMembrane | |||
*Element3DC0LinearTetrahedronStrain | |||
*Element3DC0LinearTriangularLaplaceBeltrami | |||
*Element3DC0LinearTriangularMembrane | |||
Here is an example definition of two elements. | |||
<Element2DC0LinearTriangularStress> | |||
0 % Global element number | |||
0 % Node 1 ID | |||
1 % Node 2 ID | |||
2 % Node 3 ID | |||
0 % MaterialLinearElasticity ID | |||
<Element2DC0LinearTriangularStress> | |||
1 % Global element number | |||
0 % Node 1 ID | |||
2 % Node 2 ID | |||
3 % Node 3 ID | |||
0 % MaterialLinearElasticity ID | |||
<END> % End of elements | |||
===Loads=== | |||
Similar structures are used for Loads. Each load definition is slightly different depending on the type of load. Here are examples that show how to define each of the Load types in the Meta file. AT the end of the load section there should be a '''<END>''' marker to signal the end of loads. | |||
<LoadNode> | |||
0 % Global load number | |||
1 % GN of the element on which the load acts | |||
2 % Point number within the element | |||
2 0 -1000 % Force vector | |||
<LoadBC> | |||
1 % Global load number | |||
0 % GN of element | |||
0 % DOF# in element | |||
1 0 % rhs of MFC | |||
<LoadBCMFC> | |||
5 % Global object number | |||
2 % Number of DOFs in this MFC | |||
%==> | |||
0 % GN of element | |||
1 % DOF# in element | |||
1 % weight | |||
%==> | |||
1 % GN of element | |||
3 % DOF# in element | |||
-1 % weight | |||
%==> | |||
1 0 % rhs of MFC | |||
<LoadEdge> | |||
0 % Global load number | |||
0 % GN of the element on which the load acts | |||
1 % Edge number | |||
2 2 % # Rows and Columns in force matrix | |||
10 0 % Force Matrix - Row 1 | |||
5 0 % Force Matrix - Row 2 | |||
<LoadGravConst> | |||
1 % Global load number | |||
1 % Number of elements on which the load acts | |||
1 % GN of the element on which the load acts | |||
2 0 -12.762 % Force vector | |||
<LoadLandmark> | |||
0 % Global load number | |||
2 0. 0. % Original point location | |||
2 0. 1. % Deformed point location | |||
1.e-2 % Square root of landmark variance | |||
<LoadPoint> | |||
0 % Global load number | |||
2 0. 0. % Point of applied load | |||
2 0. 1. % Applied force on the point | |||
'''NOTE: LoadPoint needs to be added. Prototype now works''' | |||
==Other Formats== | |||
The has been discussion about support for other formats. While these formats in general will support the geometry (nodes and elements) of the problem, they do not support the loads or material properties. People have expressed interest for these formats. | |||
*gmsh format | |||
**Used to support Netgen | |||
*VTK Format | |||
**tetgen support | |||
**VTK visualization | |||
=Assembling and Solving FEM in ITK= | |||
There are three ways to define a FEMObject in ITK. The first is to read the object as a spatial object, the second is to manually assemble the FEM object, and the third is to create the FEMObject based upon an image. Once the FEMObject is defined the object can be used as input for the Solver and a solution generated. | |||
==Loading a FEMObject as a SpatialObject== | |||
Here is an example of how to load a FEMOBject from a SpatialObject file. This example defines a 3D problem. | |||
<source lang="cpp"> | |||
//Need to register default FEM object types, | |||
//and setup SpatialReader to recognize FEM types | |||
//which is all currently done as a HACK in | |||
//the initializaiton of the itk::FEMFactoryBase::GetFactory() | |||
itk::FEMFactoryBase::GetFactory()->RegisterDefaultTypes(); | |||
typedef itk::FEMSpatialObjectReader<3> FEMSpatialObjectReaderType; | |||
typedef FEMSpatialObjectReaderType::Pointer FEMSpatialObjectReaderPointer; | |||
FEMSpatialObjectReaderPointer SpatialReader = FEMSpatialObjectReaderType::New(); | |||
SpatialReader->SetFileName( argv[1] ); | |||
SpatialReader->Update(); | |||
FEMSpatialObjectReaderType::ScenePointer myScene = SpatialReader->GetScene(); | |||
typedef itk::FEMObjectSpatialObject<3> FEMObjectSpatialObjectType; | |||
typedef FEMObjectSpatialObjectType::Pointer FEMObjectSpatialObjectPointer; | |||
FEMObjectSpatialObjectType::ChildrenListType* children = SpatialReader->GetGroup()->GetChildren(); | |||
//Verify that the Spatial Object contains a FEMObjectSpatialObject. If not then exit | |||
if( strcmp( (*(children->begin() ) )->GetTypeName(), "FEMObjectSpatialObject") ) | |||
{ | |||
return EXIT_FAILURE; | |||
} | |||
// Get the Spatial Object and cast appropriately | |||
FEMObjectSpatialObjectType::Pointer femSO = | |||
dynamic_cast<FEMObjectSpatialObjectType *>( (*(children->begin() ) ).GetPointer() ); | |||
// Get the FEMObject from the Spatial Object | |||
typedef itk::fem::FEMObject<3> FEMObjectType; | |||
FEMObjectType::Pointer femObject = femSO->GetFEMObject(); | |||
femObject->FinalizeMesh(); | |||
</source> | |||
==Manually defining a FEMObject== | |||
Here is an example how to manually define a FEMObject directly in C++. This is a 2D example. | |||
<source lang="cpp"> | |||
/* Create a FEMObject for storing the FEM problem */ | |||
typedef itk::fem::FEMObject<2> FEMObjectType; | |||
FEMObjectType::Pointer femObject = FEMObjectType::New(); | |||
/* First define nodes - Here we define 5 nodes */ | |||
itk::fem::Node::Pointer n1; | |||
itk::fem::Element::VectorType pt(2); | |||
// Node 0 | |||
n1 = itk::fem::Node::New(); | |||
pt[0] = 0.0; | |||
pt[1] = 0.0; | |||
n1->SetCoordinates(pt); | |||
n1->SetGlobalNumber(0); | |||
femObject->AddNextNode(&*n1); | |||
// Node 1 | |||
n1 = itk::fem::Node::New(); | |||
pt[0] = 1500.0; | |||
pt[1] = 0.0; | |||
n1->SetCoordinates(pt); | |||
n1->SetGlobalNumber(1); | |||
femObject->AddNextNode(&*n1); | |||
// Node 2 | |||
n1 = itk::fem::Node::New(); | |||
pt[0] = 3000.0; | |||
pt[1] = 0.0; | |||
n1->SetCoordinates(pt); | |||
n1->SetGlobalNumber(2); | |||
femObject->AddNextNode(&*n1); | |||
// Node 3 | |||
n1 = itk::fem::Node::New(); | |||
pt[0] = 3000.0; | |||
pt[1] = 3000.0; | |||
n1->SetCoordinates(pt); | |||
n1->SetGlobalNumber(3); | |||
femObject->AddNextNode(&*n1); | |||
// Node 4 | |||
n1 = itk::fem::Node::New(); | |||
pt[0] = 0.0; | |||
pt[1] = 4500.0; | |||
n1->SetCoordinates(pt); | |||
n1->SetGlobalNumber(4); | |||
femObject->AddNextNode(&*n1); | |||
/* Define the Element Material properties */ | |||
itk::fem::MaterialLinearElasticity::Pointer m; | |||
// Material 0 | |||
m = itk::fem::MaterialLinearElasticity::New(); | |||
m->SetGlobalNumber(0); /* Global number of the material */ | |||
m->SetYoungsModulus(200000000000.0); /* Young modulus */ | |||
m->SetPoissonsRatio(0.3); /* Poissons Ratio */ | |||
m->SetCrossSectionalArea(2000.0); /* Crossection area */ | |||
m->SetMomentOfInertia(1.0); /* Moment of inertia */ | |||
femObject->AddNextMaterial(&*m); | |||
m = itk::fem::MaterialLinearElasticity::New(); | |||
// Material 1 | |||
m->SetGlobalNumber(1); /* Global number of the material */ | |||
m->SetYoungsModulus(200000.0); /* Young modulus */ | |||
m->SetPoissonsRatio(0.3); /* Poissons Ratio */ | |||
m->SetCrossSectionalArea(1200.0); /* Crossection area */ | |||
m->SetMomentOfInertia(1.0); /* Moment of inertia */ | |||
femObject->AddNextMaterial(&*m); | |||
m = itk::fem::MaterialLinearElasticity::New(); | |||
// Material 2 | |||
m->SetGlobalNumber(2); /* Global number of the material */ | |||
m->SetYoungsModulus(70000.0); /* Young modulus */ | |||
m->SetPoissonsRatio(0.3); /* Poissons Ratio */ | |||
m->SetCrossSectionalArea(900.0); /* Crossection area */ | |||
m->SetMomentOfInertia(1.0); /* Momemt of inertia */ | |||
femObject->AddNextMaterial(&*m); | |||
/* Define the Elements */ | |||
itk::fem::Element2DC0LinearLineStress::Pointer e1; | |||
// Element 0 | |||
e1 = itk::fem::Element2DC0LinearLineStress::New(); | |||
e1->SetGlobalNumber(0); | |||
e1->SetNode( 0, &*femObject->GetNode(0) ); | |||
e1->SetNode( 1, &*femObject->GetNode(1) ); | |||
e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(0) ) ); | |||
femObject->AddNextElement( &*e1); | |||
e1 = itk::fem::Element2DC0LinearLineStress::New(); | |||
// Element 1 | |||
e1->SetGlobalNumber(1); | |||
e1->SetNode( 0, &*femObject->GetNode(1) ); | |||
e1->SetNode( 1, &*femObject->GetNode(2) ); | |||
e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(0) ) ); | |||
femObject->AddNextElement( &*e1); | |||
e1 = itk::fem::Element2DC0LinearLineStress::New(); | |||
// Element 2 | |||
e1->SetGlobalNumber(2); | |||
e1->SetNode( 0, &*femObject->GetNode(1) ); | |||
e1->SetNode( 1, &*femObject->GetNode(3) ); | |||
e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(2) ) ); | |||
femObject->AddNextElement( &*e1); | |||
e1 = itk::fem::Element2DC0LinearLineStress::New(); | |||
// Element 3 | |||
e1->SetGlobalNumber(3); | |||
e1->SetNode( 0, &*femObject->GetNode(0) ); | |||
e1->SetNode( 1, &*femObject->GetNode(4) ); | |||
e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(1) ) ); | |||
femObject->AddNextElement( &*e1); | |||
/* Define the Loads */ | |||
itk::fem::LoadBC::Pointer l1; | |||
// Load 0 | |||
l1 = itk::fem::LoadBC::New(); | |||
l1->SetGlobalNumber(0); | |||
l1->SetElement( &*femObject->GetElement(2) ); | |||
l1->SetDegreeOfFreedom(2); | |||
l1->SetValue( vnl_vector< double >(1, 0.0) ); | |||
femObject->AddNextLoad( &*l1); | |||
// Load 1 | |||
l1 = itk::fem::LoadBC::New(); | |||
l1->SetGlobalNumber(1); | |||
l1->SetElement( &*femObject->GetElement(2) ); | |||
l1->SetDegreeOfFreedom(3); | |||
l1->SetValue( vnl_vector< double >(1, 0.0) ); | |||
femObject->AddNextLoad( &*l1); | |||
// Load 2 | |||
l1 = itk::fem::LoadBC::New(); | |||
l1->SetGlobalNumber(2); | |||
l1->SetElement( &*femObject->GetElement(3) ); | |||
l1->SetDegreeOfFreedom(2); | |||
l1->SetValue( vnl_vector< double >(1, 0.0) ); | |||
femObject->AddNextLoad( &*l1); | |||
// Load 3 | |||
l1 = itk::fem::LoadBC::New(); | |||
l1->SetGlobalNumber(3); | |||
l1->SetElement( &*femObject->GetElement(3) ); | |||
l1->SetDegreeOfFreedom(3); | |||
l1->SetValue( vnl_vector< double >(1, 0.0) ); | |||
femObject->AddNextLoad( &*l1); | |||
// Load 4 | |||
itk::fem::LoadNode::Pointer l2; | |||
l2 = itk::fem::LoadNode::New(); | |||
l2->SetGlobalNumber(4); | |||
l2->SetElement( femObject->GetElement(1) ); | |||
l2->SetNode(0); | |||
vnl_vector< double > F(2); | |||
F[0] = 0; | |||
F[1] = 30000; | |||
l2->SetForce(F); | |||
femObject->AddNextLoad( &*l2 ); | |||
// Load 5 | |||
itk::fem::LoadBCMFC::Pointer bcmfc = itk::fem::LoadBCMFC::New(); | |||
bcmfc->SetGlobalNumber(5); | |||
bcmfc->AddLeftHandSideTerm( itk::fem::LoadBCMFC::MFCTerm(femObject->GetElement(0), 1, 1) ); | |||
bcmfc->AddLeftHandSideTerm( itk::fem::LoadBCMFC::MFCTerm(femObject->GetElement(1), 3, -1) ); | |||
bcmfc->AddRightHandSideTerm(0.0); | |||
femObject->AddNextLoad( &*bcmfc ); | |||
</source> | |||
==Define a FEMObject from an image== | |||
This example shows the user how to convert an image into a FEMObject. This will define nodes, elements, and materials for the FEMObject. A constant material property will be used in this conversion. No loads are defined in the resulting FEMObject. The user will have to add the loads to be applied to the FEMObject. | |||
<source lang="cpp"> | |||
//Need to register default FEM object types, | |||
//and setup SpatialReader to recognize FEM types | |||
//which is all currently done as a HACK in | |||
//the initializaiton of the itk::FEMFactoryBase::GetFactory() | |||
itk::FEMFactoryBase::GetFactory()->RegisterDefaultTypes(); | |||
// Define the image | |||
typedef itk::Image<unsigned char, 2> ImageType; | |||
typedef itk::ImageFileReader<ImageType> ImageFileReaderType; | |||
vnl_vector<unsigned int> pixelsPerElement; | |||
pixelsPerElement.set_size(2); | |||
pixelsPerElement[0] = 1; | |||
pixelsPerElement[1] = 1; | |||
ImageFileReaderType::Pointer reader = ImageFileReaderType::New(); | |||
reader->SetFileName(argv[1]); | |||
reader->Update(); | |||
/* Define the Material and Element Type to be used */ | |||
typedef itk::fem::MaterialLinearElasticity ElasticityType; | |||
ElasticityType::Pointer m; | |||
m = ElasticityType::New(); | |||
m->SetGlobalNumber(0); | |||
m->SetYoungsModulus(3000.0); | |||
m->SetCrossSectionalArea(0.02); | |||
m->SetMomentOfInertia(0.004); | |||
typedef itk::fem::Element2DC0LinearQuadrilateralMembrane MembraneElementType; | |||
MembraneElementType::Pointer e0 = MembraneElementType::New(); | |||
e0->SetGlobalNumber(0); | |||
e0->SetMaterial( dynamic_cast<ElasticityType *>( m.GetPointer() ) ); | |||
/* Convert the image to FEMObject */ | |||
typedef itk::fem::ImageToRectilinearFEMObjectFilter<ImageType> MeshFilterType; | |||
MeshFilterType::Pointer meshFilter = MeshFilterType::New(); | |||
meshFilter->SetInput( reader->GetOutput() ); | |||
meshFilter->SetPixelsPerElement( pixelsPerElement ); | |||
meshFilter->SetElement( e0.GetPointer() ); | |||
meshFilter->Update(); | |||
typedef itk::fem::FEMObject<2> FEMObjectType; | |||
FEMObjectType::Pointer femObject = meshFilter->GetOutput(); | |||
</source> | |||
==Solving a FEM problem== | |||
Once a FEMObject is created to define the FEM problem, the [http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1Solver.html itk::fem::Solver] is then used to solve the FEM problem and create the deformed FEMObject. The solver provides a generic interface for providing the actual numeric solver used to generate the results. All of the numeric solvers are derived from the [http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1LinearSystemWrapper.html LinearSystemWrapper] class and support linear solutions to the set of linear equations. Presently three numeric solvers are supported: | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1LinearSystemWrapperItpack.html Itpack] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1LinearSystemWrapperDenseVNL.html DenseVNL] | |||
#[http://www.itk.org/Doxygen/html/classitk_1_1fem_1_1LinearSystemWrapperVNL.html VNL] | |||
Additional numeric solutions can be defined using this generic interface. | |||
Here is an example of how to use the Solver to generate a result from a FEM problem. | |||
<source lang="cpp"> | |||
typedef itk::fem::FEMObject<3> FEMObjectType; | |||
FEMObjectType::Pointer fem = FEMObjectType::New(); | |||
. | |||
. | |||
. | |||
itk::fem::LinearSystemWrapperVNL lsw_vnl; | |||
SolverType::Pointer solver = SolverType::New(); | |||
solver->SetInput( fem ); | |||
solver->SetLinearSystemWrapper(&lsw_dvnl); | |||
solver->Update(); | |||
FEMObjectType::Pointer result = solver->GetOutput(); | |||
</source> | |||
=Update the itk::FEMRegistrationFilter= | |||
To make the FEMRegistrationFilter similar to the other registration filters the following changes will be made | |||
#Remove these public member functions. The application programmer will be responsible for loading the images and setting the parameters for the filter. The configuration file will be removed. | |||
#*'''GetConfigFileName''' () | |||
#*'''GetFixedFile''' () | |||
#*'''GetMovingFile''' () | |||
#*'''GetResultsFileName''' () | |||
#*'''GetWriteDisplacements''' () | |||
#*'''ReadConfigFile''' (const char *) | |||
#*'''SetConfigFileName''' (const char *f) | |||
#*'''SetDisplacementsFile''' (const char *r) | |||
#*'''SetFixedFile''' (const char *t) | |||
#*'''SetLandmarkFile''' (const char *l) | |||
#*'''SetMovingFile''' (const char *r) | |||
#*'''SetResultsFile''' (const char *r) | |||
#*'''SetResultsFileName''' (const char *f) | |||
#*'''WriteDisplacementField''' (unsigned int index) | |||
#*'''WriteDisplacementFieldMultiComponent''' () | |||
#*'''WriteWarpedImage''' (const char *fn) | |||
#Add these new public member functions | |||
#*bool '''AddNextMovingLandmark'''(PointType) – Add another point for the moving image to the registration. | |||
#*bool '''AddNextFixedLandmark'''(PointType) – Add another point from the fixed image to the registration. | |||
#*void '''ClearMovingLandmarks'''() – Remove all of the existing landmarks for the moving image. | |||
#*void '''ClearFixedLandmarks'''() – Remove all of the existing landmarks for the fixed image. | |||
#*bool '''InsertMovingLandmark'''(PointType p, int index) – Insert the moving landmark at the specified index. | |||
#*bool '''InsertFixedLandmark'''(PointType p, int index) – Insert the fixed landmark at the specified index. | |||
#*bool '''RemoveMovingLandmark'''(int index) | |||
#*bool '''RemoveFixedLandmark'''(int index) | |||
#*void '''SetFEMesh'''() – Set the mesh to be used for the registration. | |||
#*void '''GetFEMesh'''() – Get the mesh used in the registration. | |||
#*void '''SetUserDefinedMesh'''(bool) – Specifies if the user will provide a custom mesh or if it will be generated based on the image. | |||
=Data Structure= | |||
*Spatial Objects - | |||
*ITK::Mesh - | |||
*VTK Unstructured Grids - | |||
=Solvers= | |||
*SuperLU | |||
*PETSc |
Latest revision as of 23:23, 18 September 2011
This page outlines the proposed changes to the itk::FEM framework. A number of these changes will break backwards compatibility.
Submission Of Code
http://review.source.kitware.com/#change,1993
Authors
The proposed changes for the itk::FEM framework are being proposed by the University of Iowa. This group is made up of investigators from the Departments of Radiology, Biomedical Engineering, and Psychiatry. Questions and comments regarding these changes should be sent to
- Vincent Magnotta (vincent-magnotta -at- uiowa.edu) - Radiology
Other team members include
- Nicole Grosland - Biomedical Engineering, MIMX
- Kiran Shivanna - MIMX
- Hans Johnson - Psychiatry
- Kent Williams - Psychiatry
Changes to the FEM Framework Classes
itk::fem::FEMLightObject
This class has been updated to remove public access to the class variables. All fem classes derive from this class. This class was also converted to support smart pointers. Support for the old method where the pointers were not smart pointers has been removed. The ITK framework should now behave similar to the rest of ITK in regards to object pointers. Public access to class member variables has been eliminated in this and all derived classes. The use should now use public membership functions to get or set the value of internal class member variables. These are major changes to the class are outlined here:
- Moved the following class variables from public to protected:
- GN - This variable was renamed to m_GlobalNumber
- Removed the following class functions:
- Clone()
- ClassID()
- Read()
- Write()
- CreateFromStream()
- SkipWhiteSpace()
- The following member functions were added to support setting the global number for fem objects
- void SetGlobalNumber(int)
- int GetGlobalNumber()
In addition, we have removed all of the graphics capabilities from the fem classes. In addition, I/O was removed from the classes as well. The user must now use the Spatial Objects to support I/O for FEM.
itk::FEMObject
In the prior versions of ITK (< 4.0) the itk::fem framework offers a complicated interface for specifying mesh to be used for analysis. The defintion of the FEM problem (nodes, elements, materials, and loads) was done in the Solver. We have created a new ITK object called itk::FEMObject that will define the entire FE problem. The itk::FEMObject inherits from itk::DataObject. This object allows the user to completely specify the Nodes, Elements, Loads, and Materials for the problem. This object was based on the decision to make a parallel structure to itk::Mesh without a complete refactoring of the mesh data structure. This also removes the specification of the FE simulation from the solver.
In order to support input/output, we have decided to use SpatialObjects. To support the new FEMObject, a new FEMObjectSpatialObject was created. This also required that additions be added to the metaIO library to support this new spatial object type. The minor disadvantage of this implementation is that new element, load, and material types will require additions in multiple locations (ie both in the fem framework as well as the metaIO library). We have decided to use the same FEM file format for the model specification that was used previously, but it is now encoded in a spatial object.
Migration Guide
The itk::fem::FEMObject was created to provide support for a first class object in ITK that will define the finite element problem being used for analysis. This used to be defined only in the itk::fem::Solver. All support for file I/O was removed from this class. The user now defines a FEMObject object and use this an input into the solver. Here is an outline on how to define a new problem.
<source lang="cpp">
itk::fem::FEMObject::Pointer fem = itk::fem::FEMObject::New(); . . . itk::fem::Solver::Pointer solver = itk::fem::Solver::New(); solver->SetInput( fem ); solver->Update( ); itk::fem::FEMObject::Pointer result = solver->GetOutput();
</source>
The FEMObject has interfaces to define the nodes, elements, materials, and loads used to define the finite element problem. Here are the public member functions used to manipulate the various portions of the FEM problem.
- Nodes:
- AddNextNode (Element::Node::Pointer e) - Add another node to the FEM problem. This will be added as the last node in the node container
- InsertElement (Element::Pointer e, ElementIdentifier index) - Add another node at the specified position in the node container
- GetNode (NodeIdentifier index) - Get the node at the specified index of the node container
- GetNodeWithGlobalNumber (int globalNumber) - Get the node with the specified global number. This is independent of the order in the node container, and is defined by the user when defining the FEM problem.
- GetNumberOfElements (void) - Get number of nodes in the FEM problem
- GetNodeContainer () - Get the entire node container
- Elements:
- AddNextElement (Element::Pointer e) - Add another element to the FEM problem. This will be added as the last element in the element container
- GetElementContainer () - Get the entire element container
- GetNumberOfElements (void) - Get number of elements in the FEM problem
- InsertElement (Element::Pointer e, ElementIdentifier index) - Add another element at the specified position in the element container
- GetElement (ElementIdentifier index) - Get the element at the specified index of the element container
- GetElementWithGlobalNumber (int globalNumber) - Get the element with the specified global number. This is independent of the order in the element container, and is defined by the user when defining the FEM problem.
- Loads:
- AddNextLoad() - Add another load to the FEM problem. This will be added as the last load in the load container
- InsertLoad() - Add another load at the specified position in the load container
- GetNumberOfLoads() - Get number of loads in the FEM problem
- GetLoadContainer() - Get the entire load container
- GetLoadWithGlobalNumber() - Get the load with the specified global number. This is independent of the order in the load container, and is defined by the user when defining the FEM problem.
- GetLoad() - Get the load at the specified index in the load container.
- Materials:
- GetMaterialContainer () - Get the entire material container
- AddNextMaterial (MaterialLinearElasticity::Pointer mat) - Add another material at the specified position in the material container
- GetNumberOfMaterials (void) - Get number of materials in the FEM problem
- InsertMaterial (Material::Pointer e, MaterialIdentifier index) - Add another material at the specified position in the material container
- GetMaterial (MaterialIdentifier index)
- GetMaterialWithGlobalNumber (int globalNumber) - Get the material with the specified global number. This is independent of the order in the material container, and is defined by the user when defining the FEM problem.
- Other Public Membership Functions:
- Clear () - Initialize the FEMObject
- DeepCopy (FEMObject *Copy) - Copy the FEMObject into the current FEMObject
- FinalizeMesh () - Finalize the mesh. This must be called before using this object in the itk::fem::Solver
- GetNumberOfDegreesOfFreedom (void) - Get the degrees of freedom for the FEMObject
- GetNumberOfMultiFreedomConstraints (void) - Get the number of constraints for the FEMObject
itk::fem::Node
This class is defined in itkFEMElementBase.h and defines the degree of freedom and coordinate for the node. A node is essentially a point in space.
- Public Member Functions:
- VectorType & GetCoordinates(void) - get the spatial coordinates for the node
- void SetCoordinates(const VectorType & coords) - Set the spatial coordinates of the node
- DegreeOfFreedomIDType GetDegreeOfFreedom(unsigned int i) - Get the degree of freedom for the spatial coordinate (x,y,z)
- void SetDegreeOfFreedom(unsigned int i, DegreeOfFreedomIDType dof) - Set the degree of freedom for the spatial coordinate
- void ClearDegreesOfFreedom(void) - Remove all of the degrees of freedom
- Removed these Member Functions
- Read()
- Write()
itk::fem::Element
This is the base class and specific element types are defined below. This class did not change from its implementation in previous versions of ITK. The following changes were made to each of the specific element types.
- The following variables have been moved from public to protected:
- mat - This variable is now called m_mat
- The following public member functions were added:
- Material::ConstPointer GetMaterial(void) - Get the material property used for the element
- void SetMaterial(Material::ConstPointer mat_) - Set the material property for the element
- CreateAnother - Duplicate the specified object
- The following public membership functions have been removed:
- void Read(std::istream &, void *info)
- void Write(std::ostream & f)
- void Draw(CDC* pDC, Solution::ConstPointer sol)
The specific element types available are defined below:
- Element2DC1Beam
- Element2DC0LinearQuadrilateralStress
- Element2DC0LinearQuadrilateralStrain
- Element2DC0LinearQuadrilateralMembrane
- Element2DC0LinearTriangularStress
- Element2DC0LinearTriangularStrain
- Element2DC0LinearTriangularMembrane
- Element2DC0QuadraticTriangularStress
- Element2DC0QuadraticTriangularStrain
- Element3DC0LinearHexahedronMembrane
- Element3DC0LinearHexahedronStrain
- Element3DC0LinearTetrahedronMembrane
- Element3DC0LinearTetrahedronStrain
- Element3DC0LinearTriangularLaplaceBeltrami
- Element3DC0LinearTriangularMembrane
itk::fem::MaterialLinearElasticity
MaterialLinearElasticity is the only material type that currently exists in ITK. Several modifications were made to this class during the ITKv4 refactoring.
- Moved the following class variables were moved from public to protected signatures:
- A - renamed to m_CrossSectionalArea
- E - renamed m_YoungModulus
- h - renamed m_Thickness
- I - renamed m_MomentOfInertia
- nu - renamed m_PoissonRatio
- RhoC - renamed m_DensityHeatCapacity
- Add the following public class member functions to get/set the class variables.
- void SetCrossSectionalArea(double) – Set the cross sectional area
- void SetYoungsModulus(double) – Set the Youngs Modulus
- void SetThickness(double) – Set the cross sectional thickness
- void SetMomentOfInertia(double) – Set the Moment of Inertia
- void SetPoissonsRatio(double) – Set the Poisson’s ratio
- void SetDensityHeatProduct(double) – Set the Density - Heat Capacity product
- double GetCrossSectionalArea() – Get the cross sectional area
- double GetYoungsModulus() – Get the Youngs Modulus
- double GetThickness() – Get the cross sectional thickness
- double GetMomentOfInertia() – Get the Moment of Inertia
- double GetPoissonsRatio() – Get the Poisson’s ratio
- double GetDensityHeatProduct() – Get the Density - Heat Capacity product
- CreateAnother - Duplicate the specified object
- Removed these public class member functions
- virtual void Read(std::istream& f, void* info)
- virtual void Write(std::ostream& f )
itk::fem::Load
This is the load base class for all specific implementations used in the itk::fem framework. This class used to use a Visitor Dispatcher pattern to apply the loads to elements. While this provided a dynamic and general interface, it was very complicated and we had never taken advantage of its flexibility. Therefore, this patter was eliminated and concrete Load classes now must define a ApplyLoad method that will apply the load to the element specified.
itk::fem::LoadBC
- The following class variables were moved from public to protected:
- m_dof - renamed to m_DegreeOfFreedom
- m_element - renamed to m_Element
- m_value - renamed to m_Value
- The following class variable was removed
- CLID
- The following class functions were added, allowing the user to get and set the class variables:
- void SetDegreeOfFreedom(unsigned int dof) – Set the degree of freedom for the local element for which the boundary condition is being applied.
- void SetElement(Element::ConstPointer element) – Set the element for which the boundary condition is being applied.
- void SetValue(vnl_vector< Element::Float > value) – Set the boundary condition using a vector representation. This can be used to restrict motion of an element in a particular direction.
- unsigned int GetDegreeOfFreedom() – Returns the local degree of freedom for the element on which the boundary condition is being applied.
- Element::ConstPointer GetElement() – Returns the element on which the boundary condition is being applied.
- vnl_vector< Element::Float > GetValue() – Returns the assigned boundary condition.
itk::fem::LoadBCMFC
- The following class variables were moved from public to protected:
- Index - renamed to m_Index
- lhs - renamed to m_LeftHandSide
- rhs - renamed to m_RightHandSide
- Add class methods to get and set the class variables.
- void SetIndex(int) – Set the index variable for the multi freedom displacement constraint. This is used internally by the itk::Fem::Solver.
- void AddLeftHandSideTerm(LoadBCMFC::MFCTerm) – Add terms to the right hand side of the multi freedom displacement constraint. Used to replace loadBCMFC.lhs.push_back().
- void AddRightHandSideTerm(vnl_vector< Element::Float >) – Set the right hand side of the linear equation that defines the constraints. Replaces loadBCMFC.rhs = a;
- int GetIndex – Get the index variable for the multi freedom displacement constraint.
- int GetNumberOfLeftHandSideTerms() – Returns the number of terms used to define the left hand side of the multi freedom displacement constraint.
- itk::LoadBFMC GetLeftHandSideTerm(int index) – Returns the specified left hand side term.
- int GetNumberOfRightHandSideTerms()– Returns the number of terms used to define the left hand side of the multi freedom displacement constraint.
- Element::Float GetRightHandSideTerm(int index) – Returns the specified right hand side term.
- std::vector< MFCTerm >& GetLeftHandSideArray() - Get the left hand side of the FEM system
- vnl_vector< Element::Float >& GetRightHandSideArray() - Get the right hand side of the FEM system
itk::fem::LoadEdge
- The following member variables were moved from public to protected signatures:
- m_Edge
- m_Force
- The following public membership functions are now provided to get/set class variables
- void SetEdge(int) – Set the edge to apply the desired force.
- void SetForce(vnl_matrix< Float >) – Set the force to be applied to an edge.
- int GetEdge() – Get the edge for the applied force.
- vnl_matrix< Float > GetForce() – Get the force applied.
- void ApplyLoad(Element::ConstPointer element, Element::VectorType & Fe) - Apply the load to the elements on which it is applied.
itk::fem::LoadGravConst
- The following member variables were moved from public to protected signatures:
- Fg_value - renamed m_GravityForce
- The following public membership functions are now provided to get/set class variables:
- void SetForce(vnl_vector< Float >) – Set the constant force vector that exists for every point in space.
- vnl_vector< Float > GetForce() – Return the constant force vector that exists for every point in space.
- vnl_vector< Float > GetGravitationalForceAtPoint(vnl_vector< Float > ) - Return the force at the specified location. The same value is returned at every point.
- void ApplyLoad(Element::ConstPointer element, Element::VectorType & Fe) - Apply the load to the specified element.
itk::fem::LoadLandmark
- The following member variables were moved from public to protected signatures:
- eta - renamed to m_Eta
- m_force - renamed to m_Force
- m_pt - renamed to m_Point
- m_Solution
- m_Source
- m_Target
- The following public membership functions are now provided to get/set class variables:
- void SetEta(double) – Set the square root of the variance.
- double GetEta(double) – Get the square root of the variance.
- void ApplyLoad(Element::ConstPointer element, Element::VectorType & Fe) - Apply load the specified element
- itk::LightObject::Pointer CreateAnother(void) - Create a copy of the load
itk::fem::LoadNode
- The following member variables were moved from public to protected signatures:
- F - renamed m_Force
- m_element - renamed m_Element
- m_pt - renamed m_Point
- The following public membership functions are now provided to get/set class variables:
- void SetForce(vnl_vector< Float >) – Set the applied force to the node.
- void SetElement(Element::ConstPointer) – Set the element in the system that contains the degrees of freedom on which the force is applied.
- void SetNode(unsigned int) – Set the point on which the force is being applied.
- vnl_vector< Float > &GetForce() – Get the applied force.
- Element::ConstPointer GetElement() – Get the element in the system that contains the degrees of freedom on which the force is applied.
- Unsigned int GetNode() – Get the point on which the force is being applied.
- itk::LightObject::Pointer CreateAnother(void) - Create a copy of the load
- void ApplyLoad(Element::ConstPointer element, Element::VectorType & Fe) - Apply load the specified element
itk::fem::LoadPoint
- Moved the following class variables from public to protected signatures:
- Fp - renamed m_Point
- point -renamed m_ForcePoint
- The following public membership functions are now provided to get/set class variables:
- void SetForce(vnl_vector<Float>) – Set the force to be applied to the specified point location.
- void SetPoint(vnl_vector<Float>) – Set the point where the force is applied in global coordinates.
- vnl_vector<Float> & GetForce(vnl_vector<Float>) – Get the applied force.
- vnl_vector<Float> & GetPoint (vnl_vector<Float>) – Get the point where the force is applied.
- itk::LightObject::Pointer CreateAnother(void) - Create a copy of the load
- void ApplyLoad(Element::ConstPointer element, Element::VectorType & Fe) - Apply load the specified element
itk::Solver
The itk::fem::Solver is a templated class over the problem dimension. This is now a proper ITK filter that inherits from ProcessObject. The filter takes as input a FEMObject and will generate an output FEMObject. To get the Solver to generate a solution, the Update() method needs to be called.
- The following class variables were removed:
- node
- el
- load
- mat
- The following class functions were removed:
- Read()
- Write()
- Clear()
- The following class functions were moved from public to protected
- AssembleK()
- GenerateGFN()
- InitializeMatrixForAssembly()
- FinalizeMatrixAfterAssembly()
- AssembleElementMatrix()
- AssembleLandmarkContribution()
- ApplyBC()
- AssembleF()
- DecomposeK()
- RunSolver()
- UpdateDisplacements()
- InitializeLinearSystemWrapper()
- The following public membership functions were added
- SetInput()
- GetInput()
- GetOutput()
- MakeOutput()
- PrintSelf()
itk::FEMObjectSpatialObject
To support I/O we have developed a new spatial object, FEMObjectSpatialObject, was created. A fairly simple conversion is used to convert from a FEMObjectSpatialObject to FEMObject and vice-versa. The public class methods provides this interface.
- void SetFEMObject( FEMObjectType * femobject )
- FEMObjectType * GetFEMObject( void )
The FEM objects can then be read and written using the itk::SpatialObjectReader and itk::SpatialObjectWriter respectively.
File Formats
The only file format that is currently supported for FEM objects is the MetaIO file format. This file format supports the Spatial Objects used in ITK and VTK. The complete information regarding this file format can be found on the VTK Wiki. We has extended the MetaIO format to support the FEM spatial objects that have been added to ITK. Here we outline some of the basic information regarding the file format and its extension to support FEM spatial objects.
The file format has header information that defines the object(s) stored within the file as well as other information such as the dimension, how the data is stored, etc. Comments are allowed in the file format. Any characters that follow a % symbol are defined as comments. The header contains the following elements:
- ObjectType = %Defines the type of object stored (e.g. Scene or FEMObject)
- NDims = %Defines the dimension for the stored object
- NObjects = %Defines the number of objects if the object is a Scene
- BinaryData = %Defines if the data is binary or not. Binary data is not yet supported
- Offset = %Defines the position offset - Currently ignored for FEMObject
- CenterOfRotation = %Defines center of rotation - Currently ignored for FEMObject
- ElementSpacing = %Defines element spacing - Currently ignored for FEMObject
- ElementDataFile = %Defines where the data is stored (LOCAL means the data is stored within the same file.
If you enable ITK Testing, then you will download several examples of file format that are used for testing. The data will be downloaded into the build directory ExternalData/Modules/Numerics/FEM/test/Input.
Below the header the user defines the FEM Object using the following format. Typically the user will define the Nodes, followed by Materials, Elements, and Loads.
Nodes
Each node is denoted with a <Node> marker and then the global node number is specified followed by its position. The dimension of the position is defined by the Ndims field in the META spatial object header. After all of the nodes are defined there should be a <END> marker. Here is an example.
<Node> 0 % Global node number 2 0 0 % Nodal coordinates <Node> 1 % Global node number 2 1 0 % Nodal coordinates <Node> 2 % Global node number 2 1 2 % Nodal coordinates <END> % End of nodes
Materials
Similar to Nodes, Materials are specified by a maker that defines the type of material being defined. Presently, there is only one material property type, MaterialLinearElasticity. The material marker is followed by all of the parameters that are used to define the material (Global Number, Young modulus, Cross-sectional area, Moment of inertia, Poisson's ratio, thickness, and Density Heat Capacity). Here is an example
<MaterialLinearElasticity> 0 % Global material number E : 30000000 % Young modulus A : 0 % Crossection area I : 0 % Moment of inertia nu : 0.3 % Poisson's ratio h : 1 % Thickness RhoC : 1 % Density Heat Capacity END: % End of current material definition <END> % End of materials
Elements
Elements define the type of element, the global number, the global number of the nodes used to define the element, and the global number for the material. The following element types are supported
- Element2DC1Beam
- Element2DC0LinearQuadrilateralStress
- Element2DC0LinearQuadrilateralStrain
- Element2DC0LinearQuadrilateralMembrane
- Element2DC0LinearTriangularStress
- Element2DC0LinearTriangularStrain
- Element2DC0LinearTriangularMembrane
- Element2DC0QuadraticTriangularStress
- Element2DC0QuadraticTriangularStrain
- Element3DC0LinearHexahedronMembrane
- Element3DC0LinearHexahedronStrain
- Element3DC0LinearTetrahedronMembrane
- Element3DC0LinearTetrahedronStrain
- Element3DC0LinearTriangularLaplaceBeltrami
- Element3DC0LinearTriangularMembrane
Here is an example definition of two elements.
<Element2DC0LinearTriangularStress> 0 % Global element number 0 % Node 1 ID 1 % Node 2 ID 2 % Node 3 ID 0 % MaterialLinearElasticity ID <Element2DC0LinearTriangularStress> 1 % Global element number 0 % Node 1 ID 2 % Node 2 ID 3 % Node 3 ID 0 % MaterialLinearElasticity ID <END> % End of elements
Loads
Similar structures are used for Loads. Each load definition is slightly different depending on the type of load. Here are examples that show how to define each of the Load types in the Meta file. AT the end of the load section there should be a <END> marker to signal the end of loads.
<LoadNode> 0 % Global load number 1 % GN of the element on which the load acts 2 % Point number within the element 2 0 -1000 % Force vector
<LoadBC> 1 % Global load number 0 % GN of element 0 % DOF# in element 1 0 % rhs of MFC
<LoadBCMFC> 5 % Global object number 2 % Number of DOFs in this MFC %==> 0 % GN of element 1 % DOF# in element 1 % weight %==> 1 % GN of element 3 % DOF# in element -1 % weight %==> 1 0 % rhs of MFC
<LoadEdge> 0 % Global load number 0 % GN of the element on which the load acts 1 % Edge number 2 2 % # Rows and Columns in force matrix 10 0 % Force Matrix - Row 1 5 0 % Force Matrix - Row 2
<LoadGravConst> 1 % Global load number 1 % Number of elements on which the load acts 1 % GN of the element on which the load acts 2 0 -12.762 % Force vector
<LoadLandmark> 0 % Global load number 2 0. 0. % Original point location 2 0. 1. % Deformed point location 1.e-2 % Square root of landmark variance
<LoadPoint> 0 % Global load number 2 0. 0. % Point of applied load 2 0. 1. % Applied force on the point
NOTE: LoadPoint needs to be added. Prototype now works
Other Formats
The has been discussion about support for other formats. While these formats in general will support the geometry (nodes and elements) of the problem, they do not support the loads or material properties. People have expressed interest for these formats.
- gmsh format
- Used to support Netgen
- VTK Format
- tetgen support
- VTK visualization
Assembling and Solving FEM in ITK
There are three ways to define a FEMObject in ITK. The first is to read the object as a spatial object, the second is to manually assemble the FEM object, and the third is to create the FEMObject based upon an image. Once the FEMObject is defined the object can be used as input for the Solver and a solution generated.
Loading a FEMObject as a SpatialObject
Here is an example of how to load a FEMOBject from a SpatialObject file. This example defines a 3D problem.
<source lang="cpp">
//Need to register default FEM object types, //and setup SpatialReader to recognize FEM types //which is all currently done as a HACK in //the initializaiton of the itk::FEMFactoryBase::GetFactory() itk::FEMFactoryBase::GetFactory()->RegisterDefaultTypes(); typedef itk::FEMSpatialObjectReader<3> FEMSpatialObjectReaderType; typedef FEMSpatialObjectReaderType::Pointer FEMSpatialObjectReaderPointer; FEMSpatialObjectReaderPointer SpatialReader = FEMSpatialObjectReaderType::New(); SpatialReader->SetFileName( argv[1] ); SpatialReader->Update(); FEMSpatialObjectReaderType::ScenePointer myScene = SpatialReader->GetScene(); typedef itk::FEMObjectSpatialObject<3> FEMObjectSpatialObjectType; typedef FEMObjectSpatialObjectType::Pointer FEMObjectSpatialObjectPointer; FEMObjectSpatialObjectType::ChildrenListType* children = SpatialReader->GetGroup()->GetChildren(); //Verify that the Spatial Object contains a FEMObjectSpatialObject. If not then exit if( strcmp( (*(children->begin() ) )->GetTypeName(), "FEMObjectSpatialObject") ) { return EXIT_FAILURE; } // Get the Spatial Object and cast appropriately FEMObjectSpatialObjectType::Pointer femSO = dynamic_cast<FEMObjectSpatialObjectType *>( (*(children->begin() ) ).GetPointer() ); // Get the FEMObject from the Spatial Object typedef itk::fem::FEMObject<3> FEMObjectType; FEMObjectType::Pointer femObject = femSO->GetFEMObject(); femObject->FinalizeMesh();
</source>
Manually defining a FEMObject
Here is an example how to manually define a FEMObject directly in C++. This is a 2D example.
<source lang="cpp">
/* Create a FEMObject for storing the FEM problem */ typedef itk::fem::FEMObject<2> FEMObjectType; FEMObjectType::Pointer femObject = FEMObjectType::New();
/* First define nodes - Here we define 5 nodes */ itk::fem::Node::Pointer n1; itk::fem::Element::VectorType pt(2);
// Node 0 n1 = itk::fem::Node::New(); pt[0] = 0.0; pt[1] = 0.0; n1->SetCoordinates(pt); n1->SetGlobalNumber(0); femObject->AddNextNode(&*n1);
// Node 1 n1 = itk::fem::Node::New(); pt[0] = 1500.0; pt[1] = 0.0; n1->SetCoordinates(pt); n1->SetGlobalNumber(1); femObject->AddNextNode(&*n1);
// Node 2 n1 = itk::fem::Node::New(); pt[0] = 3000.0; pt[1] = 0.0; n1->SetCoordinates(pt); n1->SetGlobalNumber(2); femObject->AddNextNode(&*n1);
// Node 3 n1 = itk::fem::Node::New(); pt[0] = 3000.0; pt[1] = 3000.0; n1->SetCoordinates(pt); n1->SetGlobalNumber(3); femObject->AddNextNode(&*n1);
// Node 4 n1 = itk::fem::Node::New(); pt[0] = 0.0; pt[1] = 4500.0; n1->SetCoordinates(pt); n1->SetGlobalNumber(4); femObject->AddNextNode(&*n1);
/* Define the Element Material properties */ itk::fem::MaterialLinearElasticity::Pointer m; // Material 0 m = itk::fem::MaterialLinearElasticity::New(); m->SetGlobalNumber(0); /* Global number of the material */ m->SetYoungsModulus(200000000000.0); /* Young modulus */ m->SetPoissonsRatio(0.3); /* Poissons Ratio */ m->SetCrossSectionalArea(2000.0); /* Crossection area */ m->SetMomentOfInertia(1.0); /* Moment of inertia */ femObject->AddNextMaterial(&*m);
m = itk::fem::MaterialLinearElasticity::New(); // Material 1 m->SetGlobalNumber(1); /* Global number of the material */ m->SetYoungsModulus(200000.0); /* Young modulus */ m->SetPoissonsRatio(0.3); /* Poissons Ratio */ m->SetCrossSectionalArea(1200.0); /* Crossection area */ m->SetMomentOfInertia(1.0); /* Moment of inertia */ femObject->AddNextMaterial(&*m);
m = itk::fem::MaterialLinearElasticity::New(); // Material 2 m->SetGlobalNumber(2); /* Global number of the material */ m->SetYoungsModulus(70000.0); /* Young modulus */ m->SetPoissonsRatio(0.3); /* Poissons Ratio */ m->SetCrossSectionalArea(900.0); /* Crossection area */ m->SetMomentOfInertia(1.0); /* Momemt of inertia */ femObject->AddNextMaterial(&*m);
/* Define the Elements */ itk::fem::Element2DC0LinearLineStress::Pointer e1; // Element 0 e1 = itk::fem::Element2DC0LinearLineStress::New(); e1->SetGlobalNumber(0); e1->SetNode( 0, &*femObject->GetNode(0) ); e1->SetNode( 1, &*femObject->GetNode(1) ); e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(0) ) ); femObject->AddNextElement( &*e1);
e1 = itk::fem::Element2DC0LinearLineStress::New(); // Element 1 e1->SetGlobalNumber(1); e1->SetNode( 0, &*femObject->GetNode(1) ); e1->SetNode( 1, &*femObject->GetNode(2) ); e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(0) ) ); femObject->AddNextElement( &*e1);
e1 = itk::fem::Element2DC0LinearLineStress::New(); // Element 2 e1->SetGlobalNumber(2); e1->SetNode( 0, &*femObject->GetNode(1) ); e1->SetNode( 1, &*femObject->GetNode(3) ); e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(2) ) ); femObject->AddNextElement( &*e1);
e1 = itk::fem::Element2DC0LinearLineStress::New(); // Element 3 e1->SetGlobalNumber(3); e1->SetNode( 0, &*femObject->GetNode(0) ); e1->SetNode( 1, &*femObject->GetNode(4) ); e1->SetMaterial( dynamic_cast< itk::fem::MaterialLinearElasticity * >( &*femObject->GetMaterial(1) ) ); femObject->AddNextElement( &*e1);
/* Define the Loads */ itk::fem::LoadBC::Pointer l1; // Load 0 l1 = itk::fem::LoadBC::New(); l1->SetGlobalNumber(0); l1->SetElement( &*femObject->GetElement(2) ); l1->SetDegreeOfFreedom(2); l1->SetValue( vnl_vector< double >(1, 0.0) ); femObject->AddNextLoad( &*l1);
// Load 1 l1 = itk::fem::LoadBC::New(); l1->SetGlobalNumber(1); l1->SetElement( &*femObject->GetElement(2) ); l1->SetDegreeOfFreedom(3); l1->SetValue( vnl_vector< double >(1, 0.0) ); femObject->AddNextLoad( &*l1);
// Load 2 l1 = itk::fem::LoadBC::New(); l1->SetGlobalNumber(2); l1->SetElement( &*femObject->GetElement(3) ); l1->SetDegreeOfFreedom(2); l1->SetValue( vnl_vector< double >(1, 0.0) ); femObject->AddNextLoad( &*l1);
// Load 3 l1 = itk::fem::LoadBC::New(); l1->SetGlobalNumber(3); l1->SetElement( &*femObject->GetElement(3) ); l1->SetDegreeOfFreedom(3); l1->SetValue( vnl_vector< double >(1, 0.0) ); femObject->AddNextLoad( &*l1);
// Load 4 itk::fem::LoadNode::Pointer l2; l2 = itk::fem::LoadNode::New(); l2->SetGlobalNumber(4); l2->SetElement( femObject->GetElement(1) ); l2->SetNode(0); vnl_vector< double > F(2); F[0] = 0; F[1] = 30000; l2->SetForce(F); femObject->AddNextLoad( &*l2 );
// Load 5 itk::fem::LoadBCMFC::Pointer bcmfc = itk::fem::LoadBCMFC::New(); bcmfc->SetGlobalNumber(5); bcmfc->AddLeftHandSideTerm( itk::fem::LoadBCMFC::MFCTerm(femObject->GetElement(0), 1, 1) ); bcmfc->AddLeftHandSideTerm( itk::fem::LoadBCMFC::MFCTerm(femObject->GetElement(1), 3, -1) ); bcmfc->AddRightHandSideTerm(0.0); femObject->AddNextLoad( &*bcmfc );
</source>
Define a FEMObject from an image
This example shows the user how to convert an image into a FEMObject. This will define nodes, elements, and materials for the FEMObject. A constant material property will be used in this conversion. No loads are defined in the resulting FEMObject. The user will have to add the loads to be applied to the FEMObject.
<source lang="cpp">
//Need to register default FEM object types, //and setup SpatialReader to recognize FEM types //which is all currently done as a HACK in //the initializaiton of the itk::FEMFactoryBase::GetFactory() itk::FEMFactoryBase::GetFactory()->RegisterDefaultTypes();
// Define the image typedef itk::Image<unsigned char, 2> ImageType; typedef itk::ImageFileReader<ImageType> ImageFileReaderType;
vnl_vector<unsigned int> pixelsPerElement; pixelsPerElement.set_size(2); pixelsPerElement[0] = 1; pixelsPerElement[1] = 1;
ImageFileReaderType::Pointer reader = ImageFileReaderType::New(); reader->SetFileName(argv[1]); reader->Update();
/* Define the Material and Element Type to be used */ typedef itk::fem::MaterialLinearElasticity ElasticityType; ElasticityType::Pointer m; m = ElasticityType::New(); m->SetGlobalNumber(0); m->SetYoungsModulus(3000.0); m->SetCrossSectionalArea(0.02); m->SetMomentOfInertia(0.004);
typedef itk::fem::Element2DC0LinearQuadrilateralMembrane MembraneElementType; MembraneElementType::Pointer e0 = MembraneElementType::New(); e0->SetGlobalNumber(0); e0->SetMaterial( dynamic_cast<ElasticityType *>( m.GetPointer() ) );
/* Convert the image to FEMObject */ typedef itk::fem::ImageToRectilinearFEMObjectFilter<ImageType> MeshFilterType; MeshFilterType::Pointer meshFilter = MeshFilterType::New(); meshFilter->SetInput( reader->GetOutput() ); meshFilter->SetPixelsPerElement( pixelsPerElement ); meshFilter->SetElement( e0.GetPointer() ); meshFilter->Update();
typedef itk::fem::FEMObject<2> FEMObjectType; FEMObjectType::Pointer femObject = meshFilter->GetOutput();
</source>
Solving a FEM problem
Once a FEMObject is created to define the FEM problem, the itk::fem::Solver is then used to solve the FEM problem and create the deformed FEMObject. The solver provides a generic interface for providing the actual numeric solver used to generate the results. All of the numeric solvers are derived from the LinearSystemWrapper class and support linear solutions to the set of linear equations. Presently three numeric solvers are supported:
Additional numeric solutions can be defined using this generic interface.
Here is an example of how to use the Solver to generate a result from a FEM problem.
<source lang="cpp">
typedef itk::fem::FEMObject<3> FEMObjectType; FEMObjectType::Pointer fem = FEMObjectType::New(); . . . itk::fem::LinearSystemWrapperVNL lsw_vnl; SolverType::Pointer solver = SolverType::New(); solver->SetInput( fem ); solver->SetLinearSystemWrapper(&lsw_dvnl); solver->Update();
FEMObjectType::Pointer result = solver->GetOutput();
</source>
Update the itk::FEMRegistrationFilter
To make the FEMRegistrationFilter similar to the other registration filters the following changes will be made
- Remove these public member functions. The application programmer will be responsible for loading the images and setting the parameters for the filter. The configuration file will be removed.
- GetConfigFileName ()
- GetFixedFile ()
- GetMovingFile ()
- GetResultsFileName ()
- GetWriteDisplacements ()
- ReadConfigFile (const char *)
- SetConfigFileName (const char *f)
- SetDisplacementsFile (const char *r)
- SetFixedFile (const char *t)
- SetLandmarkFile (const char *l)
- SetMovingFile (const char *r)
- SetResultsFile (const char *r)
- SetResultsFileName (const char *f)
- WriteDisplacementField (unsigned int index)
- WriteDisplacementFieldMultiComponent ()
- WriteWarpedImage (const char *fn)
- Add these new public member functions
- bool AddNextMovingLandmark(PointType) – Add another point for the moving image to the registration.
- bool AddNextFixedLandmark(PointType) – Add another point from the fixed image to the registration.
- void ClearMovingLandmarks() – Remove all of the existing landmarks for the moving image.
- void ClearFixedLandmarks() – Remove all of the existing landmarks for the fixed image.
- bool InsertMovingLandmark(PointType p, int index) – Insert the moving landmark at the specified index.
- bool InsertFixedLandmark(PointType p, int index) – Insert the fixed landmark at the specified index.
- bool RemoveMovingLandmark(int index)
- bool RemoveFixedLandmark(int index)
- void SetFEMesh() – Set the mesh to be used for the registration.
- void GetFEMesh() – Get the mesh used in the registration.
- void SetUserDefinedMesh(bool) – Specifies if the user will provide a custom mesh or if it will be generated based on the image.
Data Structure
- Spatial Objects -
- ITK::Mesh -
- VTK Unstructured Grids -
Solvers
- SuperLU
- PETSc