Skip to content

Commit c42b6af

Browse files
committed
Write Jacobian section and update a few others
1 parent 0e424a1 commit c42b6af

File tree

4 files changed

+127
-10
lines changed

4 files changed

+127
-10
lines changed

docs/jacobian-matrix.md

+123-6
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,130 @@ layout: default
44
nav_order: 5
55
---
66

7-
Work in progress
8-
{: .label .label-red }
9-
107
# Jacobian Matrix class
118

12-
Text
9+
Jacobian matrix class defines the Jacobian matrix $$\mathbf{J}(\mathbf{x}, t)$$ of the DAE system.
10+
This matrix can be obtained by differentiating the vector function (the RHS) $$\mathbf{f}(\mathbf{x}, t)$$ of the DAE system
11+
12+
$$\mathbf{M}(t) \frac{\mathrm{d}\mathbf{x}}{\mathrm{d}t} = \mathbf{f}(\mathbf{x}, t)$$
13+
14+
with respect to $$\mathbf{x}$$.
15+
16+
{: .note }
17+
Providing the Jacobian matrix for the DAE solver is optional. If not provided by the user, the Jacobian matrix will be computed by the solver automatically, using algorithmic differentiation package [`autodiff`](https://autodiff.github.io/).
18+
However, for big DAE systems, it is highly recommended to provide manually (analytically) derived Jacobian. This can save a lot of computation time for the solver and significantly speed it up.
19+
20+
## Jacobian matrix definition
21+
22+
The main Jacobian matrix class `daecpp::JacobianMatrix` is an abstract class that provides an interface to define the Jacobian matrix $$\mathbf{J}(\mathbf{x}, t)$$.
23+
In order to make use of this class, the user should inherit it and overload the `()` operator:
24+
25+
```cpp
26+
class UserDefinedJacobianMatrix : public daecpp::JacobianMatrix
27+
{
28+
public:
29+
void operator()(daecpp::sparse_matrix &J, const daecpp::state_vector &x, const double t) const
30+
{
31+
// Jacobian matrix definition
32+
}
33+
};
34+
```
35+
36+
In the code above, the Jacobian matrix `J` should be defined in the [three-array sparse format](sparse-matrix.html) and can depend on the current state vector $$\mathbf{x}$$ and time `t`.
37+
Initially, matrix `J` is empty and should be filled with non-zero elements.
38+
39+
{: .note }
40+
The type of vector `x` in the class definition above is `daecpp::state_vector`, which is an alias of `std::vector<float_type>` type. See [`dae-cpp` types](https://dae-cpp.github.io/prerequisites.html#dae-cpp-types) section for more information.
41+
42+
## Examples
43+
44+
Consider the following vector function $$\mathbf{f}(\mathbf{x}, t)$$ from the [Vector Function class](vector-function.html#examples) section:
45+
46+
$$
47+
\mathbf{f}(\mathbf{x}, t) =
48+
\begin{vmatrix}
49+
z + 1 \\
50+
x^2 + y \\
51+
2t
52+
\end{vmatrix}.
53+
$$
54+
55+
Differentiation this vector w.r.t. $$\{x,y,z\}$$ gives us the following Jacobian matrix $$\mathbf{J}$$:
56+
57+
$$
58+
\mathbf{J} =
59+
\begin{vmatrix}
60+
0 & 0 & 1 \\
61+
2x & 1 & 0 \\
62+
0 & 0 & 0
63+
\end{vmatrix}.
64+
$$
65+
66+
It has 3 non-zero elements and does not depend on time.
67+
In the code, this Jacobian matrix can be defined as
68+
69+
```cpp
70+
class UserDefinedJacobianMatrix : public daecpp::JacobianMatrix
71+
{
72+
public:
73+
void operator()(daecpp::sparse_matrix &J, const daecpp::state_vector &x, const double t) const
74+
{
75+
J.reserve(3); // Pre-allocate memory for 3 non-zero elements
76+
J(0, 2, 1.0); // Row 0, column 2, non-zero element 1.0
77+
J(1, 0, 2.0 * x[0]); // Row 1, column 0, non-zero element (2 * x)
78+
J(1, 1, 1.0); // Row 1, column 1, non-zero element 1.0
79+
}
80+
};
81+
```
82+
83+
Inhereting the `daecpp::JacobianMatrix` class is a good practice (it serves as a blueprint), however, the user is allowed to define Jacobian matrices using their own custom classes, for example:
84+
85+
```cpp
86+
struct UserDefinedJacobianMatrix
87+
{
88+
void operator()(daecpp::sparse_matrix &J, const daecpp::state_vector &x, const double t)
89+
{
90+
J.reserve(3); // Pre-allocate memory for 3 non-zero elements
91+
J(0, 2, 1.0); // Row 0, column 2, non-zero element 1.0
92+
J(1, 0, 2.0 * x[0]); // Row 1, column 0, non-zero element (2 * x)
93+
J(1, 1, 1.0); // Row 1, column 1, non-zero element 1.0
94+
}
95+
};
96+
```
97+
98+
{: .note }
99+
It is recommended to pre-allocate memory for the Jacobian matrix using `reserve(N_elements)` method, where `N_elements` is the number of non-zero elements in the matrix. If the number of non-zeros is difficult to estimate, it is better to overestimate `N_elements` than underestimate it to avoid unnecessary copying and memory reallocations.
100+
101+
For more information about defining the matrix in sparse format, refer to the [Sparse Matrix class](sparse-matrix.html) section.
102+
103+
## Automatic Jacobian matrix
104+
105+
The solver provides a helper class `daecpp::JacobianAutomatic` to compute the Jacobian matrix for the given vector function $$\mathbf{f}(\mathbf{x}, t)$$ algorithmically using [`autodiff`](https://autodiff.github.io/) package.
106+
For relatively small systems, the user does not even need to define the Jacobian, not even automatic one. This will be handled by the solver itself. Behind the scenes, the solver will create an automatic Jacobian matrix object for the user, if analytic Jacobian is not provided.
107+
108+
However, in some cases, it can be difficult to derive the Jacobian matrix analytically. Or if the user has provided the Jacobian matrix, but the solution diverges, which means there might be a bug in the Jacobian matrix definition. In these cases, the user can try to feed the automatic Jacobian matrix to the solver explicitly, or print out (or save to a file) both user-defined and automatic Jacobians for comparison to find possible errors in the matrix definition.
109+
110+
In order to construct an automatic Jacobian matrix object, the user needs to provide the vector function object `rhs` (see [Vector Function class](vector-function.html) section):
111+
112+
```cpp
113+
// Creates automatic Jacobian object for the given user-defined RHS
114+
daecpp::JacobianAutomatic automaticJacobian(rhs);
115+
```
116+
117+
In the example above, we created an object `automaticJacobian` which computes automatic Jacobian in sparse format suitable for the solver.
118+
To print the Jacobian out (for example, to compare with the user-defined analytic Jacobian), convert the matrix to dense format and use `std::cout`:
119+
120+
```cpp
121+
daecpp::sparse_matrix J; // Empty Jacobian matrix
122+
daecpp::state_vector x(N, 1.0); // State vector of size `N` populated with `1.0`
123+
double t = 1.0; // Time
124+
125+
// Computes automatic Jacobian for the given state `x` and time `t`
126+
automaticJacobian(J, x, t);
13127

14-
## Subtitle
128+
// Prints out Jacobian in dense format
129+
std::cout << J.dense(N) << '\n';
130+
```
15131
16-
Text
132+
{: .highlight }
133+
User-defined vs analytic Jacobian comparison functionality (element by element) will be added in future versions of the solver.

docs/mass-matrix.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Initially, matrix `M` is empty and should be filled with non-zero elements.
3333

3434
## Examples
3535

36-
Consider the following mass matrix $$\mathbf{x}$$:
36+
Consider the following mass matrix $$\mathbf{M}$$:
3737

3838
$$
3939
\mathbf{M} =
@@ -76,7 +76,7 @@ struct UserDefinedMassMatrix
7676
{: .note }
7777
It is recommended to pre-allocate memory for the mass matrix using `reserve(N_elements)` method, where `N_elements` is the number of non-zero elements in the matrix. If the number of non-zeros is difficult to estimate, it is better to overestimate `N_elements` than underestimate it to avoid unnecessary copying and memory reallocations.
7878
79-
For more information about defining the matrix in sparse format, refer to the [Sparse Matrix](sparse-matrix.html) section.
79+
For more information about defining the matrix in sparse format, refer to the [Sparse Matrix class](sparse-matrix.html) section.
8080
8181
## Identity mass matrix
8282

docs/sparse-matrix.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ M(0, 1, 2.0); // Duplicated element is OK, it will be summed up, i.e., the va
9393
## `void add_element(int_type ind_i, int_type ind_j, float_type A_ij)`
9494
9595
<br>
96-
An alias to the operator `()` defined above.
96+
An alias of the operator `()` defined above.
9797
Adds next non-zero element to the sparse matrix.
9898
Duplicated elements will be summed up.
9999

docs/vector-function.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Vector `f` is already pre-allocated with `f.size() == x.size()` and should be us
3333
The elements of vectors `x` and `f` can be accessed using square brackets `[]`.
3434

3535
{: .note }
36-
The type of vectors `f` and `x` is `daecpp::state_type`, which is an [`autodiff`](https://autodiff.github.io/)'s type used to perform automatic (algorithmic) differentiation of the vector function `f`. See [`dae-cpp` types](https://dae-cpp.github.io/prerequisites.html#dae-cpp-types) section.
36+
The type of vectors `f` and `x` is `daecpp::state_type`, which is an alias of [`autodiff`](https://autodiff.github.io/)'s type used to perform automatic (algorithmic) differentiation of the vector function `f`. See [`dae-cpp` types](https://dae-cpp.github.io/prerequisites.html#dae-cpp-types) section.
3737

3838
## Examples
3939

0 commit comments

Comments
 (0)