1 /*************************************************************************
2 * *
3 * Vega FEM Simulation Library Version 3.1 *
4 * *
5 * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2016 USC *
6 * All rights reserved. *
7 * *
8 * Code authors: Jernej Barbic, Yijing Li *
9 * http://www.jernejbarbic.com/code *
10 * *
11 * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder, *
12 * Doug L. James, Jovan Popovic *
13 * *
14 * Funding: National Science Foundation, Link Foundation, *
15 * Singapore-MIT GAMBIT Game Lab, *
16 * Zumberge Research and Innovation Fund at USC *
17 * *
18 * This library is free software; you can redistribute it and/or *
19 * modify it under the terms of the BSD-style license that is *
20 * included with this library in the file LICENSE.txt *
21 * *
22 * This library is distributed in the hope that it will be useful, *
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file *
25 * LICENSE.TXT for more details. *
26 * *
27 *************************************************************************/
28
29 #ifndef _VOLUMETRICMESH_H_
30 #define _VOLUMETRICMESH_H_
31
32 /*
33 This abstract class can store a generic volumetric 3D mesh.
34 It stores the mesh geometric information (elements and vertices),
35 and also the material parameters of each individual mesh element
36 (Young's modulus, Poisson ratio, mass density). This is done by
37 organizing elements with the same material parameters into a "region".
38 The class supports several geometric queries and interpolation to
39 an embedded triangle mesh ("Free-Form Deformation"). It also
40 supports exporting the mesh to an .ele or .node format (the format
41 used by the Stellar and TetGen mesh generation packages). Derived classes
42 are the TetMesh (general tetrahedral meshes), and CubicMesh
43 (axis-aligned cubic "voxel" meshes). See description in tetMesh.h and cubicMesh.h.
44
45 All quantities are 0-indexed, except the input mesh files where the
46 elements and vertices are 1-indexed (same as in TetGen and Stellar).
47 */
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <vector>
53 #include <set>
54 #include <string>
55 #include <map>
56 #include "minivector.h"
57
58 class VolumetricMesh
59 {
60 public:
61
62 // Note: This class is abstract and cannot be instantiated; use the constructors in the derived classes (TetMesh, CubicMesh) to initialize a mesh, or use the load routine in volumetricMeshLoader.h
63
64 // copy constructor, destructor
65 VolumetricMesh(const VolumetricMesh & volumetricMesh);
66 virtual VolumetricMesh * clone() = 0;
67 virtual ~VolumetricMesh();
68
69 // nested classes to store sets, materials and regions (declared later)
70 class Set;
71 class Material;
72 class Region;
73
74 // === save/export ===
75
76 // saves the mesh to a text file (.veg file format, see examples and documentation)
77 virtual int saveToAscii(const char * filename) const = 0;
78 virtual int save(const char * filename) const; // for backward compatibility (just calls saveToAscii)
79
80 // saves the mesh to binary format
81 // returns: 0 = success, non-zero = error
82 // output: if bytesWritten is non-NULL, it will contain the number of bytes written
83 virtual int saveToBinary(const char * filename, unsigned int * bytesWritten = NULL) const = 0;
84 // if countBytesOnly = true, user can pass NULL to binaryOutputStream
85 virtual int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten = NULL, bool countBytesOnly = false) const = 0;
86
87 // exports the mesh geometry to an .ele and .node file (TetGen and Stellar format)
88 // if includeRegions=1, an extra column is added to output, identifying the region of each element
89 int exportToEle(const char * baseFilename, int includeRegions=0) const;
90 // exports the mesh geometry to memory arrays (say, for external usage)
91 // all parameters are output parameters
92 // vertices and elements will be allocated inside the routine
93 void exportMeshGeometry(int * numVertices, double ** vertices, int * numElements = NULL, int * numElementVertices = NULL, int ** elements = NULL) const;
94
95 // === vertex and element access ===
96
97 typedef enum { INVALID, TET, CUBIC } elementType;
98 typedef enum { ASCII, BINARY, NUM_FILE_FORMATS } fileFormatType; // ASCII is the text .veg format, BINARY is the binary .vegb format
99 // opens the file and returns the element type of the volumetric mesh in the file; returns INVALID if no type information found
100 static elementType getElementType(const char * filename, fileFormatType fileFormat = ASCII);
101 virtual elementType getElementType() const = 0; // calls the derived class to identify itself
102 // advanced usage: returns the element type of the volumetric mesh from a BINARY stream (does not modify fin)
103 //static elementType getElementType(FILE * fin);
104 static elementType getElementType(void * fin, int memoryLoad = 0);
105
getNumVertices()106 inline int getNumVertices() const { return numVertices; }
getVertex(int i)107 inline Vec3d & getVertex(int i) { return vertices[i]; }
getVertex(int i)108 inline const Vec3d & getVertex(int i) const { return vertices[i]; }
getVertex(int element,int vertex)109 inline Vec3d & getVertex(int element, int vertex) { return vertices[elements[element][vertex]]; }
getVertex(int element,int vertex)110 inline const Vec3d & getVertex(int element, int vertex) const { return vertices[elements[element][vertex]]; }
getVertexIndex(int element,int vertex)111 inline int getVertexIndex(int element, int vertex) const { return elements[element][vertex]; }
getVertexIndices(int element)112 inline const int * getVertexIndices(int element) const { return elements[element]; }
getVertices()113 inline Vec3d * getVertices() { return vertices; } // advanced, internal datastructure
getVertices()114 inline const Vec3d * getVertices() const { return vertices; }
getNumElements()115 inline int getNumElements() const { return numElements; }
getNumElementVertices()116 inline int getNumElementVertices() const { return numElementVertices; }
117 void renumberVertices(const std::vector<int> & permutation); // renumbers the vertices using the provided permutation
setVertex(int i,const Vec3d & pos)118 inline void setVertex(int i, const Vec3d & pos) { vertices[i] = pos; } // set the position of a vertex
119
120 // === materials access ===
121
getNumMaterials()122 inline int getNumMaterials() const { return numMaterials; }
getMaterial(int i)123 inline const Material * getMaterial(int i) const { return materials[i]; }
getElementMaterial(int el)124 inline const Material * getElementMaterial(int el) const { return materials[elementMaterial[el]]; }
125 static void getDefaultMaterial(double * E, double * nu, double * density);
126
getNumSets()127 inline int getNumSets() const { return numSets; }
getSet(int i)128 inline const Set * getSet(int i) const { return sets[i]; }
129
getNumRegions()130 inline int getNumRegions() const { return numRegions; }
getRegion(int i)131 inline const Region * getRegion(int i) const { return regions[i]; }
132
133 // === materials editing ===
getMaterial(int i)134 inline Material * getMaterial(int i) { return materials[i]; }
getElementMaterial(int el)135 inline Material * getElementMaterial(int el) { return materials[elementMaterial[el]]; }
136 void setMaterial(int i, const Material * material); // sets i-th material to "material"
137 void setSingleMaterial(double E, double nu, double density); // erases all materials and creates a single material for the entire mesh
138 void addMaterial(const Material * material, const Set & newSet, bool removeEmptySets, bool removeEmptyMaterials);
139
140 // mass density of an element
getElementDensity(int el)141 double getElementDensity(int el) const { return materials[elementMaterial[el]]->getDensity(); }
142 // computes the mass matrix of a single element
143 // note: to compute the mass matrix for the entire mesh, use generateMassMatrix.h
144 virtual void computeElementMassMatrix(int element, double * massMatrix) const = 0; // massMatrix is numElementVertices_ x numElementVertices_
145
146 // === geometric queries and transformations ===
147
148 Vec3d getElementCenter(int el) const;
149
150 // center of mass and inertia tensor
151 double getVolume() const;
152 virtual double getElementVolume(int el) const = 0;
153 void getVertexVolumes(double * vertexVolumes) const; // compute the volume "belonging" to each vertex
154 virtual void getElementInertiaTensor(int el, Mat3d & inertiaTensor) const = 0; // returns the inertia tensor of a single element, around its center of mass, with unit density
155 double getMass() const; // compute the total mass of the mesh, using the mass density material information
156 void getInertiaParameters(double & mass, Vec3d & centerOfMass, Mat3d & inertiaTensor) const ; // mass, center of mass and inertia tensor for the entire mesh
157
158 // centroid is the geometric center of all vertices; radius is the tightest fitting sphere centered at the centroid
159 void getMeshGeometricParameters(Vec3d & centroid, double * radius) const;
160
161 // mesh 1-neighborhood queries
162 void getVerticesInElements(std::vector<int> & elements, std::vector<int> & vertices) const;
163 void getElementsTouchingVertices(std::vector<int> & vertices, std::vector<int> & elements) const;
164 void getVertexNeighborhood(std::vector<int> & vertices, std::vector<int> & neighborhood) const;
165
166 // proximity queries
167 int getClosestElement(Vec3d pos) const; // finds the closest element to the given position (using linear scan); distance to a element is defined as distance to its center
168 int getClosestVertex(Vec3d pos) const; // finds the closest vertex to the given position (using linear scan)
169 int getContainingElement(Vec3d pos) const; // finds the element that containts the given position (using linear scan); if such element does not exist, -1 is returned
170 virtual bool containsVertex(int element, Vec3d pos) const = 0; // true if given element contain given position, false otherwise
171
172 // computes the gravity vector (different forces on different mesh vertices due to potentially varying mass densities)
173 // gravityForce must be a pre-allocated vector of length 3xnumVertices()
174 void computeGravity(double * gravityForce, double g=9.81, bool addForce=false) const;
175
176 // edge queries
177 virtual int getNumElementEdges() const = 0;
178 virtual void getElementEdges(int el, int * edgeBuffer) const = 0; // edgeBuffer must be pre-allocated, of size 2 x numElementEdges()
179
180 // (permanently) applies the deformation to the vertices of the mesh
181 void applyDeformation(double * u);
182 void applyLinearTransformation(double * pos, double * R); // transforms every vertex as X |--> pos + R * X (R must be given row-major)
183
184 // === submesh creation ===
185
186 // (permanently) set this mesh to its submesh containing the specified elements (i.e., delete the mesh elements not on the given list of elements)
187 // if vertexMap is non-null, it also returns a renaming datastructure: vertexMap[big mesh vertex] is the vertex index in the subset mesh
188 void setToSubsetMesh(std::set<int> & subsetElements, int removeIsolatedVertices=1, std::map<int,int> * vertexMap = NULL);
189
190 // === interpolation ===
191
192 // the interpolant is a triple (numTargetLocations, vertices, weights)
193 // Generates interpolation weights to transfer quantities from volumetric mesh to (embedded) surface meshes.
194 // Input is a list of 3D target locations where the interpolant will be computed,
195 // e.g., those could be vertices of a triangle mesh embedded into the volumetric mesh.
196 // Each location is a 3-vector, i.e., 3 consecutive double-precision values.
197 // If zeroThreshold is set positive, than for any target location that is
198 // more than zeroThreshold away from the closest element,
199 // all weights will be set to zero; this is useful, e.g. to
200 // fix locations far away from your mesh.
201 // Output: vertices and weights arrays
202 // vertices: gives a list of integer indices of the vertices of the element
203 // closest to the target location (numElementVertices entries per target location, one for each element vertex)
204 // note: if target location is inside a voxel, that voxel will be chosen as closest
205 // weights: a list of numElementVertices_ weights, as per the numElementVertices_ vertices of each element (weights sum to 1)
206 // If zeroThreshold >= 0, then the points that are further than zeroThreshold away from any volumetric mesh vertex, are assigned weights of 0.
207 // If elements is not NULL, the closest elements for each target location will be returned in the integer list "*elements" (allocated inside the function)
208 // If elements is not NULL, the function will allocate an integer array *elements, and return the closest element to each target location in it.
209 // Returns the number of target points that do not lie inside any element.
210 int generateInterpolationWeights(int numTargetLocations, const double * targetLocations, int ** vertices, double ** weights, double zeroThreshold = -1.0, int ** elements = NULL, int verbose=0) const; // this is the "master" function, meant to be typically used to create the interpolant
211
212 // interpolates 3D vector data from vertices of the
213 // volumetric mesh (data given in u) to the target locations (output goes into uTarget)
214 // e.g., use this to interpolate deformation from the volumetric mesh to a triangle mesh
215 static void interpolate(const double * u, double * uTarget, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights);
216
217 // the following are less often used, more specialized functions
218 // same as "generateInterpolationWeights" above, except here the elements that contain the target locations are assumed to be known, and are provided in array "elements"; returns 0 on success, 1 otherwise
219 int generateInterpolationWeights(int numTargetLocations, const double * targetLocations, int * elements, int ** vertices, double ** weights, double zeroThreshold = -1.0, int verbose=0) const;
220 // generates the integer list "elements" of the elements that contain given vertices; if closestElementIfOutside==1, then vertices outside of the mesh are assigned the closest element, otherwise -1 is assigned; returns the number of target locations outside of the mesh
221 int generateContainingElements(int numTargetLocations, const double * targetLocations, int ** elements, int useClosestElementIfOutside=1, int verbose=0) const;
222 static int getNumInterpolationElementVertices(const char * filename); // looks at the first line of "filename" to determine "numElementVertices" for this particular interpolant
223 static int loadInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices, int ** vertices, double ** weights); // ASCII version; returns 0 on success
224 static int saveInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // ASCII version
225 static int loadInterpolationWeightsBinary(const char * filename, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
226 static int saveInterpolationWeightsBinary(const char * filename, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // binary version
227 static int loadInterpolationWeightsBinary(FILE * fin, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
228 static int saveInterpolationWeightsBinary(FILE * fout, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // binary version
229
230 // computes barycentric weights of the given position with respect to the given element
231 virtual void computeBarycentricWeights(int element, const Vec3d & pos, double * weights) const = 0;
232
233 // computes the gradient of a 3D vector field (specified at the volumetric mesh vertices), at the location "pos"
234 // "numFields" fields can be interpolated simultaneously; each is given as one column of the U matrix
235 // U is a 3numVertices x numFields matrix; stored column-major
236 // output: grad is 9 x numFields matrix, stored column-major; each column gives the gradient (3x3 matrix), stored row-major format
237 // return: 0 if pos inside the mesh, 1 otherwise
238 int interpolateGradient(const double * U, int numFields, Vec3d pos, double * grad) const;
239 // in this version, the element containing the "pos" must be known, and prescribed directly
240 virtual void interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const = 0;
241
242 // === material-related nested classes ===
243
244 // a set of integers, with a name (used for example, to store elements that share the same material properties)
245 class Set
246 {
247 public:
248
249 Set(const std::string & name);
250 Set(const Set & set);
251 Set(const std::string & name, const std::set<int> & elements);
252
253 inline std::string getName() const;
254 inline int getNumElements() const;
255 inline void getElements(std::set<int> & elements) const;
256 inline const std::set<int> & getElements() const;
257 inline bool isMember(int element) const;
258
259 inline std::set<int> & getElements();
260 inline void insert(int element);
261 inline void clear();
262
263 protected:
264 std::string name;
265 std::set<int> elements;
266 };
267
268 // stores a material (abstract class)
269 class Material
270 {
271 public:
272 Material(const std::string name, double density);
273 Material(const Material & material);
~Material()274 virtual ~Material() {};
275 virtual Material * clone() const = 0;
276
277 inline std::string getName() const; // material name
278 inline double getDensity() const; // density
279 inline void setName(const std::string name);
280 inline void setDensity(double density);
281
282 // ENU = any isotropic material parameterized by E (Young's modulus), nu (Poisson's ratio)
283 // ORTHOTROPIC = orthotropic anisotropic material
284 // MOONEYRIVLIN = Mooney-Rivlin material
285 typedef enum { INVALID, ENU, ORTHOTROPIC, MOONEYRIVLIN } materialType;
286 virtual materialType getType() = 0;
287
288 typedef enum { ENU_DENSITY, ENU_E, ENU_NU, ENU_NUM_PROPERTIES } enuMaterialProperties;
289 typedef enum { ORTHOTROPIC_DENSITY, ORTHOTROPIC_E1, ORTHOTROPIC_E2, ORTHOTROPIC_E3, ORTHOTROPIC_NU12, ORTHOTROPIC_NU23, ORTHOTROPIC_NU31, ORTHOTROPIC_G12, ORTHOTROPIC_G23, ORTHOTROPIC_G31, ORTHOTROPIC_NUM_PROPERTIES } orthotropicMaterialProperties;
290 typedef enum { MOONEYRIVLIN_DENSITY, MOONEYRIVLIN_MU01, MOONEYRIVLIN_MU10, MOONEYRIVLIN_V1, MOONEYRIVLIN_NUM_PROPERTIES } mooneyrivlinMaterialProperties;
291
292 protected:
293 std::string name;
294 double density;
295 };
296
297 // material with E (Young's modulus), nu (Poisson's ratio) (defined in volumetricMeshENuMaterial.h)
298 class ENuMaterial;
299 // Mooney-Rivlin material (defined in volumetricMeshMooneyRivlinMaterial.h)
300 class MooneyRivlinMaterial;
301 // Orthotropic material (defined in volumetricMeshOrthotropicMaterial.h)
302 class OrthotropicMaterial;
303
304 // a volumetric mesh region, i.e., a set of elements sharing the same material
305 class Region
306 {
307 public:
308 Region(int materialIndex, int setIndex);
309 inline int getMaterialIndex() const;
310 inline int getSetIndex() const;
311 inline void setMaterialIndex(int index);
312 inline void setSetIndex(int index);
313
314 protected:
315 int setIndex, materialIndex;
316 };
317
318 static double E_default;
319 static double nu_default;
320 static double density_default;
321
322 protected:
323 int numVertices;
324 Vec3d * vertices;
325
326 int numElementVertices;
327 int numElements;
328 int ** elements;
329
330 int numMaterials;
331 int numSets;
332 int numRegions;
333 Material ** materials;
334 Set ** sets;
335 Region ** regions;
336 int * elementMaterial; // material index of each element
337
338 // parses the mesh, and returns the mesh element type
339 VolumetricMesh(const char * filename, fileFormatType fileFormat, int numElementVertices, elementType * elementType_, int verbose);
340 // if memoryLoad is 0, binaryInputStream is FILE* (load from a file, via a stream), otherwise, it is char* (load from a memory buffer)
341 VolumetricMesh(void * binaryInputStream, int numElementVertices, elementType * elementType_, int memoryLoad = 0);
VolumetricMesh(int numElementVertices_)342 VolumetricMesh(int numElementVertices_) { numElementVertices = numElementVertices_; }
343 void propagateRegionsToElements();
344 void loadFromBinaryGeneric(void * binaryInputStream, elementType * elementType_, int memoryLoad);
345
346 // constructs a mesh from the given vertices and elements,
347 // with a single region and material ("E, nu" material)
348 // "vertices" is double-precision array of length 3 x numVertices
349 // "elements" is an integer array of length numElements x numElementVertices
350 VolumetricMesh(int numVertices, double * vertices,
351 int numElements, int numElementVertices, int * elements,
352 double E=E_default, double nu=nu_default, double density=density_default);
353
354 // constructs a mesh from the given vertices and elements,
355 // with an arbitrary number of sets, regions and materials
356 // "vertices" is double-precision array of length 3 x numVertices
357 // "elements" is an integer array of length numElements x numElementVertices
358 // "materials", "sets" and "regions" will be copied internally (deep copy), so they
359 // can be released after calling this constructor
360 VolumetricMesh(int numVertices, double * vertices,
361 int numElements, int numElementVertices, int * elements,
362 int numMaterials, Material ** materials,
363 int numSets, Set ** sets,
364 int numRegions, Region ** regions);
365
366 // creates a submesh consisting of the specified elements of the given mesh
367 // if vertexMap is non-null, it also returns a renaming datastructure: vertexMap[big mesh vertex] is the vertex index in the subset mesh
368 VolumetricMesh(const VolumetricMesh & mesh, int numElements, int * elements, std::map<int,int> * vertexMap = NULL);
369
370 int saveToAscii(const char * filename, elementType elementType_) const;
371 int saveToBinary(const char * filename, unsigned int * bytesWritten, elementType elementType_) const;
372 int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten, elementType elementType_, bool countBytesOnly = false) const;
373
374 void loadFromAscii(const char * filename, elementType * elementType_, int verbose = 0);
375 void loadFromBinary(const char * filename, elementType * elementType_);
376 void loadFromBinary(FILE * binaryInputStream, elementType * elementType_);
377 void loadFromMemory(unsigned char * binaryInputStream, elementType * elementType_);
378 void assignMaterialsToElements(int verbose);
379
380 static elementType getElementTypeASCII(const char * filename);
381 static elementType getElementTypeBinary(const char * filename);
382
383 elementType temp; // auxiliary
384
385 friend class VolumetricMeshExtensions;
386 friend class VolumetricMeshLoader;
387
388 static unsigned int readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin);
389 static unsigned int readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation);
390 };
391
Set(const std::string & name_)392 inline VolumetricMesh::Set::Set(const std::string & name_) { name = name_; }
Set(const Set & set)393 inline VolumetricMesh::Set::Set(const Set & set) { elements = set.elements; name = set.getName(); }
Set(const std::string & name_,const std::set<int> & elements_)394 inline VolumetricMesh::Set::Set(const std::string & name_, const std::set<int> & elements_) : name(name_), elements(elements_) {}
getName()395 inline std::string VolumetricMesh::Set::getName() const { return name; }
getNumElements()396 inline int VolumetricMesh::Set::getNumElements() const { return (int)(this->elements.size()); }
getElements(std::set<int> & elements)397 inline void VolumetricMesh::Set::getElements(std::set<int> & elements) const { elements = this->elements; }
getElements()398 inline const std::set<int> & VolumetricMesh::Set::getElements() const { return elements; }
getElements()399 inline std::set<int> & VolumetricMesh::Set::getElements() { return elements; }
isMember(int element)400 inline bool VolumetricMesh::Set::isMember(int element) const {return (elements.find(element) != elements.end());}
insert(int element)401 inline void VolumetricMesh::Set::insert(int element) { elements.insert(element); }
clear()402 inline void VolumetricMesh::Set::clear() { elements.clear(); }
403
Material(const std::string name_,double density_)404 inline VolumetricMesh::Material::Material(const std::string name_, double density_): density(density_) { name = name_; }
Material(const Material & material)405 inline VolumetricMesh::Material::Material(const Material & material) : density(material.getDensity()) { name = material.getName(); }
getName()406 inline std::string VolumetricMesh::Material::getName() const { return name; } // material name
getDensity()407 inline double VolumetricMesh::Material::getDensity() const { return density; } // density
setName(const std::string name_)408 inline void VolumetricMesh::Material::setName(const std::string name_) { name = name_; }
setDensity(double density_)409 inline void VolumetricMesh::Material::setDensity(double density_) { density = density_; }
410
Region(int materialIndex_,int setIndex_)411 inline VolumetricMesh::Region::Region(int materialIndex_, int setIndex_): setIndex(setIndex_), materialIndex(materialIndex_) {}
getMaterialIndex()412 inline int VolumetricMesh::Region::getMaterialIndex() const { return materialIndex; }
getSetIndex()413 inline int VolumetricMesh::Region::getSetIndex() const { return setIndex; }
setMaterialIndex(int index)414 inline void VolumetricMesh::Region::setMaterialIndex(int index) { materialIndex = index; }
setSetIndex(int index)415 inline void VolumetricMesh::Region::setSetIndex(int index) { setIndex = index; }
416
417 #endif
418
419