Skip to content

InSAR-invert/PyMFEM

 
 

Repository files navigation

'''''
PyMFEM
(built on mfem commit 324d66b0c7fc75225e830dcfb02647722bae1fe0)
'''''

PyMFEM is a python2.7 wrapper for MFEM, ligith-weight FEM (finite element
method) library developed by LLNL (http://mfem.org).
This wrapper is meant for a rapid-prototyping of FEM program, and
is built using SWIG 3.0.12
With PyMFEM, a user can create c++ MFEM objects and call their
method from python. We strongly recommend to visit the MFEM web site
to find more detail of the MFEM libirary.

-- Module structure --
  <root>/mfem/ser : wrapper for serial MFEM
             /par : wrapper for parallel MFEM
	     /common : helper modules 
             /Makefile_tempates: example template
	     /examples : example scripts

-- INSTALLATION --

  Please see INSTALL.txt

-- Features --
 Following features are realized in PyMFEM using SWIG interface
 in order to use MFEM more conveientely from python. 
 Some of them may be e integrated to MFEM itself in future.
 
  4-1) mfem::Vector
     Constructor using python list/numpy array:
     
        v = mfem.Vector([1,2,3.])
        v = mfem.Vector(np.array([1,2,3,4.])

        Note that when list is passed. Contents of list is copied and data is
	owned by mfem::Vector. When numpy arra is passed. Data is not copied.
	mfem::Vector holds the passed numpy array as _link_to_data attribute,
	so that python does not garbage collect the numpy array untill mfem::Vector
	is deleted.

        Constructor also get SWIG object to <double *>. However, memory
	management for this pointer object is left to a user.
	
     Array element access:
         reading element: print v[0]
	 element assinment v[0] = 3

         negative index is also possible; v[-1] is equivalent to v[size-1]
	 
     Assign method: operator= is replaced by Assine method 
        v.Assign(1.) (equivalent to v=1 in c++)
        v.Assign(<some mfem::Vector>) (copy data from mfem vector)
        v.Assign(np.array([1., 2., 3.])  (copy data from np.array)
	
     Data Access as Numpy:  v.GetDataArray()

     In c++, a new partial view to existing vector is acquired as
	Vector v(vec.GetData() + sc, sc);
     In Python, one can call either
        v = mfem.Vector(vec, offset, size)
     or use slice
        vec[offset:offset+size]  # more python-ish...

     Note that this slice returns mere new nmemory view, sharing the same vector
     object. However, be carefull that, when slice requires non contigeous momory
     view, PyMFEM returns a new vector object NOT sharing the memory.
     If one wants to get the access to a memory view with non-trivial striding,
     use GetDataArray discussed below to get a numpy array pointing the
     moemory allocated for Vector, and use slice of the newly created numpy object.
     
     Vector::GetDataArray returns numpy object viewing the data stored in C++ Vector
     object. Note that the returned numpy array does not own the data.

  4-2) mfem::Coefficient
       PyCoefficient is derived from FunctionCoefficient
       PyCoefficientT is time-dependent version
       VectorPyCoefficient  is derveid from VectorFunctionCoefficient
       VectorPyCoefficientT is time-dependent version

       When using these class, inherit one of them and define
       cls::EvalValue method as follows

       class Jt(mfem.VectorPyCoefficient):
           def EvalValue(self, x):
               return [0.,0.,  np.cos(np.abs(x[2]-0.03)/0.03*np.pi/2)]
       class Sigma(mfem.PyCoefficient):
           def EvalValue(self, x):
               return 0.01   // need to return 

       then use it

       Cof_Jt = Jt(3) // VectorPyCoefficient constructor need sdim 
       dd = mfem.VectorFEBoundaryTangentLFIntegrator(Cof_Jt);

       If one wants to call Eval with a transformation object and
       an integration point, and one can inherit PyCoefficientBase
       (or their Vector/Matrix version) and overwrite Eval methods.
       These classes are defined as a director classes in c++.
       Therefore, if one overwrite Eval method, SWIG reroute the
       call to the Python side.

  4-3) mfem::GridFunction
       GetNodalValues(i) will perform GetNodalValue(Vector(), i) and
       return numpy array of Vector()
  
       = operator is renamed as Assign method.
         (python)
	     g = mfem.GridFunction(fespace)
	     g.Assign(0)
         (c++)
	     GridFunction *g = new GridFunction(&fespace);
	     g = 0;
	     
      GridFunction(FiniteElementSpace *f, double *data) is extended to
      support offsets
          v = mfem.GridFunction(fec, <pointer to double>, offset)
      so that follwoing can be wrappeds
	  GridFucntion(fes, vec.GetData() + sc)
      the same is used for ParGridFunction
	     
  4-4) mfem::Mesh
         Wrapper of following methods are customized to
	 return either python list object or tuple of lists
	  
            GetBdrElementVertices(i)
            GetElementVertices(i)
            GetElementVEdges(i)
            GetBdrElementEdges(i)
            GetFaceEdges(i)
            GetEdgeVertices(i)
            GetFaceVertices(i)
            GetElementFaces(i)
	    GetBdrElementAdjacentElement()
	    
	 pointer passing in following methos are returned
	 as tuple
            elem1, elem2 = mesh.GetFaceElements(i)
	    
         Additional constructors:
	    # this one takes file name as string
	    Mesh(const char *mesh_file, int generate_edges, int refine,
                 bool fix_orientation = True)
		 
	    # this one takes element type as string
            Mesh(int nx, int ny, int nz, const char *type, int generate_edges = 0,
              double sx = 1.0, double sy = 1.0, double sz = 1.0)
            Mesh(int nx, int ny, const char *type, int generate_edges = 0,
              double sx = 1.0, double sy = 1.0)
	      
	    (note) an issue is Element::Type (c++ enum) is treated as int in
	    python, and therefore, the wrapper can not disingish it from the
	    following constructor.
              Mesh(int _Dim, int NVert, int NElem, int NBdrElem = 0,
    	           int _spaceDim= -1)
         
         GetVertexArray can be used to obtain Vertex point data
	 as numpy object

         GetBdrElementFace(i) returns tuple
         GetBdrArray(int idx) returns array of boundary element whose BdrAttribute is idx
         GetBdrAttributeArray() returns array of boundary attribute
         GetAttributeArray() returns array of aqttribute
         GetDomainArray(int idx) returns array of element whose Attribute is idx
	 
         SwapNodes returns nodes and onws_nodes as follows.
	    nodes, owns_nodes = mesh.SwapNodes(nodes, owns_nodes)
	    nodes, owns_nodes = pmesh.SwapNodes(nodes, owns_nodes)

  4-5) sparsemat
         RAP has two different implementations. One accept three references.
	 The other accept a pointer as a third argument, instead.
	 These two are not distinguishable from python. So, RAP_P and
	 RAP_R are added. P indicates the third argment is pointer.
	 
         Add functions are accessed as add_sparse.

         GetIArray, GetJArray, and GetDataArray. These methos gives numpy
	 array of CSR matrix data. It borrows data from SparseMatrix.
	 Therefore, be careful not to access after the matrix is freeed.	 
	 
  4-6) densemat
         Add functions are accessed as add_dense.
	 
	 elements are accessed similar to a regular python array
	 Assign method replaces = operator
         GetDataArray returns a memory view to the internal data as numpy array.
         (example)
              x = DenseMatrix(3, 3)	 
	      x.Assign(0)
              v = Vector([1,2,3,4,5,6,7,8,9])
  	      m = DenseMatrix(v.GetData(), 3, 3)
	      x.Assign(m)

              x[i,j] = xxx
	      print x[i,j]
	      
              x = DenseTensor(2, 3, 5)
	      x.Assign(1) # set all 1
	      x[5] # returns DenseMatrix with col=2 and row=3
	      print x[1,2,3]
	      x[1,2,3]=3

              # NOTE the index order is different in the numpy array returned
	      # by DenseTensor::GetDataArray
              x[1 2,3] = x.GetDataArray()[3,1,2]
	      
	 Note that internally MFEM densmatrix is column major, while numpy
	 is row major. Therefore,
            v = Vector([1,2,3,4,5,6,7,8,9])
	    m = DenseMatrix(v.GetData(), 3, 3)
         will make
	    [1 4,7]
	    [2,5,8]
	    [3,6,9],
	 whereas
	    numpy.arange(9).reshape(3,3)
         would give you
	    [1 2,3]
	    [4,5,6]
	    [7,8,9].


	  
  4-7) estimators
        ZienkiewiczZhuEstimator and L2ZienkiewZhuEstimator
	have two versions of constructor. flux_fes
	can be eithor passed as pointer or reference. In python class, you
	need to pass 5th argument if you want to pass flux_fes as
	reference. By default,  own_flux_fes is  False, so a user
	can skip passing this keyword when passing by reference
	
	pass by reference 
          ZienkiewiczZhuEstimator(integ, sol, flux_fes, own_flux_fes = False)	  
        pass by pointer
          ZienkiewiczZhuEstimator(integ, sol, flux_fes, own_flux_fes = True)
	pass by reference 
          L2ZienkiewiczZhuEstimator(integ, sol, flux_fes, own_flux_fes = False)	  
        pass by pointer
          L2ZienkiewiczZhuEstimator(integ, sol, flux_fes, own_flux_fes = True)	  
	  

  4-8) operator
        PyTimeDependentOperatorBase and PyOperatorBase is added to
	allow for implementing those operator in python. A user
	can inherit these class and overwrite Mult in python. See
	ex9.py for example.
	
  4-9) ode
        Step uses reference passing for t and dt. To get the result
	from this method, use
	   t, dt = ode_solver.Step(u, t, dt)

  4-10) hypre
        Following methods are added trhough SWIG interface
        HypreParVector::GetPartitioningArray  returns Partitioning as numpy array
        HypreParMatrix::GetColPartArray  returns ColPart as numpy array
        HypreParMatrix::GetRowPartArray  returns RowPart as numpy array	
        HypreParMatrix::GetCooDataArray  returns data to construct local coo matrix

        mfem.common.chypre
        This module defines CHypreVec and CHhypreMat classes. Those are classes
	supports...
	    1) create HypreParCSR and HypreParVector from scipy.sparse.csr_matrix
	    and numpy.ndarray, respectively
	    2) convert HypreMatrix/Vector to numpy array or scipy.sparse matrix
	    3) handle complex number using a pair of Hypre object (real and imag)
        Class methods of these classes are named in a similar way to numpy array.

   4-11) socketstream

         socketstream in PyMFEM has send_text and send_solution. These
	 method also send endl and flush. One can use them as follows
	 
         (example)
           sock = mfem.socketstream("localhost", 19916)
           sock.precision(8)
           sock.send_text("parallel " + str(num_procs) +  " " + str(myid))    
           sock.send_solution(pmesh, x)
	 
         socketstream object support << operator partially, but not works
	 perfectly. This was done by adding __lshift__ and other methos to
	 socketstream class in .i file. We don't want to wrap the whole
	 iostream. Instead, we are adding methods used in examples such as
	 precision. Also, note that sock << flush in c++ should be rephrased
	 sock.flush()
	   
   4-12) std::ostream&

         methods which takes std::ostream& are wrapped so that one can
	 pass python file object. Note that you may want to flush
	 sys.stdout first.

         (example)
	    sys.stdout.flush()
	    sparsmat.PrintInfo(sys.stdout)
       
   4-13) mfem::table
           GetRowList returns a version of GetRow which returns Python
	   list instead of int pointer.

   4-14) mfem::ParMesh
   
         ParMesh(comm, filename)
            load parallel file
	 
         ParPrintToFile(filename)
            write parallel file
	    
   4-14) mfem::ParGridFunction

         ParGridFunction(pmesh, filename) : read parallel GridFunction

         example to read ParMesh and ParGridFunction:
   	    smyid = '{:0>6d}'.format(myid)  # myid = MPI.COMM_WORLD.rank
            mesh1 = mfem.ParMesh(comm, 'wg.mesh.'+smyid)
            mesh1.ReorientTetMesh()
            x = mfem.ParGridFunction(mesh1, 'wg.gf.'+smyid)
	    
-- Known issues --

  PyMFEM is an on-going effort. Not all MFEM functioality is propary
  wrapped yet. Presnetly all serial/parallel examples avaiable in
  the top-level mfem/example directory are rewritten in python.
  This would be sufficient for certain projects, but for other projects,
  you may find various features are missing. Please inform us if you
  notice something missing.

  1) t*.hpp files are not wrapped
  
  2) Mesh::SwapNodes is not working perfectly.
  
  3) Memory management.
      python uses garbage collection. Since wrapper does not know
      exactly how objects are used in c++ layer, MFEM object may be
      collected before being ready to be freed. We are addressing
      this by adjusting thisown flag or %newobject directive. This
      needs to be done method by method, and not yet completed.

  4) Visualization through visit is not included

  5) SuperLU and Petsc are not supported.
  
*This work is supported by US DoE contract DE-FC02-99ER54512

About

Python wrapper for MFEM (generated from mfem commit 324d66b0c7fc75225e830dcfb02647722bae1fe0)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C++ 95.1%
  • Python 4.9%