CAMP 1.0.0
Chemistry Across Multiple Phases
Jacobian.c
Go to the documentation of this file.
1/* Copyright (C) 2021 Barcelona Supercomputing Center and University of
2 * Illinois at Urbana-Champaign
3 * SPDX-License-Identifier: MIT
4 *
5 * Jacobian functions
6 *
7 */
8/** \file
9 * \brief Jacobian functions
10 */
11#include "Jacobian.h"
12#include <math.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include "time_derivative.h"
16
17#define BUFFER_SIZE 10
18#define SMALL_NUMBER 1e-90
19
20int jacobian_initialize_empty(Jacobian *jac, unsigned int num_spec) {
21 jac->num_spec = num_spec;
22 jac->num_elem = 0;
23 jac->elements = (JacobianColumnElements *)malloc(
24 num_spec * sizeof(JacobianColumnElements));
25 if (!jac->elements) {
26 jacobian_free(jac);
27 return 0;
28 }
29 for (unsigned int i_col = 0; i_col < num_spec; ++i_col) {
30 jac->elements[i_col].array_size = BUFFER_SIZE;
31 jac->elements[i_col].number_of_elements = 0;
32 jac->elements[i_col].row_ids =
33 (unsigned int *)malloc(BUFFER_SIZE * sizeof(unsigned int));
34 if (!jac->elements[i_col].row_ids) {
35 jacobian_free(jac);
36 return 0;
37 }
38 }
39 jac->col_ptrs = NULL;
40 jac->row_ids = NULL;
41 jac->production_partials = NULL;
42 jac->loss_partials = NULL;
43 return 1;
44}
45
46int jacobian_initialize(Jacobian *jac, unsigned int num_spec,
47 unsigned int **jac_struct) {
48 unsigned int num_elem = 0;
49 for (unsigned int i_col = 0; i_col < num_spec; ++i_col)
50 for (unsigned int i_row = 0; i_row < num_spec; ++i_row)
51 if (jac_struct[i_col][i_row] == 1) ++num_elem;
52
53 jac->num_spec = num_spec;
54 jac->num_elem = num_elem;
55 jac->col_ptrs = (unsigned int *)malloc((num_spec + 1) * sizeof(unsigned int));
56 if (!jac->col_ptrs) return 0;
57 jac->row_ids = (unsigned int *)malloc(num_elem * sizeof(unsigned int));
58 if (!jac->row_ids) {
59 free(jac->col_ptrs);
60 return 0;
61 }
63 (long double *)malloc(num_elem * sizeof(long double));
64 if (!jac->production_partials) {
65 free(jac->col_ptrs);
66 free(jac->row_ids);
67 return 0;
68 }
69 jac->loss_partials = (long double *)malloc(num_elem * sizeof(long double));
70 if (!jac->loss_partials) {
71 free(jac->col_ptrs);
72 free(jac->row_ids);
73 free(jac->production_partials);
74 return 0;
75 }
76
77 unsigned int i_elem = 0;
78 unsigned int i_col = 0;
79 for (; i_col < num_spec; ++i_col) {
80 jac->col_ptrs[i_col] = i_elem;
81 for (unsigned int i_row = 0; i_row < num_spec; ++i_row) {
82 if (jac_struct[i_row][i_col] == 1) jac->row_ids[i_elem++] = i_row;
83 }
84 }
85 jac->col_ptrs[i_col] = i_elem;
86 jac->elements = NULL;
87 return 1;
88}
89
90// Add buffer space for Jacobian column elements (returns 1 on success, 0
91// otherwise)
93 unsigned int *temp_ids;
94 column->array_size += BUFFER_SIZE;
95 temp_ids = (unsigned int *)malloc(column->array_size * sizeof(unsigned int));
96 if (!temp_ids) return 0;
97 for (unsigned int i_elem = 0; i_elem < column->number_of_elements; ++i_elem) {
98 temp_ids[i_elem] = column->row_ids[i_elem];
99 }
100 free(column->row_ids);
101 column->row_ids = temp_ids;
102 return 1;
103}
104
105void jacobian_register_element(Jacobian *jac, unsigned int dep_id,
106 unsigned int ind_id) {
107 if (!jac->elements) {
108 printf(
109 "\n\nERROR - Trying to register elements in a Jacobian that has "
110 "already been built.\n\n");
111 exit(EXIT_FAILURE);
112 }
113 JacobianColumnElements *column = &(jac->elements[ind_id]);
114 for (unsigned int i_elem = 0; i_elem < column->number_of_elements; ++i_elem) {
115 if (column->row_ids[i_elem] == dep_id) return;
116 }
117 if (column->array_size == column->number_of_elements) {
119 }
120 jac->elements[ind_id].row_ids[jac->elements[ind_id].number_of_elements] =
121 dep_id;
122 ++(jac->elements[ind_id].number_of_elements);
123}
124
125int compare_ids(const void *a, const void *b) {
126 return (*(unsigned int *)a - *(unsigned int *)b);
127}
128
129unsigned int jacobian_build_matrix(Jacobian *jac) {
130 if (!jac->elements) {
131 printf(
132 "\n\nERROR - Trying to build a Jacobian that has already been "
133 "built.\n\n");
134 exit(EXIT_FAILURE);
135 }
136 jac->num_elem = 0;
137 for (unsigned int i_col = 0; i_col < jac->num_spec; ++i_col) {
138 JacobianColumnElements *column = &(jac->elements[i_col]);
139 qsort(column->row_ids, column->number_of_elements, sizeof(unsigned int),
141 jac->num_elem += column->number_of_elements;
142 }
143 jac->col_ptrs =
144 (unsigned int *)malloc((jac->num_spec + 1) * sizeof(unsigned int));
145 if (!jac->col_ptrs) {
146 jacobian_free(jac);
147 return 0;
148 }
149 jac->row_ids = (unsigned int *)malloc(jac->num_elem * sizeof(unsigned int));
150 if (!jac->row_ids) {
151 jacobian_free(jac);
152 return 0;
153 }
154 unsigned int i_elem = 0;
155 for (unsigned int i_col = 0; i_col < jac->num_spec; ++i_col) {
156 jac->col_ptrs[i_col] = i_elem;
157 JacobianColumnElements *column = &(jac->elements[i_col]);
158 for (unsigned int j_elem = 0; j_elem < column->number_of_elements;
159 ++j_elem) {
160 jac->row_ids[i_elem++] = column->row_ids[j_elem];
161 }
162 }
163 jac->col_ptrs[jac->num_spec] = i_elem;
164 if (i_elem != jac->num_elem) {
165 printf("\n\n ERROR - Internal error building Jacobain matrix %d %d\n\n",
166 i_elem, jac->num_elem);
167 exit(EXIT_FAILURE);
168 }
170 (long double *)malloc(jac->num_elem * sizeof(long double));
171 if (!jac->production_partials) {
172 jacobian_free(jac);
173 return 0;
174 }
175 jac->loss_partials =
176 (long double *)malloc(jac->num_elem * sizeof(long double));
177 if (!jac->loss_partials) {
178 jacobian_free(jac);
179 return 0;
180 }
181 for (unsigned int i_col = 0; i_col < jac->num_spec; ++i_col) {
183 }
184 free(jac->elements);
185 jac->elements = NULL;
186 jacobian_reset(*jac);
187 return 1;
188}
189
190unsigned int jacobian_number_of_elements(Jacobian jac) { return jac.num_elem; }
191
192unsigned int jacobian_column_pointer_value(Jacobian jac, unsigned int col_id) {
193 return jac.col_ptrs[col_id];
194}
195
196unsigned int jacobian_row_index(Jacobian jac, unsigned int elem_id) {
197 return jac.row_ids[elem_id];
198}
199
200unsigned int jacobian_get_element_id(Jacobian jac, unsigned int dep_id,
201 unsigned int ind_id) {
202 if (ind_id >= jac.num_spec || ind_id < 0) {
203 printf(
204 "\nError: Bad Jacobian column id: %u. Expected value between 0 and "
205 "%u\n",
206 ind_id, jac.num_spec);
207 exit(EXIT_FAILURE);
208 }
209 for (unsigned int i_elem = jac.col_ptrs[ind_id];
210 i_elem < jac.col_ptrs[ind_id + 1]; ++i_elem) {
211 if (jac.row_ids[i_elem] == dep_id) return i_elem;
212 }
213 return -1;
214}
215
217 for (unsigned int i_elem = 0; i_elem < jac.num_elem; ++i_elem) {
218 jac.production_partials[i_elem] = 0.0;
219 jac.loss_partials[i_elem] = 0.0;
220 }
221}
222
223void jacobian_output(Jacobian jac, double *dest_array) {
224 for (unsigned int i_col = 0; i_col < jac.num_spec; ++i_col) {
225 for (unsigned int i_elem = jac.col_ptrs[i_col];
226 i_elem < jac.col_ptrs[i_col + 1]; ++i_elem) {
227 long double drf_dy = jac.production_partials[i_elem];
228 long double drr_dy = jac.loss_partials[i_elem];
229 dest_array[i_elem] = drf_dy - drr_dy;
230 }
231 }
232}
233
234void jacobian_add_value(Jacobian jac, unsigned int elem_id,
235 unsigned int prod_or_loss,
236 long double jac_contribution) {
237 if (prod_or_loss == JACOBIAN_PRODUCTION)
238 jac.production_partials[elem_id] += jac_contribution;
239 if (prod_or_loss == JACOBIAN_LOSS)
240 jac.loss_partials[elem_id] += jac_contribution;
241}
242
244 printf("\n *** Jacobian ***");
245 printf("\nnumber of variables: %d", jac.num_spec);
246 printf("\nnumber of non-zero Jacobian elements: %d", jac.num_elem);
247 if (!jac.col_ptrs && !jac.row_ids && !jac.production_partials &&
248 !jac.loss_partials && jac.elements) {
249 printf("\nstatus: building Jacobian");
250 for (unsigned int i_col = 0; i_col < jac.num_spec; ++i_col) {
251 for (unsigned int i_elem = 0;
252 i_elem < jac.elements[i_col].number_of_elements; ++i_elem) {
253 printf("\n col = %6d row = %6d", i_col,
254 jac.elements[i_col].row_ids[i_elem]);
255 }
256 }
257 } else if (jac.col_ptrs && jac.row_ids && jac.production_partials &&
258 jac.loss_partials) {
259 printf("\nstatus: Jacobian built");
260 for (unsigned int i_col = 0; i_col < jac.num_spec; ++i_col) {
261 for (unsigned int i_elem = jac.col_ptrs[i_col];
262 i_elem < jac.col_ptrs[i_col + 1]; ++i_elem) {
263 printf("\n col = %6d row = %6d production = %Le loss = %Le", i_col,
264 jac.row_ids[i_elem], jac.production_partials[i_elem],
265 jac.loss_partials[i_elem]);
266 }
267 }
268 } else {
269 printf("\nstatus: invalid state");
270 }
271 printf("\n *** end Jacobian ***");
272}
273
275 if (column->row_ids) {
276 free(column->row_ids);
277 column->row_ids = NULL;
278 }
279}
280
282 if (jac->col_ptrs) {
283 free(jac->col_ptrs);
284 jac->col_ptrs = NULL;
285 }
286 if (jac->row_ids) {
287 free(jac->row_ids);
288 jac->row_ids = NULL;
289 }
290 if (jac->production_partials) {
291 free(jac->production_partials);
292 jac->production_partials = NULL;
293 }
294 if (jac->loss_partials) {
295 free(jac->loss_partials);
296 jac->loss_partials = NULL;
297 }
298 if (jac->elements) {
299 for (unsigned int i_col = 0; i_col < jac->num_spec; ++i_col) {
301 }
302 free(jac->elements);
303 jac->elements = NULL;
304 }
305}
unsigned int jacobian_column_pointer_value(Jacobian jac, unsigned int col_id)
Returns the value of a column pointer.
Definition Jacobian.c:192
void jacobian_free(Jacobian *jac)
Free memory associated with a Jacobian.
Definition Jacobian.c:281
unsigned int jacobian_build_matrix(Jacobian *jac)
Builds the sparse matrix with the registered elements.
Definition Jacobian.c:129
int jacobian_initialize_empty(Jacobian *jac, unsigned int num_spec)
Initialize the Jacobian.
Definition Jacobian.c:20
#define BUFFER_SIZE
Definition Jacobian.c:17
int jacobian_initialize(Jacobian *jac, unsigned int num_spec, unsigned int **jac_struct)
Initialize the Jacobian.
Definition Jacobian.c:46
unsigned int jacobian_number_of_elements(Jacobian jac)
Returns the number of elements in the Jacobian.
Definition Jacobian.c:190
unsigned int jacobian_get_element_id(Jacobian jac, unsigned int dep_id, unsigned int ind_id)
Get an element id in the Jacobian data arrays.
Definition Jacobian.c:200
void jacobian_column_elements_free(JacobianColumnElements *column)
Free memory associated with a JacobianColumnElements.
Definition Jacobian.c:274
void jacobian_add_value(Jacobian jac, unsigned int elem_id, unsigned int prod_or_loss, long double jac_contribution)
Add a contribution to the Jacobian.
Definition Jacobian.c:234
int compare_ids(const void *a, const void *b)
Definition Jacobian.c:125
void jacobian_reset(Jacobian jac)
Reset the Jacobian.
Definition Jacobian.c:216
void jacobian_register_element(Jacobian *jac, unsigned int dep_id, unsigned int ind_id)
Adds an element to the sparse matrix.
Definition Jacobian.c:105
int jacobian_column_elements_add_space(JacobianColumnElements *column)
Definition Jacobian.c:92
void jacobian_print(Jacobian jac)
Prints the Jacobian structure.
Definition Jacobian.c:243
void jacobian_output(Jacobian jac, double *dest_array)
Output the Jacobian.
Definition Jacobian.c:223
unsigned int jacobian_row_index(Jacobian jac, unsigned int elem_id)
Returns the row for a given Jacobian element.
Definition Jacobian.c:196
Header for the Jacobian structure and related functions.
#define JACOBIAN_LOSS
Definition Jacobian.h:19
#define JACOBIAN_PRODUCTION
Definition Jacobian.h:18
unsigned int number_of_elements
Definition Jacobian.h:25
unsigned int * row_ids
Definition Jacobian.h:27
unsigned int array_size
Definition Jacobian.h:23
JacobianColumnElements * elements
Definition Jacobian.h:39
unsigned int num_elem
Definition Jacobian.h:33
long double * production_partials
Definition Jacobian.h:37
unsigned int * row_ids
Definition Jacobian.h:35
unsigned int num_spec
Definition Jacobian.h:32
unsigned int * col_ptrs
Definition Jacobian.h:34
long double * loss_partials
Definition Jacobian.h:38
Header for the time derivative structure and related functions.