├── .gitattributes ├── .gitignore ├── COPYING ├── MANIFEST.in ├── README.md ├── examples └── dsimpletest.py ├── mumps ├── __init__.py └── _dmumps.pyx ├── pyproject.toml └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | mumps/_dmumps.c binary 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | build 3 | dist 4 | *.py[oc] 5 | *~ 6 | __pycache__ 7 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Bradley Froehle 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the copyright holder nor the names of its 12 | contributors may be used to endorse or promote products derived from this 13 | software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md COPYING 2 | recursive-include mumps *.pyx 3 | recursive-include examples *.py 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PyMUMPS: A parallel sparse direct solver 2 | ======================================== 3 | 4 | 5 | Requirements 6 | ------------ 7 | 8 | * [MUMPS](http://graal.ens-lyon.fr/MUMPS/) 9 | * [mpi4py](https://code.google.com/p/mpi4py/) 10 | 11 | 12 | Installation 13 | ------------ 14 | 15 | PyMUMPS can be installed from PyPI using pip: 16 | 17 | ``` 18 | pip install pymumps 19 | ``` 20 | 21 | Custom build flags, e.g. to specify the MUMPS installation location, 22 | can be specified using `--global-option`: 23 | 24 | ``` 25 | pip install pymumps --global-option="build_ext" \ 26 | --global-option="-I$MUMPS_PREFIX/include" \ 27 | --global-option="-L$MUMPS_PREFIX/lib" \ 28 | ``` 29 | 30 | Use `python setup.py build_ext --help` to get a list of all allowed 31 | options. 32 | 33 | There is also conda recipe: 34 | 35 | ``` 36 | conda install -c conda-forge pymumps 37 | ``` 38 | 39 | 40 | Examples 41 | -------- 42 | 43 | Centralized input & output. The sparse matrix and right hand side are 44 | input only on the rank 0 process. The system is solved using all 45 | available processes and the result is available on the rank 0 process. 46 | 47 | ```python 48 | from mumps import DMumpsContext 49 | ctx = DMumpsContext() 50 | if ctx.myid == 0: 51 | ctx.set_centralized_sparse(A) 52 | x = b.copy() 53 | ctx.set_rhs(x) # Modified in place 54 | ctx.run(job=6) # Analysis + Factorization + Solve 55 | ctx.destroy() # Cleanup 56 | ``` 57 | 58 | Re-use symbolic or numeric factorizations. 59 | 60 | ```python 61 | from mumps import DMumpsContext 62 | ctx = DMumpsContext() 63 | if ctx.myid == 0: 64 | ctx.set_centralized_assembled_rows_cols(A.row+1, A.col+1) # 1-based 65 | ctx.run(job=1) # Analysis 66 | 67 | if ctx.myid == 0: 68 | ctx.set_centralized_assembled_values(A.data) 69 | ctx.run(job=2) # Factorization 70 | 71 | if ctx.myid == 0: 72 | x = b1.copy() 73 | ctx.set_rhs(x) 74 | ctx.run(job=3) # Solve 75 | 76 | # Reuse factorizations by running `job=3` with new right hand sides 77 | # or analyses by supplying new values and running `job=2` to repeat 78 | # the factorization process. 79 | ``` 80 | -------------------------------------------------------------------------------- /examples/dsimpletest.py: -------------------------------------------------------------------------------- 1 | """ 2 | DMUMPS test routine. 3 | 4 | Run as: 5 | 6 | mpirun -np 2 python dsimpletest.py 7 | 8 | The solution should be [ 1. 2. 3. 4. 5.]. 9 | """ 10 | 11 | import numpy as np 12 | import mumps 13 | 14 | # Set up the test problem: 15 | n = 5 16 | irn = np.array([1,2,4,5,2,1,5,3,2,3,1,3], dtype='i') 17 | jcn = np.array([2,3,3,5,1,1,2,4,5,2,3,3], dtype='i') 18 | a = np.array([3.0,-3.0,2.0,1.0,3.0,2.0,4.0,2.0,6.0,-1.0,4.0,1.0], dtype='d') 19 | 20 | b = np.array([20.0,24.0,9.0,6.0,13.0], dtype='d') 21 | 22 | # Create the MUMPS context and set the array and right hand side 23 | ctx = mumps.DMumpsContext(sym=0, par=1) 24 | if ctx.myid == 0: 25 | ctx.set_shape(5) 26 | ctx.set_centralized_assembled(irn, jcn, a) 27 | x = b.copy() 28 | ctx.set_rhs(x) 29 | 30 | ctx.set_silent() # Turn off verbose output 31 | 32 | ctx.run(job=6) # Analysis + Factorization + Solve 33 | 34 | if ctx.myid == 0: 35 | print("Solution is %s." % (x,)) 36 | 37 | ctx.destroy() # Free memory 38 | -------------------------------------------------------------------------------- /mumps/__init__.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import mumps._dmumps 3 | 4 | __all__ = [ 5 | 'DMumpsContext', 6 | 'spsolve', 7 | ] 8 | 9 | ######################################################################## 10 | # Classes 11 | ######################################################################## 12 | 13 | # The main class which is shared between the various datatype variants. 14 | class _MumpsBaseContext(object): 15 | """MUMPS Context 16 | 17 | This context acts as a thin wrapper around MUMPS_STRUC_C 18 | which is accessible in the `id` attribute. 19 | 20 | Usage 21 | ----- 22 | 23 | Basic usage generally involves setting up the context, adding 24 | the sparse matrix and right hand side in process 0, and using 25 | `run` to execute the various MUMPS phases. 26 | 27 | ctx = MumpsContext() 28 | if rank == 0: 29 | ctx.set_centralized_sparse(A) 30 | x = b.copy() # MUMPS modifies rhs in place, so make copy 31 | ctx.set_rhs(x) 32 | ctx.run(6) # Symbolic + Numeric + Solve 33 | ctx.destroy() # Free internal data structures 34 | 35 | assert abs(A.dot(x) - b).max() < 1e-10 36 | """ 37 | 38 | def __init__(self, par=1, sym=0, comm=None): 39 | """Create a MUMPS solver context. 40 | 41 | Parameters 42 | ---------- 43 | par : int 44 | 1 if rank 0 participates 45 | 0 if rank 0 does not participate 46 | sym : int 47 | 0 if unsymmetric 48 | comm : MPI Communicator or None 49 | If None, use MPI_COMM_WORLD 50 | """ 51 | if comm is None: 52 | from mpi4py import MPI 53 | comm = MPI.COMM_WORLD 54 | self.comm = comm 55 | 56 | self.id = self._MUMPS_STRUC_C() 57 | self.id.par = par 58 | self.id.sym = sym 59 | self.id.comm_fortran = comm.py2f() 60 | self.run(job = -1) # JOB_INIT 61 | self.myid = comm.rank 62 | self._refs = {} # References to matrices 63 | 64 | def __enter__(self): 65 | return self 66 | 67 | def __exit__(self, *exc_info): 68 | self.destroy() 69 | 70 | def set_shape(self, n): 71 | """Set the matrix shape.""" 72 | self.id.n = n 73 | 74 | def set_centralized_sparse(self, A): 75 | """Set assembled matrix on processor 0. 76 | 77 | Parameters 78 | ---------- 79 | A : `scipy.sparse.coo_matrix` 80 | Sparse matrices of other formats will be converted to 81 | COOrdinate form. 82 | """ 83 | if self.myid != 0: 84 | return 85 | 86 | A = A.tocoo() 87 | n = A.shape[0] 88 | assert A.shape == (n, n), "Expected a square matrix." 89 | self.set_shape(n) 90 | self.set_centralized_assembled(A.row+1, A.col+1, A.data) 91 | 92 | 93 | #################################################################### 94 | # Centralized (the rank 0 process supplies the entire matrix) 95 | #################################################################### 96 | 97 | def set_centralized_assembled(self, irn, jcn, a): 98 | """Set assembled matrix on processor 0. 99 | 100 | The row and column indices (irn & jcn) should be one based. 101 | """ 102 | self.set_centralized_assembled_rows_cols(irn, jcn) 103 | self.set_centralized_assembled_values(a) 104 | 105 | def set_centralized_assembled_rows_cols(self, irn, jcn): 106 | """Set assembled matrix indices on processor 0. 107 | 108 | The row and column indices (irn & jcn) should be one based. 109 | """ 110 | if self.myid != 0: 111 | return 112 | assert irn.size == jcn.size 113 | self._refs.update(irn=irn, jcn=jcn) 114 | self.id.nz = irn.size 115 | self.id.irn = self.cast_array(irn) 116 | self.id.jcn = self.cast_array(jcn) 117 | 118 | def set_centralized_assembled_values(self, a): 119 | """Set assembled matrix values on processor 0.""" 120 | if self.myid != 0: 121 | return 122 | assert a.size == self.id.nz 123 | self._refs.update(a=a) 124 | self.id.a = self.cast_array(a) 125 | 126 | 127 | #################################################################### 128 | # Distributed (each process enters some portion of the matrix) 129 | #################################################################### 130 | 131 | def set_distributed_assembled(self, irn_loc, jcn_loc, a_loc): 132 | """Set the distributed assembled matrix. 133 | 134 | Distributed assembled matrices require setting icntl(18) != 0. 135 | """ 136 | self.set_distributed_assembled_rows_cols(irn_loc, jcn_loc) 137 | self.set_distributed_assembled_values(a_loc) 138 | 139 | def set_distributed_assembled_rows_cols(self, irn_loc, jcn_loc): 140 | """Set the distributed assembled matrix row & column numbers. 141 | 142 | Distributed assembled matrices require setting icntl(18) != 0. 143 | """ 144 | assert irn_loc.size == jcn_loc.size 145 | 146 | self._refs.update(irn_loc=irn_loc, jcn_loc=jcn_loc) 147 | self.id.nz_loc = irn_loc.size 148 | self.id.irn_loc = self.cast_array(irn_loc) 149 | self.id.jcn_loc = self.cast_array(jcn_loc) 150 | 151 | def set_distributed_assembled_values(self, a_loc): 152 | """Set the distributed assembled matrix values. 153 | 154 | Distributed assembled matrices require setting icntl(18) != 0. 155 | """ 156 | assert a_loc.size == self._refs['irn_loc'].size 157 | self._refs.update(a_loc=a_loc) 158 | self.id.a_loc = self.cast_array(a_loc) 159 | 160 | 161 | #################################################################### 162 | # Right hand side entry 163 | #################################################################### 164 | 165 | def set_rhs(self, rhs): 166 | """Set the right hand side. This matrix will be modified in place.""" 167 | assert rhs.size == self.id.n 168 | self._refs.update(rhs=rhs) 169 | self.id.rhs = self.cast_array(rhs) 170 | 171 | def set_icntl(self, idx, val): 172 | """Set the icntl value. 173 | 174 | The index should be provided as a 1-based number. 175 | """ 176 | self.id.icntl[idx-1] = val 177 | 178 | def set_job(self, job): 179 | """Set the job.""" 180 | self.id.job = job 181 | 182 | def set_silent(self): 183 | """Silence most messages.""" 184 | self.set_icntl(1, -1) # output stream for error msgs 185 | self.set_icntl(2, -1) # otuput stream for diagnostic msgs 186 | self.set_icntl(3, -1) # output stream for global info 187 | self.set_icntl(4, 0) # level of printing for errors 188 | 189 | @property 190 | def destroyed(self): 191 | return self.id is None 192 | 193 | def destroy(self): 194 | """Delete the MUMPS context and release all array references.""" 195 | if self.id is not None and self._mumps_c is not None: 196 | self.id.job = -2 # JOB_END 197 | self._mumps_c(self.id) 198 | self.id = None 199 | self._refs = None 200 | 201 | def __del__(self): 202 | if not self.destroyed: 203 | warnings.warn("undestroyed %s" % self.__class__.__name__, 204 | RuntimeWarning) 205 | self.destroy() 206 | 207 | def mumps(self): 208 | """Call MUMPS, checking for errors in the return code. 209 | 210 | The desired job should have already been set using `ctx.set_job(...)`. 211 | As a convenience, you may wish to call `ctx.run(job=...)` which sets 212 | the job and calls MUMPS. 213 | """ 214 | self._mumps_c(self.id) 215 | if self.id.infog[0] < 0: 216 | raise RuntimeError("MUMPS error: %d" % self.id.infog[0]) 217 | 218 | def run(self, job): 219 | """Set the job and run MUMPS. 220 | 221 | Valid Jobs 222 | ---------- 223 | 1 : Analysis 224 | 2 : Factorization 225 | 3 : Solve 226 | 4 : Analysis + Factorization 227 | 5 : Factorization + Solve 228 | 6 : Analysis + Factorization + Solve 229 | """ 230 | self.set_job(job) 231 | self.mumps() 232 | 233 | class DMumpsContext(_MumpsBaseContext): 234 | 235 | cast_array = staticmethod(_dmumps.cast_array) 236 | _mumps_c = staticmethod(_dmumps.dmumps_c) 237 | _MUMPS_STRUC_C = staticmethod(_dmumps.DMUMPS_STRUC_C) 238 | 239 | 240 | ######################################################################## 241 | # Functions 242 | ######################################################################## 243 | 244 | def spsolve(A, b, comm=None): 245 | """Sparse solve A\b.""" 246 | 247 | assert A.dtype == 'd' and b.dtype == 'd', "Only double precision supported." 248 | with DMumpsContext(par=1, sym=0, comm=comm) as ctx: 249 | if ctx.myid == 0: 250 | # Set the sparse matrix -- only necessary on 251 | ctx.set_centralized_sparse(A.tocoo()) 252 | x = b.copy() 253 | ctx.set_rhs(x) 254 | 255 | # Silence most messages 256 | ctx.set_silent() 257 | 258 | # Analysis + Factorization + Solve 259 | ctx.run(job=6) 260 | 261 | if ctx.myid == 0: 262 | return x 263 | -------------------------------------------------------------------------------- /mumps/_dmumps.pyx: -------------------------------------------------------------------------------- 1 | __all__ = ['DMUMPS_STRUC_C', 'dmumps_c', 'cast_array'] 2 | 3 | ######################################################################## 4 | # libdmumps / dmumps_c.h wrappers (using Cython) 5 | ######################################################################## 6 | 7 | MUMPS_INT_DTYPE = 'i' 8 | DMUMPS_REAL_DTYPE = 'd' 9 | DMUMPS_COMPLEX_DTYPE = 'd' 10 | 11 | from libc.string cimport strncpy 12 | 13 | cdef extern from "dmumps_c.h": 14 | 15 | ctypedef int MUMPS_INT 16 | ctypedef double DMUMPS_COMPLEX 17 | ctypedef double DMUMPS_REAL 18 | 19 | char* MUMPS_VERSION 20 | 21 | ctypedef struct c_DMUMPS_STRUC_C "DMUMPS_STRUC_C": 22 | MUMPS_INT sym, par, job 23 | MUMPS_INT comm_fortran # Fortran communicator 24 | MUMPS_INT icntl[40] 25 | DMUMPS_REAL cntl[15] 26 | MUMPS_INT n 27 | 28 | # used in matlab interface to decide if we 29 | # free + malloc when we have large variation 30 | MUMPS_INT nz_alloc 31 | 32 | # Assembled entry 33 | MUMPS_INT nz 34 | MUMPS_INT *irn 35 | MUMPS_INT *jcn 36 | DMUMPS_COMPLEX *a 37 | 38 | # Distributed entry 39 | MUMPS_INT nz_loc 40 | MUMPS_INT *irn_loc 41 | MUMPS_INT *jcn_loc 42 | DMUMPS_COMPLEX *a_loc 43 | 44 | # Element entry 45 | MUMPS_INT nelt 46 | MUMPS_INT *eltptr 47 | MUMPS_INT *eltvar 48 | DMUMPS_COMPLEX *a_elt 49 | 50 | # Ordering, if given by user 51 | MUMPS_INT *perm_in 52 | 53 | # Orderings returned to user 54 | MUMPS_INT *sym_perm # symmetric permutation 55 | MUMPS_INT *uns_perm # column permutation 56 | 57 | # Scaling (input only in this version) 58 | DMUMPS_REAL *colsca 59 | DMUMPS_REAL *rowsca 60 | 61 | # RHS, solution, ouptput data and statistics 62 | DMUMPS_COMPLEX *rhs, *redrhs, *rhs_sparse, *sol_loc 63 | MUMPS_INT *irhs_sparse, *irhs_ptr, *isol_loc 64 | MUMPS_INT nrhs, lrhs, lredrhs, nz_rhs, lsol_loc 65 | MUMPS_INT schur_mloc, schur_nloc, schur_lld 66 | MUMPS_INT mblock, nblock, nprow, npcol 67 | MUMPS_INT info[40],infog[40] 68 | DMUMPS_REAL rinfo[20], rinfog[20] 69 | 70 | # Null space 71 | MUMPS_INT deficiency 72 | MUMPS_INT *pivnul_list 73 | MUMPS_INT *mapping 74 | 75 | # Schur 76 | MUMPS_INT size_schur 77 | MUMPS_INT *listvar_schur 78 | DMUMPS_COMPLEX *schur 79 | 80 | # Internal parameters 81 | MUMPS_INT instance_number 82 | DMUMPS_COMPLEX *wk_user 83 | 84 | char *version_number 85 | # For out-of-core 86 | char *ooc_tmpdir 87 | char *ooc_prefix 88 | # To save the matrix in matrix market format 89 | char *write_problem 90 | MUMPS_INT lwk_user 91 | void c_dmumps_c "dmumps_c" (c_DMUMPS_STRUC_C *) nogil 92 | 93 | cdef class DMUMPS_STRUC_C: 94 | cdef c_DMUMPS_STRUC_C ob 95 | 96 | property sym: 97 | def __get__(self): return self.ob.sym 98 | def __set__(self, value): self.ob.sym = value 99 | property par: 100 | def __get__(self): return self.ob.par 101 | def __set__(self, value): self.ob.par = value 102 | property job: 103 | def __get__(self): return self.ob.job 104 | def __set__(self, value): self.ob.job = value 105 | 106 | property comm_fortran: 107 | def __get__(self): return self.ob.comm_fortran 108 | def __set__(self, value): self.ob.comm_fortran = value 109 | 110 | property icntl: 111 | def __get__(self): 112 | cdef MUMPS_INT[:] view = self.ob.icntl 113 | return view 114 | property cntl: 115 | def __get__(self): 116 | cdef DMUMPS_REAL[:] view = self.ob.cntl 117 | return view 118 | 119 | property n: 120 | def __get__(self): return self.ob.n 121 | def __set__(self, value): self.ob.n = value 122 | property nz_alloc: 123 | def __get__(self): return self.ob.nz_alloc 124 | def __set__(self, value): self.ob.nz_alloc = value 125 | 126 | property nz: 127 | def __get__(self): return self.ob.nz 128 | def __set__(self, value): self.ob.nz = value 129 | property irn: 130 | def __get__(self): return self.ob.irn 131 | def __set__(self, long value): self.ob.irn = value 132 | property jcn: 133 | def __get__(self): return self.ob.jcn 134 | def __set__(self, long value): self.ob.jcn = value 135 | property a: 136 | def __get__(self): return self.ob.a 137 | def __set__(self, long value): self.ob.a = value 138 | 139 | property nz_loc: 140 | def __get__(self): return self.ob.nz_loc 141 | def __set__(self, value): self.ob.nz_loc = value 142 | property irn_loc: 143 | def __get__(self): return self.ob.irn_loc 144 | def __set__(self, long value): self.ob.irn_loc = value 145 | property jcn_loc: 146 | def __get__(self): return self.ob.jcn_loc 147 | def __set__(self, long value): self.ob.jcn_loc = value 148 | property a_loc: 149 | def __get__(self): return self.ob.a_loc 150 | def __set__(self, long value): self.ob.a_loc = value 151 | 152 | property nelt: 153 | def __get__(self): return self.ob.nelt 154 | def __set__(self, value): self.ob.nelt = value 155 | property eltptr: 156 | def __get__(self): return self.ob.eltptr 157 | def __set__(self, long value): self.ob.eltptr = value 158 | property eltvar: 159 | def __get__(self): return self.ob.eltvar 160 | def __set__(self, long value): self.ob.eltvar = value 161 | property a_elt: 162 | def __get__(self): return self.ob.a_elt 163 | def __set__(self, long value): self.ob.a_elt = value 164 | 165 | property perm_in: 166 | def __get__(self): return self.ob.perm_in 167 | def __set__(self, long value): self.ob.perm_in = value 168 | 169 | property sym_perm: 170 | def __get__(self): return self.ob.sym_perm 171 | def __set__(self, long value): self.ob.sym_perm = value 172 | property uns_perm: 173 | def __get__(self): return self.ob.uns_perm 174 | def __set__(self, long value): self.ob.uns_perm = value 175 | 176 | property colsca: 177 | def __get__(self): return self.ob.colsca 178 | def __set__(self, long value): self.ob.colsca = value 179 | property rowsca: 180 | def __get__(self): return self.ob.rowsca 181 | def __set__(self, long value): self.ob.rowsca = value 182 | 183 | property rhs: 184 | def __get__(self): return self.ob.rhs 185 | def __set__(self, long value): self.ob.rhs = value 186 | property redrhs: 187 | def __get__(self): return self.ob.redrhs 188 | def __set__(self, long value): self.ob.redrhs = value 189 | property rhs_sparse: 190 | def __get__(self): return self.ob.rhs_sparse 191 | def __set__(self, long value): self.ob.rhs_sparse = value 192 | property sol_loc: 193 | def __get__(self): return self.ob.sol_loc 194 | def __set__(self, long value): self.ob.sol_loc = value 195 | 196 | 197 | property irhs_sparse: 198 | def __get__(self): return self.ob.irhs_sparse 199 | def __set__(self, long value): self.ob.irhs_sparse = value 200 | property irhs_ptr: 201 | def __get__(self): return self.ob.irhs_ptr 202 | def __set__(self, long value): self.ob.irhs_ptr = value 203 | property isol_loc: 204 | def __get__(self): return self.ob.isol_loc 205 | def __set__(self, long value): self.ob.isol_loc = value 206 | 207 | property nrhs: 208 | def __get__(self): return self.ob.nrhs 209 | def __set__(self, value): self.ob.nrhs = value 210 | property lrhs: 211 | def __get__(self): return self.ob.lrhs 212 | def __set__(self, value): self.ob.lrhs = value 213 | property lredrhs: 214 | def __get__(self): return self.ob.lredrhs 215 | def __set__(self, value): self.ob.lredrhs = value 216 | property nz_rhs: 217 | def __get__(self): return self.ob.nz_rhs 218 | def __set__(self, value): self.ob.nz_rhs = value 219 | property lsol_loc: 220 | def __get__(self): return self.ob.lsol_loc 221 | def __set__(self, value): self.ob.lsol_loc = value 222 | 223 | property schur_mloc: 224 | def __get__(self): return self.ob.schur_mloc 225 | def __set__(self, value): self.ob.schur_mloc = value 226 | property schur_nloc: 227 | def __get__(self): return self.ob.schur_nloc 228 | def __set__(self, value): self.ob.schur_nloc = value 229 | property schur_lld: 230 | def __get__(self): return self.ob.schur_lld 231 | def __set__(self, value): self.ob.schur_lld = value 232 | 233 | 234 | property mblock: 235 | def __get__(self): return self.ob.mblock 236 | def __set__(self, value): self.ob.mblock = value 237 | property nblock: 238 | def __get__(self): return self.ob.nblock 239 | def __set__(self, value): self.ob.nblock = value 240 | property nprow: 241 | def __get__(self): return self.ob.nprow 242 | def __set__(self, value): self.ob.nprow = value 243 | property npcol: 244 | def __get__(self): return self.ob.npcol 245 | def __set__(self, value): self.ob.npcol = value 246 | 247 | property info: 248 | def __get__(self): 249 | cdef MUMPS_INT[:] view = self.ob.info 250 | return view 251 | property infog: 252 | def __get__(self): 253 | cdef MUMPS_INT[:] view = self.ob.infog 254 | return view 255 | 256 | property rinfo: 257 | def __get__(self): 258 | cdef DMUMPS_REAL[:] view = self.ob.rinfo 259 | return view 260 | property rinfog: 261 | def __get__(self): 262 | cdef DMUMPS_REAL[:] view = self.ob.rinfog 263 | return view 264 | 265 | property deficiency: 266 | def __get__(self): return self.ob.deficiency 267 | def __set__(self, value): self.ob.deficiency = value 268 | property pivnul_list: 269 | def __get__(self): return self.ob.pivnul_list 270 | def __set__(self, long value): self.ob.pivnul_list = value 271 | property mapping: 272 | def __get__(self): return self.ob.mapping 273 | def __set__(self, long value): self.ob.mapping = value 274 | 275 | property size_schur: 276 | def __get__(self): return self.ob.size_schur 277 | def __set__(self, value): self.ob.size_schur = value 278 | property listvar_schur: 279 | def __get__(self): return self.ob.listvar_schur 280 | def __set__(self, long value): self.ob.listvar_schur = value 281 | property schur: 282 | def __get__(self): return self.ob.schur 283 | def __set__(self, long value): self.ob.schur = value 284 | 285 | property instance_number: 286 | def __get__(self): return self.ob.instance_number 287 | def __set__(self, value): self.ob.instance_number = value 288 | property wk_user: 289 | def __get__(self): return self.ob.wk_user 290 | def __set__(self, long value): self.ob.wk_user = value 291 | 292 | property version_number: 293 | def __get__(self): 294 | return ( self.ob.version_number).decode('ascii') 295 | 296 | property ooc_tmpdir: 297 | def __get__(self): 298 | return ( self.ob.ooc_tmpdir).decode('ascii') 299 | def __set__(self, char *value): 300 | strncpy(self.ob.ooc_tmpdir, value, sizeof(self.ob.ooc_tmpdir)) 301 | property ooc_prefix: 302 | def __get__(self): 303 | return ( self.ob.ooc_prefix).decode('ascii') 304 | def __set__(self, char *value): 305 | strncpy(self.ob.ooc_prefix, value, sizeof(self.ob.ooc_prefix)) 306 | 307 | property write_problem: 308 | def __get__(self): 309 | return ( self.ob.write_problem).decode('ascii') 310 | def __set__(self, char *value): 311 | strncpy(self.ob.write_problem, value, sizeof(self.ob.write_problem)) 312 | 313 | property lwk_user: 314 | def __get__(self): return self.ob.lwk_user 315 | def __set__(self, value): self.ob.lwk_user = value 316 | 317 | def dmumps_c(DMUMPS_STRUC_C s not None): 318 | with nogil: 319 | c_dmumps_c(&s.ob) 320 | 321 | __version__ = ( MUMPS_VERSION).decode('ascii') 322 | 323 | ######################################################################## 324 | # Casting routines. 325 | ######################################################################## 326 | 327 | def cast_array(arr): 328 | """Convert numpy array to corresponding cffi pointer. 329 | 330 | The user is entirely responsible for ensuring the data is contiguous 331 | and for holding a reference to the underlying array. 332 | """ 333 | dtype = arr.dtype 334 | if dtype == 'i': 335 | return arr.__array_interface__['data'][0] 336 | elif dtype == 'd': 337 | return arr.__array_interface__['data'][0] 338 | else: 339 | raise ValueError("Unknown dtype %r" % dtype) 340 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, Extension 4 | 5 | try: 6 | import Cython 7 | except ImportError: 8 | raise ImportError(''' 9 | Cython is required for building this package. Please install using 10 | 11 | pip install cython 12 | 13 | or upgrade to a recent PIP release. 14 | ''') 15 | 16 | 17 | with open('README.md') as f: 18 | long_description = f.read() 19 | 20 | 21 | setup( 22 | name='PyMUMPS', 23 | version='0.3.3', 24 | description='Python bindings for MUMPS, a parallel sparse direct solver', 25 | long_description=long_description, 26 | long_description_content_type='text/markdown', 27 | author='Bradley M. Froehle', 28 | author_email='brad.froehle@gmail.com', 29 | maintainer='Stephan Rave', 30 | maintainer_email='stephan.rave@uni-muenster.de', 31 | license='BSD', 32 | url='http://github.com/pymumps/pymumps', 33 | packages=['mumps'], 34 | ext_modules=[ 35 | Extension( 36 | 'mumps._dmumps', 37 | sources=['mumps/_dmumps.pyx'], 38 | libraries=['dmumps', 'mumps_common'], 39 | ), 40 | ], 41 | install_requires=['mpi4py'], 42 | classifiers=[ 43 | 'Development Status :: 3 - Alpha', 44 | 'Intended Audience :: Science/Research', 45 | 'License :: OSI Approved :: BSD License', 46 | 'Programming Language :: Python :: 2', 47 | 'Programming Language :: Python :: 3', 48 | 'Topic :: Scientific/Engineering :: Mathematics', 49 | ], 50 | ) 51 | --------------------------------------------------------------------------------