G+Smo
24.08.0
Geometry + Simulation Modules
|
In this tutorial you will get to know the structure of the library, some main tools which are available as well as how to write your first program.
The source code can be obtained via Subversion or Git. See the section Downloads of the wiki for downloading information.
The configuration/compilation is based on the CMake build management system. A C++ compiler should be available in the system. See the section Compiling of the wiki for information on compilation.
The source folder contains the following sub-folders:
src
In this folder we have the source files of the library. Code is partitioned into modules, which correspond to folders inside src. Here is a list of these folders/modules:
Contains several general objects that are useful for the rest of the modules. Most importantly it contains abstract classes such as gsFunction and gsBasis which represent functions and sets of bases on a parametric domain. Continue here.
Contains classes for matrices, vectors, and sparse matrices. These objects are derived from the Eigen linear algebra library.
Contains classes for knot-vectors, univariate B-splines, tensor-product B-splines as well as NURBS and tensor-product NURBS classes.
Contains classes for hierarchical B-spline basis, as well as truncated hierarchical B-spline basis. These are based on a tree-structure which holds the domain information. It also contains adaptive fitting algorithms using hierarchical splines.
Contains classes related to modeling with geometric objects. The main functionality is currently fitting and boundary-represented solids.
Contains classes which represent boundary value problems of partial differential equations. This includes the PDE definitions, boundary conditions and point loads.
Contains classes which allow us to generate (assemble) the matrices and right-hand side(s) of a boundary value problem by means of (Gauss) quadrature. Also, it contains classes which allow us to compute the norms or semi-norms of the difference of two functions, error estimators and related utilities.
Contains iterative linear solvers which complement the ones from the Eigen library, such as GMRES. It also contains multigrid solvers and preconditioners.
Contains utilities related to data input and output. This includes helpers for adding command line arguments to executables, reading data from files and writing data to the disk. It implements the XML file format of G+Smo. Finally, there are functions which generate Paraview files for visualization.
Contains classes and function to manipulate tensor-structured objects.
Contains several utilities that are needed by other modules. This includes combinatorics utilities, point grid generators and iterators, utilities for measuring the CPU time, and a data structure for triangular meshes.
examples
Contains source code for the examples, small programs and tutorials. Each of these files has extension .cpp and produces an executable upon compilation.
filedata
Contains some example data files in the XML format the G+Smo can read and write.
optional
Optional additional modules that can be compiled along G+Smo, typically extending functionality, sometimes also requiring external packages, which may be automatically downloaded. For instance, the Parasolid module requires the Parasolid library, and IpOpt module requires the IpOpt optimization library. The OpenNurbs module is used to read and write the Rhino3D 3DM file format, but it does not require any external package, since the source code of the OpenNurbs library is already in /external.
plugins
The plugins are typically dynamic libraries which wrap G+Smo for linking with external software and for exporting functionality to third party software. A plugin is available for the Axel geometric modeller (INRIA).
cmake
Contains Cmake configuration script files which are required for compilation.
doc
Contains files related to Doxygen documentation pages.
The build folder has the following subfolders and files:
lib
In this folder the G+Smo library is created. On MS Windows the dynamic library will be called gismo.dll, while on linux libgismo.so and on OSX libgismo.dylib. The static libraries are gismo_static.lib and libgismo_static.a, respectively. When typing "make -j2 gismo" in the root build folder the dynamic library is created. If you need a static library then "make -j2 gismo_static" will do the trick.
bin
Inside this folder all the compiled executable files will be created upon compilation. In particular, each .cpp file in the examples folder corresponds to the file in this folder, with the same name. On MS Windows, the file will have extension .exe, while on Linux they will be marked as executable files. If this folder is empty, then type "make -j2 examples" in the root build folder to trigger the compilation.
On a command line, you can compile (and execute) specific examples, for instance to execute the executable coming from examples/gsView.cpp it suffices to issue inside the build folder:
Note that on Linux you need to provide correctly the path (starting with dot-slash-bin), in order to execute a file. Similarly on Windows you should execute (eg. from a command line) the file gsView.exe
doc
This folder contains files related to Doxygen documentation. In particular, if Doxygen is available on the system, and inside the root build folder we execute:
then the resulting main Doxygen page will be created in doc/html/index.html and can be opened with a web-browser.
CMakeFiles
This folder contains files related to the CMake configuration. Similarly several files (eg. with extension .cmake) which are found in the root folder are related to CMake. Most important of the latter files is the CMakeCache.txt file which keeps all the needed paths (eg. the path to the source folder) and all compilation flags and options.
src, examples, external, optional
These folders are named as the corresponding folders in the source folder, and contain the compiled object files. All files in these folders are temporary, and their goal is to allow one to re-compile the library fast when there are only a few changes in the source files (while all the other object files are unchanged). Most of the time we do not need to care about or work in these folders.
There are a number of options which parametrize the compilation process. The default options should work most of the time. For a list of the available options have a look at this wiki page.
The most important setting is setting the build type. As common to the CMake build management system, there are three basic build types:
Build type | Characteristics |
---|---|
Debug | No optimizations, NDEBUG not defined |
RelWithDebInfo | Optimized code, but NDEBUG defined |
Release | The macro NDEBUG is defined, fully optimized |
The default build type (if not specified with the CMAKE_BUILD_TYPE setting) is Release. Contrarily to the default setting of CMake, in G+Smo the macro NDEBUG is defined in this setting. Therefore in RelWithDebInfo mode you still get a fairly good checking level, with the cost that the performance is reduced.
The Debug mode is often too slow. However, as a last resort for chasing bugs this mode should be tried. Several times RelWithDebInfo inlines functions, and causes the debug information to point to the wrong line in the code.
The Release mode is the fully optimized, production code.
A toy program using the library looks as follows.
All the objects of the library are found inside the gismo namespace.
There are pre-defined streams that should be preferred:
std::cout
and should be preferred over it.Note that the end-of-line character "\n"
should be preferred over std::endl
.
In Debug/RelWithDebInfo the assertions of the type are executed:
In Release Mode these commands are striped out of the code. An assertion which executes even in release mode is the following:
Also, the following emits an runtime error exception (without check condition):
Common arithmetic types are defined by macros in G+Smo, and should be preferred:
Note that index_t and size_t might be different types, therefore care should be taken when comparing their value.
Many template classes (for instance matrices) have a default first argument which is in most cases real_t.
A handy class called gsCmdLine for adding command line options to your programs. Adding options to a program makes it more friendly to the users, while allowing you to execute many different scenarios without having to change the source code and re-compile the source file.
Command-line arguments are useful to avoid re-compiling your code every time a small parameter is altered, as well as to make your program easily accessible by others. In G+Smo there is an easy way to add command-line arguments, which come automatically with a small help message and error checking, see commandLineArg_example.cpp.
In the file gsView.cpp for instance, we find the following code for parsing the command line:
The last argument of each "add" command is, as expected, a boolean (for the addSwitch), or an integer (for the addInt). The gsCmdLine::getValues command parses all the user's input and updates the variables with new values. Therefore, after this command, the value of all the variables can be different. If an argument is not provided by the user, then the value of the variable does not change.
After compiling the program we can issue:
and we obtain a list of the arguments together with the short explanation that we provided in the definition of the argument. Note that all arguments are checked automatically during parsing, eg. duplicate arguments and other subtle errors are detected.
Combined with reading the input data from files, this feature allows you eg. to write code which works for any input file which contains data that G+Smo can read, merely by adding a command line argument which accepts a string describing a filename.
The native format for data input, output and data exchange is XML. In G+Smo we define a number of tags that are used to read and write respective objects (classes). Typically, the data in an XML tree which represent, for instance, a B-spline, follow the logic of the classes in the code. As a first example the file square.xml is:
The space between the numbers and line breaks do not matter. The plain text files can become big in size, when the data is a lot. For this reason, a compressed XML format is also supported, which can also be read/written directly.
All the tags in the XML tree of a G+Smo XML file must be contained inside the <xml
> tag. Arbitrary data can be contained inside an XML file, as long as they are valid XML files. If you write (a small) XML file yourself, you can validate it for instance this page to make sure that there are no errors. In any case, invalid files will not be read properly by G+Smo and errors will be emitted. Each object in a G+Smo XML is also expected to have a unique ID attribute, which can be used for identifiing the specific data in the file.
The XML file format is extensible. As soon as objects/classes are created, a new tag for reading or writing the data can be added in the XML manager.
To read a file in your program the recommended solution is to first define an input argument for your program which will be a string holding the name of the filename. For instance
In the above code note the default value of the input string: The macro GISMO_DATA_DIR always points to the filedata folder inside the source folder. Using this macro allows you to make sure that your default argument will work on any system which will compile the library. Using an absolute path such as /home/myname/gismo/filedata/square.xml will directly make your program to fail when used in a different system. Therefore the GISMO_DATA_DIR macro is highly recommended.
The main class which allows you to read data from the file is the gsFileData object. In the following we read a polymorphic gsGeometry object from the file:
When creating the gsFileData object, all the XML data in the file are loading into memory as an XML tree. This XML tree can contain many different objects. In the above, the gsFileData::has helper searches the tree for the existence of a gsGeometry tag. This tag corresponds to a polymorphic geometry object (which can be a polynomial curve, surface or volume or a rational NURBS patch, or a different type of geometry/patch). In the case that a gsGeometry object is found, a pointer is allocated and returned to the user. As the name of the get function suggests, the returned object is the first object which was encountered in the XML tree.
To see what is the gsGeometry data that have been obtained, we can stream it to gsInfo:
You can have better control of which data is read from the file by reading using the ID of the tag. For instance, the following:
reads two matrices from an XML tree, that correspond to parameters and values for a fitting problem. The uv matrix is fetched from the file as the matrix inside the file with id=0, and then the xyz matrix is fetched again by its id=1. this way there is no ambiguity which object is being read from the tree (as long as we use unique ids).
Another command which appears in the above example is the safe function. This macro takes a pointer to a matrix (which is the returned type of getId, and produces a smart pointer. The smart pointer is then transferred to uv/xyz matrices. This is an emulation of the move semantics of C++11, which we do not use yet, for the sake of compatibility.
Similarly to reading XML data, we are able to create XML tree data and save them as a file in the disk:
the << operator translates the object into an XML tag. Then the save command creates a file with all the contents of the fd XML tree. Since we only pushed one object in the tree up to now, only one gsGeometry tag will be found in the resulting file.
Apart from the native XML format of the library, a number of third party formats can be imported or exported. One such useful format is Rhino's 3DM file format (note: the gsOpennurbs submodule must be enabled). Also, the Siemens' NX (Parasolid) file format is supported, if the Parasolid library is available in the system (note: GISMO_WITH_PSOLID compilation parameter). Other formats include the GeoPDE (Matlab/octave) text format, the GoTools format and OBJ, OFF and STL mesh files. The IGES file format is under development.
The matrices in G+Smo are obtained from the Eigen linear algebra library.
All the functionality of Eigen::Matrix is available in the (inherited) class gsMatrix. The linearAlgebra_example.cpp contains starting examples for using matrices.
The sparse matrix class is gsSparseMatrix and is derived from Eigen::SparseMatrix class. Several sparse linear solvers are available, demonstrated in sparseSolvers_example.cpp. The main information about the matrix manipulations can be found in the Eigen documentation pages. We link to the most important chapters:
The quick reference pages give a summary of the functionality:
There is also a list with the correspondance to Matlab functions. However, note that some operations of Matlab are not mirrored (yet) with functionality in Eigen. For example, a reshape function does not exist. The Topic Aliasing is also important to learn how to avoid bugs and write more efficient code.
All the information and functionality in the above pages apply to the gsMatrix, gsVector and gsSparseMatrix, gsSparseVector classes. For certain functionalities, which we need to extend or adapt in G+Smo, for example for the sparse liner solver classes, there are more objects defined which correspond to Eigen objects. In general, we try to avoid the direct use of the Eigen namespace inside the library, whenever possible.
The sources of the Eigen linear algebra library are included in the code repository of G+Smo, for your convenience.
Matrix operations of Eigen should be preferred over using for loops or manual functions. One reason for this is the clarity of the code, the reduction of lines of code and last but not least the fact that the provided operations are vectorized therefore they are more efficient than manual coding.
As a simple example, consider that you would like to multiply each row of a matrix by a number. Here are some equivalent ways to do this:
Output:
v is: 0.68 -0.211 0.566 0.597 M is: 0.823 -0.444 -0.27 0.271 -0.967 -0.687 -0.605 0.108 0.0268 0.435 -0.514 -0.198 -0.33 -0.0452 0.904 -0.717 -0.726 -0.74 0.536 0.258 0.832 0.214 0.608 -0.782 R1 is: 0.56 -0.302 -0.184 0.185 -0.658 -0.467 0.128 -0.0228 -0.00566 -0.0918 0.109 0.0418 -0.187 -0.0256 0.512 -0.406 -0.411 -0.419 0.32 0.154 0.497 0.128 0.363 -0.467 R2 is: 0.56 -0.302 -0.184 0.185 -0.658 -0.467 0.128 -0.0228 -0.00566 -0.0918 0.109 0.0418 -0.187 -0.0256 0.512 -0.406 -0.411 -0.419 0.32 0.154 0.497 0.128 0.363 -0.467 R3 is: 0.56 -0.302 -0.184 0.185 -0.658 -0.467 0.128 -0.0228 -0.00566 -0.0918 0.109 0.0418 -0.187 -0.0256 0.512 -0.406 -0.411 -0.419 0.32 0.154 0.497 0.128 0.363 -0.467
From these 3 versions, the last one is preferred, since we know well from linear algebra that left multiplication by a diagonal matrix corresponds to multiplying the rows by the diagonal elements. Note that v.asDiagonal() does not make a copy of the vector v, but only returns an expression of the v as a diagonal matrix. Also note the noalias() function to avoid Aliasing, eg. an un-needed temporary copy of R3.
One of the most basic objects in G+Smo are the ones representing functions. There are several implementations of functions, or sets of functions. They all derive by a common class, gsFunctionSet. The main descendants of gsFunctionSet are two:
This class represents a (abstract) functions of several variables, possibly vector-valued. The function is defined over an (arbitrary) square domain, referred to as its support. One characteristic of the function is that in principle it takes non-zero values throughout its support.
The main attributes of functions (and bases) are the dimension of the parameter domain and the target domain, as well as their support. The main functionality is the ability to evaluate the function and its derivatives at given points inside the support.
One instance of function is the gsFunctionExpr. This class can be defined by a string containing the mathematical formula of a function. It makes it very easy ad efficient to define and work with complicated mathematical function with minimal effort.
This class represent an (abstract) function basis, that is, a set of basis functions. The functions are usually local, that is, they take non-zero value only at a small portion of the support of the basis. See basis_example.cpp
One more instance of a function (derived from gsFunction) is the gsGeometry. This class is essentially a function defined by a coefficient vector and a gsBasis. For example a NURBS patch will fall in this category.
Printing the geometry:
Printing the main properties:
Computing values and derivatives: