/florence

A finite element framework for Python's scientific stack: arbitrary order planar/curvilinear mesh generation and finite element methods for linear and nonlinear analysis of coupled multiphysics problems

Primary LanguagePythonMIT LicenseMIT

Build Status Coverage Status

Florence is a Python-based computational framework for the numerical simulations of multi-physics problems using the finite element methods.

Features

A non-exhaustive list of core features:

  • High order planar and curved finite and boundary elements (line, tri, quad, tet, hex)
  • In-built CAD-conformal curvilinear mesh generator
  • Powerful in-built pre and post processor with the ability to visualise high order curved meshes
  • Poisson, electrostatic and heat transfer solvers
  • Linear, geometrically linearised and fully nonlinear solid/structural mechanics solvers
  • Linear, geometrically linearised and fully nonlinear electromechanics solvers
  • Implicit and explicit dynamic solvers with contact formulation
  • Generic monolithic, staggered and multigrid solvers for coupled multiphysics driven problems
  • Strain gradient and micropolar elasticity and electro-elasticty solvers
  • A suite of advanced hyperelastic, electrostatic and electro-hyperelastic material models
  • Ability to read/write mesh/simulation data to/from gmsh, Salome, GID, Tetgen, obj, FRO, VTK and HDF5
  • Support for heterogeneous computing using SIMD, shared parallelism, cloud-based parallelism and cluster-based parallelism
  • Interfaces to a suite of sparse direct and iterative solvers including MUMPS, Pardiso, Petsc and hypre

In addition, the framework also provides Python interfaces to many low-level numerical subroutines written in C, C++ and Cython.

Platform support

Florence supports all major operating systems including Linux, macOS and Windows (under Cygwin/MinGW) under

  • Python 2.7
  • Python >= 3.5
  • PyPy >= v5.7.0

Dependencies

The following packages are hard dependencies

  • Fastor: Data parallel (SIMD) FEM assembler
  • cython
  • numpy
  • scipy

The following packages are optional (but recommended) dependencies

  • PostMesh: High order curvilinear mesh generator
  • pyevtk
  • matplotlib
  • mayavi
  • scikit-umfpack
  • pyamg
  • psutil
  • h5py

In addition, it is recommended to have an optimised BLAS library such as OpenBLAS or MKL installed and configured on your machine.

Installation

The easy way

using pip

pip install Florence

For pip installation to work you need to have Fastor installed. You can achieve this by

cd ~
git clone https://github.com/romeric/Fastor
mv Fastor/ /usr/local/include/Fastor/

It is also a good practice to set your compilers before pip installing florence

export CC=/path/to/c/compiler
export CXX=/path/to/c++/compiler

Building from source

Have a look at travis.yml file for directions on installing florence's core library. First install cython, numpy and scipy. Download Fastor headers and place them under their default location /usr/local/include/Fastor

cd ~
git clone https://github.com/romeric/Fastor
mv Fastor/ /usr/local/include/Fastor/

Then installation of the core library is as easy as

git clone https://github.com/romeric/florence
cd florence
python setup.py build
export PYTHONPATH="/path/to/florence:$PYTHONPATH"

This builds many low-level cython modules, ahead of time. Options can be given to setup.py for instance

python setup.py build BLAS=mkl CXX=/usr/local/bin/g++ CC=~/LLVM/clang

By default, florence builds in parallel using all the machine's CPU cores. To limit the build process to a specific number of cores, use the np flag for instance, for serial build one can trigger the build process as

python setup.py build np=1

Configuring MUMPS direct sparse solver

Florence can automatically switch to MUMPS sparse direct solver if available. To install MUMPS, the easiest way is to use homebrew on macOS and linuxbrew on linux:

brew install mumps --without-mpi --with-openblas
git clone https://github.com/romeric/MUMPS.py
cd MUMPS.py
python setup.py build
python setup.py install

And whenever MUMPS solver is needed, just open a new terminal window/tab and do (this is the default setting for linuxbrew)

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/linuxbrew/.linuxbrew/lib

Configuring Pardiso direct sparse solver

The direct sparse solver shipped with MKL, Pardiso can be used if MKL is available. Both Anaconda and Intel distribution for python ship these. If MKL is installed, the low-level FEM assembler in florence is also automatically linked to it during compilation, as long as "BLAS=mkl" flag is issued to setup.py.

conda install -c haasad pypardiso

We typically do not recommed adding anaconda/bin to your path. Hence, whenever MKL features or Pardiso solver is needed, just open a new terminal window/tab and type

export PATH="/path/to/anaconda2/bin:$PATH"

Philosophy

Florence follows scipy's philosophy of providing a high level pythonic interface to finite element analysis of partial differential equations. It is a light weight library that depends only on the most ubiquitous python packages namely numpy, scipy and cython. Yet it is aimed to deliver high performance numerical computations on a range modern architectures. It is backend is designed to be configurable for plugging new solvers such as Petsc's and hypre's parallel solvers.

Documentation

Documentation is available under wiki pages. Furthermore, a series of well explained examples are provided in the example folder that cover most of the functionality of florence.

To get a quick taste of Florence, let us consider the Laplacian for example. Setting up and solving the Laplace equation using fourth order hexahedral Lagrange shape functions over a cube is as simple as

import numpy as np
from Florence import *


def simple_laplace():
    """An example of solving the Laplace equation using
        fourth order hexahedral elements on a cube
    """

    # generate a linear hexahedral mesh on a cube
    mesh = Mesh()
    mesh.Cube(element_type="hex", nx=6, ny=6, nz=6)
    # generate the corresponding fourth order mesh
    mesh.GetHighOrderMesh(p=4)

    # set up boundary conditions
    def dirichlet_function(mesh):
        # create boundary flags - nan values would be treated as free boundary
        boundary_data = np.zeros(mesh.nnode)+np.NAN
        # potential at left (Y=0)
        Y_0 = np.isclose(mesh.points[:,1],0)
        boundary_data[Y_0] = 0.
        # potential at right (Y=1)
        Y_1 = np.isclose(mesh.points[:,1],mesh.points[:,1].max())
        boundary_data[Y_1] = 10.

        return boundary_data

    boundary_condition = BoundaryCondition()
    boundary_condition.SetDirichletCriteria(dirichlet_function, mesh)

    # set up material
    material = IdealDielectric(mesh.InferSpatialDimension(), eps=2.35)
    # set up variational form
    formulation = LaplacianFormulation(mesh)
    # set up solver
    fem_solver = FEMSolver(optimise=True)
    # solve
    results = fem_solver.Solve( boundary_condition=boundary_condition,
                                material=material,
                                formulation=formulation,
                                mesh=mesh)

    # write results to vtk file
    results.WriteVTK("laplacian_results")


if __name__ == "__main__":
    simple_laplace()