1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdbool.h>
4 #include <string.h>
5 
6 // This would normally be in a header file, but as we do not need one,
7 // we will explicitly include it here.
8 #if defined(_WIN32) || defined(_WIN64)
9 #  if defined(BUILDING_PYNUMERO_MA27)
10 #    define PYNUMERO_HSL_EXPORT __declspec(dllexport)
11 #  else
12 #    define PYNUMERO_HSL_EXPORT __declspec(dllimport)
13 #  endif
14 #else
15 #  define PYNUMERO_HSL_EXPORT
16 #endif
17 
18 // Forward declaration of MA27 fortran routines
19 extern "C" {
20    void ma27id_(int* ICNTL, double* CNTL);
21    void ma27ad_(int *N, int *NZ, int *IRN, int* ICN,
22                 int *IW, int* LIW, int* IKEEP, int *IW1,
23                 int* NSTEPS, int* IFLAG, int* ICNTL,
24                 double* CNTL, int *INFO, double* OPS);
25    void ma27bd_(int *N, int *NZ, int *IRN, int* ICN,
26                 double* A, int* LA, int* IW, int* LIW,
27                 int* IKEEP, int* NSTEPS, int* MAXFRT,
28                 int* IW1, int* ICNTL, double* CNTL,
29                 int* INFO);
30    void ma27cd_(int *N, double* A, int* LA, int* IW,
31                 int* LIW, double* W, int* MAXFRT,
32                 double* RHS, int* IW1, int* NSTEPS,
33                 int* ICNTL, int* INFO);
34 } // extern "C"
35 
abort_bad_memory(int status)36 void abort_bad_memory(int status) {
37    printf("Bad memory allocation in MA27 C interface. Aborting.");
38    exit(status);
39 }
40 
41 
42 struct MA27_struct {
43    // Constructor: set defaults, initialize cached arrays to NULL
MA27_structMA27_struct44    MA27_struct():
45       LA(0),
46       LIW_a(0),
47       LIW_b(0),
48       NSTEPS(0),
49       IFLAG(0),
50       MAXFRT(0),
51       IW_factor(1.2),
52       A_factor(2.0),
53       OPS(0),
54       IW_a(NULL),
55       IW_b(NULL),
56       IKEEP(NULL),
57       A(NULL)
58    {
59       ma27id_(this->ICNTL, this->CNTL);
60    }
61    // Destructor: delete all cached arrays
~MA27_structMA27_struct62    virtual ~MA27_struct() {
63       if ( this->A ) {
64          delete[] this->A;
65       }
66       if ( this->IW_a ) {
67          delete[] this->IW_a;
68       }
69       if ( this->IW_b ) {
70          delete[] this->IW_b;
71       }
72       if ( this->IKEEP ) {
73          delete[] this->IKEEP;
74       }
75    }
76 
77    int LA, LIW_a, LIW_b, NSTEPS, IFLAG, MAXFRT;
78    double IW_factor, A_factor, OPS;
79    int* IW_a;
80    int* IW_b;
81    // Use different arrays for IW that is sent to MA27A and that sent to
82    // MA27B because IW must be discarded after MA27A but kept after MA27B.
83    // If these arrays are the same, and a symbolic factorization is performed
84    // after a numeric factorization (e.g. on a new matrix), user-defined
85    // and MA27B-defined allocations of IW can be conflated.
86    int* IKEEP;
87    double* A;
88    int ICNTL[30], INFO[20];
89    double CNTL[5];
90 };
91 
92 extern "C" {
93 
94    PYNUMERO_HSL_EXPORT
new_MA27_struct(void)95    MA27_struct* new_MA27_struct(void) {
96       MA27_struct* ma27 = new MA27_struct;
97       if (ma27 == NULL) { abort_bad_memory(1); }
98       // Return pointer to ma27 that Python program can pass to other
99       // functions in this code
100       return ma27;
101    }
102 
103 
104    PYNUMERO_HSL_EXPORT
free_MA27_struct(MA27_struct * ma27)105    void free_MA27_struct(MA27_struct* ma27) {
106       delete ma27;
107    }
108 
109    // Functions for setting/accessing INFO/CNTL arrays:
110    PYNUMERO_HSL_EXPORT
set_icntl(MA27_struct * ma27,int i,int val)111    void set_icntl(MA27_struct* ma27, int i, int val) {
112       ma27->ICNTL[i] = val;
113    }
114 
115    PYNUMERO_HSL_EXPORT
get_icntl(MA27_struct * ma27,int i)116    int get_icntl(MA27_struct* ma27, int i) {
117       return ma27->ICNTL[i];
118    }
119 
120    PYNUMERO_HSL_EXPORT
set_cntl(MA27_struct * ma27,int i,double val)121    void set_cntl(MA27_struct* ma27, int i, double val) {
122       ma27->CNTL[i] = val;
123    }
124 
125    PYNUMERO_HSL_EXPORT
get_cntl(MA27_struct * ma27,int i)126    double get_cntl(MA27_struct* ma27, int i) {
127       return ma27->CNTL[i];
128    }
129 
130    PYNUMERO_HSL_EXPORT
get_info(MA27_struct * ma27,int i)131    int get_info(MA27_struct* ma27, int i) {
132       return ma27->INFO[i];
133    }
134 
135    // Functions for allocating WORK/FACT arrays:
136    PYNUMERO_HSL_EXPORT
alloc_iw_a(MA27_struct * ma27,int l)137    void alloc_iw_a(MA27_struct* ma27, int l) {
138       if ( ma27->IW_a ) {
139          delete[] ma27->IW_a;
140       }
141       ma27->LIW_a = l;
142       ma27->IW_a = new int[l];
143       if (ma27->IW_a == NULL) { abort_bad_memory(1); }
144    }
145 
146    PYNUMERO_HSL_EXPORT
alloc_iw_b(MA27_struct * ma27,int l)147    void alloc_iw_b(MA27_struct* ma27, int l) {
148       if ( ma27->IW_b ) {
149          delete[] ma27->IW_b;
150       }
151       ma27->LIW_b = l;
152       ma27->IW_b = new int[l];
153       if (ma27->IW_b == NULL) { abort_bad_memory(1); }
154    }
155 
156    PYNUMERO_HSL_EXPORT
alloc_a(MA27_struct * ma27,int l)157    void alloc_a(MA27_struct* ma27, int l) {
158       if ( ma27->A ) {
159          delete[] ma27->A;
160       }
161       ma27->LA = l;
162       ma27->A = new double[l];
163       if (ma27->A == NULL) { abort_bad_memory(1); }
164    }
165 
166    PYNUMERO_HSL_EXPORT
do_symbolic_factorization(MA27_struct * ma27,int N,int NZ,int * IRN,int * ICN)167    void do_symbolic_factorization(MA27_struct* ma27, int N, int NZ,
168                                   int* IRN, int* ICN) {
169       // Arrays, presumably supplied from Python, are assumed to have base-
170       // zero indices. Convert to base-one before sending to Fortran.
171       for (int i=0; i<NZ; i++) {
172          IRN[i] = IRN[i] + 1;
173          ICN[i] = ICN[i] + 1;
174       }
175 
176       if ( ! ma27->IW_a ) {
177          int min_size = 2*NZ + 3*N + 1;
178          int size = (int)(ma27->IW_factor*min_size);
179          alloc_iw_a(ma27, size);
180       }
181 
182       if ( ma27->IKEEP ) {
183          delete[] ma27->IKEEP;
184       }
185       ma27->IKEEP = new int[3*N];
186       if (ma27->IKEEP == NULL) { abort_bad_memory(1); }
187       int* IW1 = new int[2*N];
188       if (IW1 == NULL) { abort_bad_memory(1); }
189 
190       ma27ad_(&N,
191               &NZ,
192               IRN,
193               ICN,
194               ma27->IW_a,
195               &(ma27->LIW_a),
196               ma27->IKEEP,
197               IW1,
198               &(ma27->NSTEPS),
199               &(ma27->IFLAG),
200               ma27->ICNTL,
201               ma27->CNTL,
202               ma27->INFO,
203               &(ma27->OPS));
204 
205       delete[] IW1;
206       delete[] ma27->IW_a;
207       ma27->IW_a = NULL;
208    }
209 
210    PYNUMERO_HSL_EXPORT
do_numeric_factorization(MA27_struct * ma27,int N,int NZ,int * IRN,int * ICN,double * A)211    void do_numeric_factorization(MA27_struct* ma27, int N, int NZ,
212                                  int* IRN, int* ICN, double* A) {
213 
214       // Convert indices to base-one for Fortran
215       for (int i=0; i<NZ; i++) {
216          IRN[i] = IRN[i] + 1;
217          ICN[i] = ICN[i] + 1;
218       }
219 
220       // Get memory estimates from INFO, allocate A and IW
221       if ( ! ma27->A ) {
222          int info5 = ma27->INFO[5-1];
223          int size = (int)(ma27->A_factor*info5);
224          alloc_a(ma27, size);
225          // A is now allocated
226       }
227       // Regardless of ma27->A's previous allocation status, copy values from A.
228       memcpy(ma27->A, A, NZ*sizeof(double));
229 
230       if ( ! ma27->IW_b ) {
231          int info6 = ma27->INFO[6-1];
232          int size = (int)(ma27->IW_factor*info6);
233          alloc_iw_b(ma27, size);
234       }
235 
236       int* IW1 = new int[N];
237       if (IW1 == NULL) { abort_bad_memory(1); }
238 
239       ma27bd_(&N,
240               &NZ,
241               IRN,
242               ICN,
243               ma27->A,
244               &(ma27->LA),
245               ma27->IW_b,
246               &(ma27->LIW_b),
247               ma27->IKEEP,
248               &(ma27->NSTEPS),
249               &(ma27->MAXFRT),
250               IW1,
251               ma27->ICNTL,
252               ma27->CNTL,
253               ma27->INFO);
254 
255       delete[] IW1;
256    }
257 
258    PYNUMERO_HSL_EXPORT
do_backsolve(MA27_struct * ma27,int N,double * RHS)259    void do_backsolve(MA27_struct* ma27, int N, double* RHS) {
260 
261       double* W = new double[ma27->MAXFRT];
262       if (W == NULL) { abort_bad_memory(1); }
263       int* IW1 = new int[ma27->NSTEPS];
264       if (IW1 == NULL) { abort_bad_memory(1); }
265 
266       ma27cd_(
267               &N,
268               ma27->A,
269               &(ma27->LA),
270               ma27->IW_b,
271               &(ma27->LIW_b),
272               W,
273               &(ma27->MAXFRT),
274               RHS,
275               IW1,
276               &(ma27->NSTEPS),
277               ma27->ICNTL,
278               ma27->INFO
279               );
280 
281       delete[] IW1;
282       delete[] W;
283    }
284 
285 } // extern "C"
286