TopologyTracer is an MPI/OpenMP-parallelized datamining tool for conducting spatio-temporal analyses of microstructural element dynamics. Its purpose is the quantification of correlations — spatial and temporal — between individual microstructural elements and their higher-order neighboring elements. Currently, the software allows comprehensive studies of individual grains’ volume evolution and set their growth history into relation to the evolution of their higher-order neighbors. The tool is unique in two ways: on the one hand in its ability to process these surveys in parallel and thereby handle hitherto intractable large datasets including millions of grains. On the other hand in its capability to explicitly take the spatial distribution of the long range neighborhood into account.
The source code was developed by Markus Kühbach during his PhD time with Luis A. Barrales-Mora and Günter Gottstein at the Institute of Physical Metallurgy and Metal Physics (IMM) with RWTH Aachen University. Being now with the Max-Planck-Institut fur Eisenforschung GmbH in Düsseldorf, I maintain the code, though at disregular intervals. Nonetheless, feel free to utilize the tool, do not hesitate contacting me for sharing thoughts, suggesting improvements, or reporting your experiences.
1. Getting started¶
In a nutshell¶
What is the TopologyTracer?¶
Currently, a software for the datamining of dump snapshots obtained from densely sampled instrumented grain coarsening simulations.
What are the user benefits?¶
What sets the TopologyTracer apart from other tools like DREAM3D or MTex?¶
While the aforementioned tools’ strength is their ability to compute distributional descriptive statistics, like the grain size distribution or the orientation distribution function (ODF), they fall still short in their spatio-temporal and correlative capabilities. Additionally, they have much lower scalability as they do so far not employ parallelization. Thus, it remains the user’s responsibility to add parallel functionality into the source code or set up batch processing queues via modifying the analysis scripts. Either way, this is not only a tedious task but in particular with respect to the scripting environment not straightforward to implement in a manner to advise the script interpreter exactly and in full control of the user were data should be stored to employ as much memory locality as possible: that is to assure that data which describe the microstructure in local regions are mapped as closely as possible into main memory such that their processing requires less costly memory queries.
By contrast, the TopologyTracer improves on this with its two-layer data parallelism. The individual datasets, each of which represents one time step, are elements in the coarser MPI-layer. Each MPI process hosts a contiguous set of all datasets over a particular time step interval. Additionally, each dataset is stored spatially partitioned into memory regions. These become mapped in such a manner as to reduce the number of remote memory accesses. It is exactly this data-locality-improvement-oriented design which suits potential thread-parallel execution with OpenMP for instance. Several pieces of this MPI/OpenMP-hybrid parallel scheme have already been implemented.
The figure illustrates this two-level parallelism.
In particular the logical and physical partitioning of the data in the second layer is fundamental to achieve scalable performance by improving data locality. This stands in clear contrast to many proprietary Materials Science software packages which still rely on traditional data structures commonly employed during sequential programming and their execution via scripting-language-based post-processing during which the complexity of data placement is opaque to the user. This opacity is the key to making perceivingly easily maintainable most Computational Materials Science scripting workflows. At the same time, though, it is the prime brake for achieving performance and thereby productive workflows - in particular for ever growing datasets. Admittedly, one could also employ thread parallelism without worrying about locality aspects. Beyond some problem-specific thread count threshold, though, this practice results in increasingly less performant execution as internally frequent remote memory accesses will be required. Exactly, these will then effectively saturate most contemporary memory infrastructures on not only super computers but also workstations and therefore prevent the quick delivering of data to the floating point processing units.
References¶
The method¶
- The method and the key to its implementation.
- Kühbach, M.On the Significance of the Long-Range Environment and Capillary Contributionsfor Nucleating Abnormal Grain Growth and Recrystallizationsubmitted to Acta Materialia, 2018Kühbach, M., Mießen, C., Barrales-Mora, L. A., Gottstein, G.Simulation and data-analytics of sub-grain growth with considerationof stored elastic energy and anisotropic grain boundary propertiesProceedings of the 6th International Conference on Recrystallization and Grain Growth2016 (ReX & GG 2016) in the Omni William Penn Hotel in Pittsburgh, PA, U.S.ISBN 978-1-119-32835-3Kühbach M., Barrales-Mora L.A., Mießen C., Gottstein G.:Ultrafast analysis of individual grain behavior during grain growth by parallel computingProceedings of the 36th Riso International Symposium on Materials Science
- Further details are provided within my dissertation entitled
- Kühbach, M.Efficient Recrystallization Microstructure Modeling by Utilizing Parallel ComputationPhD thesis RWTH Aachen UniversitySuccessfully defended and accepted for publication in August, 2017Finally open source published in January, 2018
Contact¶
Feel free to contact me and allow me to identify whether the TopologyTracer supplies functionalities which can cater also your analyses needs! Interested? Please contact me
Specifically about GraGLeS¶
Currently, the GraGLeS model is one of the very few grain coarsening simulation packages available that is capable of stressing the TopologyTracer.
Mießen, C. Velinov, N., Gottstein, G., Barrales-Mora, L. A.A highly efficient 3D level-set grain growth algorithm tailored for ccNUMA architectureModelling and Simulation in Materials Science and EngineeringMießen C., Liesenjohann M., Barrales-Mora L.A., Shvindlerman L.S., Gottstein G.An advanced level set approach to grain growth – Accounting for grain boundary anisotropy and finite triple junction mobilityActa Materialia, 2015, 99Mießen, C.A massive parallel simulation approach to 2d and 3d grain growthPhD thesis RWTH Aachen UniversitySuccessfully defended in November, 2017
Third-party contributions¶
The TopologyTracer makes use of third-party contributions for some of its functionalities:
- RapidXML: for the XML-based processing of control files and parameterization
- Kalicinski, M.
- Poly2Tri: for the triangularization of non-self-intersecting polygons
- Robust predicates are required for numerical stable computational geometry. For these tasks Poly2Tri relies on
- Shewchuk, J. R.Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric PredicatesComputational Geometry, 1997, 18, p305
- Boost C++ library for probing the project folder structure to obtain meta data automatically
- Eigen open-source library for performing various geometrical operations and the fitting of circles to point clouds for principal curvature radius estimation.
- The detection of the intersection area between arbitrary triangles and a circle was inspired by the algorithm idea described in
Furthermore, I would like to acknowledge the work of S. Strobl et. al. who provided an open source algorithm to the numerical robust computation of the intersection volume of an arbitrary tetrahedron and with a sphere:
Strobl, S., Formella, A., Pöschel, T.Exact calculation of the overlap volume of spheres and mesh elementsJournal of Computational Physics, Vol 311, 2016, p158
Additionally, I acknowledge the tetrahedralization project TetGen by Hang Si, which enabled me at least to probe in a substantiated manner the potential extension of the above-mentioned intersection problem in 3d:
Hang SiTetGen A Quality Tetrahedral Mesh Generator and a 3D Delaunay Triangulator
Its source code is not part of the TopologyTracer due to licencing issues but can be linked to the program, for which the interested user is referred to the source code.
Setup¶
Which prerequisites are necessary?¶
The compilation of the program utilizes open source libraries as well as standard Linux tools.
- Check your Linux installation for a working installation of a C/C++ build system including cmake and make.
- Check your C and C++ compiler. We utilized successfully the Intel (v14.0 and newer) and the GNU (v4.8 and newer) compiler.
- You need a working installation of an MPI (Message Passing Interface) API library to be able to compile the program.
- We utilized successfully both the OpenMPI (v1.10.4 and newer) and the IntelMPI (2017.0.12) implementation.
- The minimum threading support level of the MPI implementation required is MPI_THREAD_FUNNELED.
- You need a working installation of the Boost C++ libraries. Details on where to get it and how to install are found here.
- The shipped-with default version of Boost of at least Ubuntu 16.04 provided to me all functionality required.
- You need the Eigen libraries that come along with the TopologyTracer package. Further information to this library is provided.
Which prerequisites are optional?¶
- It is well-known that the general purpose standard malloc(3) memory allocator class implementation does not assure that in particular many small allocations become locality-aware placed. Hence, the performance of the TopologyTracer can be improved by linking against an alternative memory allocator class, such as Jason Evans jemalloc. A documentation of how to obtain the library and how to compile it can be found online.
- In the future, an OpenMP-parallelization and HDF5 extension of the TopologyTracer is planned. So far however HDF5 is not required to run the TopologyTracer.
Where to get source code from?¶
TopologyTracer is available for Linux only. It is free software.
- Download the source from its git repository https://github.com/mkuehbach/TopologyTracer
- Eventually unpack the repository such that finally the following ends up in a single folder.
- This folder from now on will be called the root directory. You can give it any Linux-conformant name.
- Make sure in this root there is a src subdirectory with the cpp and the h source code files,
- a build directory for storing the executable and an exemplary XML control file.
- Additionally, check whether there is a CMakeLists.txt file in the root folder.
- Next, utilize the top section of this CMakeList.txt file to switch on and off the compiler (GNU or Intel)
- Next, open a console and dive into the build directory.
- If now its the first time you compile the TopologyTracer type cmake ...
- This inspects your system and generates a customized makefile for you.
- Next use this makefile by typing make to compile the program.
- Upon success you should now have a binary within build called topotracer2d3d
Placement of files¶
The resulting executable expects the XML control file always in its current location folder!. Relative indexing is utilized. Other than that restriction, the executable can be renamed and relocated. The latter enables the scripting of TopologyTracer job queues and executing batch jobs.
Optimization¶
If desired, adjust the level of compiler optimization via the OPTLEVEL variable in the CMakeLists.txt upper section. OPTLEVEL “-O0” means no optimization and should be utilized for debugging purposes only, while “-O3” is the maximum and recommended level for production tasks. Improvements between the two extremes vary between a factor of 2 - 5 faster with maximum optimization compared to without.
Troubleshooting?!¶
If in between the compilation process unrecoverable errors occur, attempt first a make clean command. If this does not help: Delete everything in the build folder except for the TopologyTracer2D3D_Parameter.xml control file and start over with cmake ...
Utilizing the GNU compiler? If the __int64 definition is unknown to the preprocessor and the code therefore does not complains, does not compile: define it as typedef long __int64 in the TopologyTracer_Topology.cpp.
2. Creating input¶
What is TopologyTracer input?¶
A temporal sequence of microstructure dynamics snapshots. Currently, the TopologyTracer requests each of these snapshots as to be pairs of two binary files: a Faces_*.bin and a Textures_*.bin file. The asterisk placeholder (*) stands for a set of integer numbers separated by a constant offset, i.e. (1,2,3,4) or (10, 20, 30, 40) for instance, on an interval [SnapshotFirst,SnapshotOffset,SnapshotLast]. As the format of these files is generic, it is possible to feed either in-situ microstructural data from experiments or check pointing data from simulations. Currently, the TopologyTracer is implemented to work with input from the GraGLeS level-set code (https://github.com/GraGLeS). Feel free contacting me when there is interest in adding input routines for other tools. I am aware that conventions within our community differ so I am highly interested in offering such alternative entry to use my code.
Binary file format layout¶
The TopologyTracer utilizes binary data in order to provide accuracy, to minimize data storage costs, and to reduce I/O time via a one-file-per-process concept. During I/O operations all binary files are interpreted assuming
- LittleEndian byte order
- The default MPI view of the file is as of a linear byte stream
- Namely, displacement = 0, etype = MPI_BYTE, filetype = MPI_BYTE
In what follows a 64-bit C/C++ double (precision) floating point value reads as d, a 32-bit integer as i, a 32-bit unsigned integer as ui.
Grain boundary face data via Faces files¶
The Faces_*.bin file is headerless and contains all disjoint grain boundary faces. It reads as a contiguous block of as many MPI structs as faces exist. Each struct specifies the segment length and the two disjoint integer IDs A, B (with A > B >= 0). These IDs reference two grains that share the boundary face. As such, each boundary is stored only once. Currently, its description occupies 16B on a 64-bit machine. A single entry reads as follows:
d ui ui
Grain meta data via Texture files¶
The Texture_*.bin file is headerless as well. It contains the properties of all grains at the timestep, i.e. size, barycenter position to name but a few. The binary file encodes a contiguous block of as many MPI structs as grains remain. Each MPI struct encodes the properties of a disjoint grain. The struct layout differs for 2D and 3D data in an effort to reduce the amount of data!
In 2D the order is as follows: Area, Perimeter, GBEnergy, Stored elastic energy, Bunge-Euler phi1, Bunge-Euler Psi, Bunge-Euler phi2, Barycenter X, Barycenter Y, GrainID, First-order neighbor count, Contact with boundary yes/or no, padding.
In 3D the order is as follows: Area, Perimeter, GBEnergy, Stored elastic energy, Bunge-Euler phi1, Bunge-Euler Psi, Bunge-Euler phi2, Barycenter X, Barycenter Y, Barycenter Z, GrainID, First-order neighbor count, Contact with boundary yes/or no, padding.
The MPI struct encodes these pieces of information as follows:
d d d d d d d d d ui ui ui ui
d d d d d d d d d d ui ui ui ui
Consequently, a grain occupies 88B in 2D and 96B in 3D, respectively on a 64-bit architecture when utilizing the default byte order.
Illustrative example of expected datavolume¶
For a 3D polycrystal with 500,000 grains spanning a boundary network of 3,500,000 individual faces a single snapshot requires ((5.0e5 * 96B) + (3.5e6 * 16B)) / 1024 / 1024 KB/B MB/KB = 99.2MB disk space. For comparison, a practical peak bandwidth of a Lustre parallel file system for a one-file-per-process I/O scenario with a few MPI processes ranges typically between 1000-15000 MB/s.
3. TopologyTracing¶
TopologyTracing requires at least to have
- A collection of ID contiguous binary data file pairs, i.e. Faces_*.bin and Texture_*.bin binary files,
- The topotracer2d3d executable,
- A XML control file all in the same folder.
Several analysis routines require additional files as it is explained for many in the input section.
- With these prerequisites, TopologyTracing, i.e. the datamining of simulation data, requires two steps:
- 1. Setup the control file2. Specify the post-processing tasks
Both tasks are accomplished via the XML control file:
XML Control File Settings¶
The entire datamining is controlled by only one control parameter settings file — the TopologyTracer2D3D_Parameter.xml file. All angular values are expected in degrees!
Analysis mode¶
Fundamental settings¶
Physical properties¶
Datamining operations¶
The following options define which analyses should be executed (1) or not (0).
The following functions are currently UNDOCUMENTED
Datamining operation specific settings¶
Datamining is performed on the timestep interval [SnapshotFirst, SnapshotLast] in increments of SnapshotOffset. Grains with contact to the simulation domain boundaries (special grain ID 0) are expelled from the analyses in every case by default. The code allows for the implementation of utilizing results from simulations that were conducted under periodic boundary conditions to enable higher statistical significance. The latter is of particular interest for 3D simulations and in particular when quantifying the higher-order neighbors. The results are either stored in ASCII files (*.csv) or as binary matrices 2D (*.bin). The latter have no header but a speaking filename (<whatever>.F.<SnapshotFirst>.O.<SnapshotOffset>.L.<SnapshotLast>.NC.<ncols>.NR<nrows>.bin) which specifies the number of columns (NC) and rows (NR), respectively. In general, columns are column vectors, each of which encoding all grains at one time step, while rows are row vectors which specify properties of one grain along all time steps. With these definitions the binary results file read as a 2D matrix (grains-time) in implicit form by aligning entire column vectors according to their row ID contiguously in memory.
Auxiliary files¶
Additional settings¶
Program execution¶
MPI only¶
All set? Excellent! Then, the tracing is executed via one command line call:
mpiexec -n <nprocesses> <topotracer> <simid> <TopologyTracer2D3D_Parameter.xml> <optional: arguments>
Please note that the angle brackets must not be typed into the command line as they only mark the input parameter!
In its current version the following input arguments are required:
- <nprocesses> How many MPI processes to utilize?
- <topotracer> The name of the executable.
- <simid> JobID, a positive integer to distinguish the datamining results from runs with other settings but the same raw data.
- <TopologyTracer2D3D_Parameter.xml> a properly formatted XML control file. The name can be changed as long as the file remains a properly formatted XML file.
Be careful: if the <simid> value is set to the same value during subsequent runs in the same folder, data will be overwritten without prompting!
Below is a typical example call which executes the program with 10 MPI processes, reads from MySpecialSettings what should be done and tags all output with a consistent run ID, 1000 in this case:
mpiexec -n 10 topotracer2d3d 1000 MySpecialSettings.xml
MPI/OpenMP¶
Core functionalities within the analyses modes 4, 5, and 6 are additionally OpenMP thread parallelized. For this, the OpenMP environment variable OMP_NUM_THREADS must be set to the desired number of threads spawned at most per MPI process. Otherwise the program call is as above:
export OMP_NUM_THREADS=10
When utilizing exemplarily the Intel compiler it is usually worthwhile to reconsider the thread placement on the computing cores. One can guide this operating system decision by for instance the KMP AFFINITY environment.
Compile a growth history for individual grains¶
Consider the Matlab scripts in the scripts/ folder.
Please note that these scripts provide only the key functionalities to import TopologyTracer results data into MATLAB. Hence, they require adaptation to the analysis target desired.
These scripts are the originals I used for post-processing the long-range effects paper results:
Kühbach, M.On the Significance of the Long-Range Environment and Capillary Contributionsfor Nucleating Abnormal Grain Growth and Recrystallizationsubmitted to Acta Materialia, 2018
Output data format specification¶
The TopologyTracer generates a number of binary results file which can be visualized and processed with Matlab. These files do not include the simulated or scaled real physical time. That one has to be supported manually as a part of the Matlab analysis!
In what follows, a 64-bit C/C++ double precision floating point value reads as d, a 32-bit integer as i, and a 32-bit unsigned integer as ui. The byte order is read and written assuming LittleEndian. MPI I/O views by default all files with displacement = 0, etype = MPI_TYPE and filetype = MPI_BYTE. The TopologyTracer follows this default. All files encode as 2D matrices of an elemental datatype (either d,i, or ui, or an MPI struct) as a number of nc columns of a contiguous block (row) of size nr each. All means are arithmetic mean values.
The results filenames are speaking with NR = nr and NC = nc!
Mind that in all what follows the statement all grains means explicitly all grains filtered for the analysis. This number is not necessarily equal to the number of grains in the population, because if the simulation is performed with open boundary conditions the grains touching the domain boundary have been expelled from the analysis! In general the TopologyTracer assumes that the grain with ID 0 is a special grain, namely a proxy ID for the RVE domain itself. Equivalently, we can state that the TopologyTracer utilizes Fortran ID labeling for grains and faces.
analyze_vol_quantiles()¶
Writes two files VolQuantiles which specifies the n = STATISTICS_VOL_NQUANTILES (currently 100) (1/n,2/n,…,n/n) quantile of the distribution of grain size (area (2D), volume (3D)). Specifically, nr = n and nc = (SnapshotLast - SnapshotFirst)/SnapshotOffset + 1. The elemental datatype is d.
The file VolMeta provides descriptive statistics with nr = 1 and nc as above. The elemental type is a contiguous MPI struct of five d. They encode
- The total area covered by all grains considered
- The mean size
- The variance
- The total number of grains in the snapshot
- The total number of grains considered (for mean and var)
analyze_vol_forward()¶
Writes four 2D matrices, all with the same layout. Except for FW.NF, whose elemental datatype is ui, all others have d.
- FW.NF is the number of neighbors
- FW.VOL is the size of the grains (micron^Dimensionality)
- FW.HAGBFRAC gives fraction of boundary (length (2D), area (3D) with disorientation of target and neighbor >= HAGBDetectionThreshold
- FW.MOBDSEE gives area-weighted product of mobility * (stored elastic energy nbor - stored elastic energy target) (m/s)
Values for no longer existing grains get filled up with zeros!
analyze_vol_backward()¶
Monitor for all targets (grains which survived the coarsening step up to SnapshotLast) backwards in time for the evolution of their faces, size, HAGB fraction, and instantaneous biased velocity. Even though tracking is performed backwards, the organization of the matrix is positive in time, i.e. higher column vector indices mean later in time.
The layout and content is the same as for analyze_vol_forward(). The only differences are the file name suffixes.
- BK.NF
- BK.VOL
- BK.HAGBFRAC
- BK.MOBDSEE
analyze_sizegain_vs_bk()¶
Idea: for all survivors identify in each snapshot how their size is relative to the mean of the matrix. The matrix constitutes of all grains excluding the survivors. Writes one 2D matrix of elemental type d. Each first entry in each row describes the number of matrix grains remaining, while the second entry the mean size of the matrix grains.
- SIZEGAINBK
analyze_approx_rxfraction()¶
Calculate approximate recrystallized area/volume fraction assuming the following. Matrix grains constitute all grains. The recrystallized grains are all those which survive the coarsening, i.e. all remaining up to SnapshotLast. A 2D matrix with nr = 1 is written. The elemental datatype is a contiguous MPI_struct of d. These detail
- The total size of all grains considered in the analysis
- The total size covered by the targets, i.e. the considered as recrystallized
- The recrystallized coverage as TotalSizeTargets/TotalSizeAllGrains
- The total number of all grains considered in the analysis but were still detectable in the snapshot
- The total number of rxgrains
analyze_modf()¶
Writes a 2D matrix of doubles (d) which encodes the disorientation distribution function (MDF) with equiangular adjustable binning, via nr = largest positive integer ( MaxDisoriAngle/DisoriAngleBinWidth) and nc = 1 + ((SnapshotLast-SnapshotFirst)/SnapshotOffset+1). The first row of the matrix gives the bin ends in degrees. Thereafter, the MDF for each snapshot follows.
analyze_see()¶
Same layout as analyze_modf. First row gives bin ends. Thereafter, each snapshot.
analyze_gsd()¶
Equal in mentality and data output structure to SEE and MODF.
analyze_abnormal_graingrowth()¶
Writes a 2D matrix of structs containing 13 doubles (d) each which encode the following
The total size of all grains in the list of
- survivors
- matrix
- matrix exclusive survivors
The equivalent radius of
- survivors
- matrix
- matrix exclusive survivors
The remaining number of grains in
- survivors
- matrix
- matrix exclusive survivors
The number of abnormal large grains, i.e. whose equivalent radius is larger than 3.0 the average of the individual populations
- matrix exclusive survivors abnormal when testing against mean of matrix
- matrix exclusive survivors abnormal when testing against mean of matrix exclusive survivors
- survivors abnormal when testing against mean of matrix
- survivors abnormal when testing against mean of matrix exclusive survivors
Mind that when approaching SnapshotLast the population of matrix grains ceases because this understood as the matrix into which the survivors grow.
Currently undocumented¶
- analyze_grainsize_quantiles()
- analyze_drivingforce_see()
- analyze_topologydifference_forward()
- analyze_classical_nucmodels()
- analyze_knn_naive()
4. Visualizing results¶
In its current state, the TopologyTracer does not contain a GUI. Instead, plain MPI I/O raw files and ASCII files are generated which require post-processing with for instance Matlab scripts available in the scripts folder. In general one can visualize a growth path by the plot3(X,Y,Z) command and access a distribution of values via the histogram function. Therein X, Y, Z specify each a row vector of length NC for a particular grain ID in row r.
Funding¶
Version history¶
Licence¶
The project is licenced under the GNU v3.0¶
Copyright Markus Kühbach, 2015-2017
TopologyTracer is an MPI-parallel datamining tool for the conducting of spatio-temporal analyses of microstructural element dynamics. Its purpose is the quantification of correlations — spatial and temporal — between individual microstructural elements and their higher-order neighboring elements. Specifically, its current functionalities allow to study the volume evolution of individual grains over time and set their growth history into relation to the evolution of the higher-order neighbors. The tool is unique insofar as it allows processing these individual surveys in a parallelized manner. Thus, enabling the post-processing of so far intractable large datasets.
The source code was developed by Markus Kühbach during his PhD time with Luis A. Barrales-Mora and Günter Gottstein at the Institute of Physical Metallurgy and Metal Physics with RWTH Aachen University. Being now with the Max-Planck-Institut fur Eisenforschung GmbH in Dusseldorf, I maintain the code, though at disregular intervals. Nonetheless, feel free to utilize the tool, do not hesitate contacting me for sharing thoughts, suggesting improvements, or reporting your experiences. markus.kuehbach at rwth-aachen.de and m.kuehbach at mpie.de
The authors gratefully acknowledge the financial support from the Deutsche Forschungsgemeinschaft (DFG) within the Reinhart Koselleck-Project (GO 335/44-1) and computing time grants kindly provided by RWTH Aachen University and the FZ Jülich within the scope of the JARAHPC project JARA0076.
This file is part of TopologyTracer.
TopologyTracer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
TopologyTracer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with SCORE. If not, see <http://www.gnu.org/licenses/>.
Questions, contributions¶
Just let me know or contact m.kuehbach@mpie.de