Hi all,
I am currently trying to optimize a package I’ve written to model stellar evolution. Just in case I prefer to give some context to know if there might be a much better way to do what I’m trying to achieve. Stellar evolution is studied (almost always) as a one dimensional spherically symmetric problem. At each cell we have a set of properties such as the temperature and pressure at a given mass coordinate, with one differential equation for each. Describing it very loosely, the usual way in which this problem is treated is by taking all these variables in a single vector, for a simple case with just temperature and pressure for instance we have
\vec{x}=(T_1, P_1, T_2, P_2,\dots, T_n, P_n)
where n is the number of radial cells. The set of equations are then written as a vector function,
\vec{f}(\vec{x})=(f_{T,1}(\vec{x}),f_{P,1}(\vec{x}), f_{T,2}(\vec{x}),f_{P,2}(\vec{x}),\dots,f_{T,n}(\vec{x}),f_{P,n}(\vec{x}))
where a solution requires \vec{f}(\vec{x})=\vec{0}. Given an initial guess for \vec{x}, the problem is then solved using a Newton-Rhapson solver, which requires the calculation of the Jacobian of \vec{f}. Each of the differential equations only depends on its neighboring cells, so the Jacobian has a nice block tridiagonal structure (such as the unrealistic example below with 5 cells).
J=\left(\matrix{ D_1 & U_1 & & & \\ L_2 & D_2 & U_2 & & \\ & L_3 & D_3 & U_3 & \\ & & L_4 & D_4 & U_4 \\ & & & L_5 & D_5 }\right)
The way I am approaching this right now is computing each row of blocks in the Jacobian in their own using ForwardDiff. So I’m computing all differential equations corresponding to a cell and computing their partials with respect to the other variables in the current and neighboring cell (the edges are treated slightly different).
This is working pretty nicely, and already managed to perform some simple simulations. But there are two things bothering me
- Various intermediate results, such as nuclear reaction rates, depend only on local properties. Because of that it is wasteful to compute their partial derivatives with respect to the properties of neighboring cells.
- While computing each row of blocks in the Jacobian, I have to compute some of these intrinsic properties for the current and neighboring cells. When I compute the row of blocks that follows, I could reutilize all that information, but instead it just goes to waste right now and I recompute everything. So a heavy part of the code is being computed 3 times for the sake of code simplicity.
One solution I see for this would be that before I use ForwardDiff.jacobian
, I pre-compute all of these intrinsic properties with ForwardDiff including only the variables I know they depend on. While computing the Jacobian, I could then unload these pre-computed values into the partials
attribute of a dual vector and perform the rest of the calculations normally. But trying to do that I found out I cannot simply edit the values of an instance of ForwardDiff.Partials
. Is there any simple way to directly modify a dual object to specify its partials inside a function where a Jacobian is being computed with ForwardDiff
?