GithubHelp home page GithubHelp logo

bfb-reactor's People

Contributors

wigging avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

bfb-reactor's Issues

Suggestions on appropriate code structure

The gasifier model calculations are implemented in two classes named GasPhase and SolidPhase. See the gas_phase.py and solid_phase.py files. Are using class objects a good design for this project or would it be better to just use functions for everything? Or is there a better design pattern that is suitable for this project?

Improving the Jacobian update speed

The issue of the Jacobian update is closely related to the slowness of the time integration using implicit methods.
Usually, in 1D systems, the temporal evolution of a variable in the i-th cell (or point) is only affected by the variables in the same cell, and the variables in the neighbouring cells, potentially up to a few neighbours, depending on the order of the spatial scheme used. The number of neighbours used is called the stencil. For example, for the simple heat equation dT/dt = d/dx( dT/dx ), the semi-discretisation in space with a second-order centered finite difference scheme yields:
dT(i) / dt = (T(i+1) - 2 T(i) + T(i-1) ) / dx^2, this is a 3 points stencil (2 neighbours + the cell in the middle).

This means that if, for you complete 1D system of the form dy/dt = f(t,y) with y the vector of all variables (order by cell rather than by type), the Jacobian df/dy will be block-diagonal. This is very helpful to allow for a fast Jacobian determination via finite difference (see "On the Estimation of Sparse Jacobian Matrices", by A. R. CURTIS, M. J. D. POWELL, J. K. REID).

Here, I see that your default oredering of y is by variable type (i.e. all Tg variables are contiguous for instance). Looking at the sparsity pattern reveals that your Jacobian is not "nice" for a 1D system:

image

Here you see a black dot at line i, column j every time the derivative of the i-th component of f(y) with respect to the j-th variable is not zero, i.e. ( df(i)/dt )(j) != 0. What wee see here is that there are several big black boxes. Let's take the example of the first one in the upper left corner of the graph. This corresponds to the partial derivatives of dTg/dt with respect to Tg. We see that all the discrete values of Tg (actually only the Ni first ones I believe) have an impact on all the others ! So we do not this "finite" length stencil. This is not coherent with the traditional PDEs of the form dTg/dt = d T_g / dx for example.
In the gas phase code, I see that this is actually due to the use of a mean temperature:

Tgm = np.mean(np.append(self.Tg[0:Ni], Tgin))

If you cannot avoid computing such a mean temperature (either by using a constant value, or by using reformulating the model so as to not use one), you will not be able to obtain a very sparse Jacobian, therefore it will not be possible to determine the Jacobian efficiently (again, see the linked article to see why having a very sparse structure is helpful). It is likely that all the other black blocks in your Jacobian arise from similar problems.

With the current code, I have used a python function that is able to recognize the sparsity pattern and optimize the determination of the jacobian (this is scipy.optimize._numdiff.approx_derivative). It is able to compute the Jacobian in ~600 function calls against 1501 calls without this optimization. This would maybe give you a speedup of 2 overall if you gave this optimized Jacobian determination to solve_ivp. This is still not enough...

Ideally, if you had a 3 point stencils for your spatial discretisation and no such big black boxes in your Jacobian sparsity pattern, you would be able to compute the Jacobian in 3*Nvar = 45 calls. This would definitely make a big difference !

If you cannot do that, one solution is to give up on Python and use a truly performant code (Fortran, C++, Julia...). Another idea would be to perform an operator splitting in time (using an implicit solver for the stiff terms in f, if those have a "sparse" Jacobian, otherwise it would not bring much benefit), and an explicit one for the less stiff terms (which could then have any kind of stencil, as no Jacobian would be involved).

So, my main question is: would you have a way to modify your model so as to get rid of these black boxes you have in your Jacobian ?

Solver iterates very slowly and does not obtain a solution

The current implementation of the model using the Radau method with default tolerances does not converge to a solution. The model will run for several minutes and eventually output the warnings shown below. I terminated the program after 26 minutes because the solver had not obtained a solution. I'm not sure what's going on but suggestions on how to use SciPy's solve_ivp function for this gasifier model would be helpful.

solid_phase.py:139: RuntimeWarning: invalid value encountered in power
  * ((kp * rhop * cpp)**(-0.5) + (ks * rhos * cps)**(-0.5))**(-1)

gas_phase.py:104: RuntimeWarning: overflow encountered in power
  cpgg[:, j] = Acp[j] + Bcp[j] * Tg + Ccp[j] * Tg**2 + Dcp[j] * Tg**3 + Ecp[j] * Tg**4

gas_phase.py:104: RuntimeWarning: invalid value encountered in add
  cpgg[:, j] = Acp[j] + Bcp[j] * Tg + Ccp[j] * Tg**2 + Dcp[j] * Tg**3 + Ecp[j] * Tg**4

/numpy/core/fromnumeric.py:87: RuntimeWarning: invalid value encountered in reduce
  return ufunc.reduce(obj, axis, dtype, out, **passkwargs)

gas_phase.py:172: RuntimeWarning: overflow encountered in double_scalars
  Ar = dp**3 * rhogi * (rhop - rhogi) * g / muin**2

gas_phase.py:175: RuntimeWarning: divide by zero encountered in double_scalars
  Umsr = (np.exp(-0.5405 * Lsi / Db) * (4.294e3 / Ar + 1.1) + 3.676e2 * Ar**(-1.5) + 1)

gas_phase.py:181: RuntimeWarning: invalid value encountered in double_scalars
  Rrb = (1 - 0.103 * (Umsr * umf - umf)**(-0.362) * Drbs)**(-1)

solid_phase.py:80: RuntimeWarning: overflow encountered in double_scalars
  Cs[i] = rhob_s[i] * cps[i]

solid_phase.py:161: RuntimeWarning: invalid value encountered in sqrt
  Nud = 2 + 0.6 * Re_dc**0.5 * Pr**0.33

kinetics.py:97: RuntimeWarning: invalid value encountered in power
  yv = m0v * Tsv**b0v

gas_phase.py:492: RuntimeWarning: invalid value encountered in sqrt
  Nud = 2 + 0.6 * Re_dc**0.5 * Pr**0.33

gas_phase.py:497: RuntimeWarning: invalid value encountered in power
  (7 - 10 * afg + 5 * afg**2) * (1 + 0.7 * Rep**0.2 * Pr**0.33)

gas_phase.py:498: RuntimeWarning: invalid value encountered in power
  + (1.33 - 2.4 * afg + 1.2 * afg**2) * Rep**0.7 * Pr**0.33

gas_phase.py:513: RuntimeWarning: invalid value encountered in power
  Nuf = 0.023 * ReD**0.8 * Pr**0.4

solid_phase.py:279: RuntimeWarning: invalid value encountered in power
  Nup = (7 - 10 * afg + 5 * afg**2) * (1 + 0.7 * Rep**0.2 * Pr**0.33) + (1.33 - 2.4 * afg + 1.2 * afg**2) * Rep**0.7 * Pr**0.33

solid_phase.py:362: RuntimeWarning: invalid value encountered in power
  Nup = (7 - 10 * afg + 5 * afg**2) * (1 + 0.7 * Rep**0.2 * Pr**0.33) + (1.33 - 2.4 * afg + 1.2 * afg**2) * Rep**0.7 * Pr**0.33

solid_phase.py:368: RuntimeWarning: overflow encountered in power
  qwr = np.pi * Dwi * epb / ((1 - ep) / (ep * epb) + (1 - ew) / ew + 1) * sc * (Tw**4 - Tp**4)

solid_phase.py:368: RuntimeWarning: invalid value encountered in subtract
  qwr = np.pi * Dwi * epb / ((1 - ep) / (ep * epb) + (1 - ew) / ew + 1) * sc * (Tw**4 - Tp**4)

solid_phase.py:381: RuntimeWarning: invalid value encountered in power
  Nuf = 0.023 * ReD**0.8 * Pr**0.4

solid_phase.py:433: RuntimeWarning: invalid value encountered in power
  24 / Re_dc * (1 + 8.1716 * Re_dc**(0.0964 + 0.5565 * sfc) * np.exp(-4.0655 * sfc))

solid_phase.py:464: RuntimeWarning: overflow encountered in multiply
  + Spav[0:Ni] * v[0:Ni] / rhos[0:Ni]

solid_phase.py:464: RuntimeWarning: invalid value encountered in add
  + Spav[0:Ni] * v[0:Ni] / rhos[0:Ni]

gas_phase.py:285: RuntimeWarning: invalid value encountered in power
  24 / Re_dc * (1 + 8.1716 * Re_dc**(0.0964 + 0.5565 * sfc) * np.exp(-4.0655 * sfc))

gas_phase.py:305: RuntimeWarning: overflow encountered in multiply
  Smgs = (3 / 4) * rhosbav * (rhog / rhos) * (Cd / ds) * np.abs(-ug - v)

kinetics.py:97: RuntimeWarning: overflow encountered in power
  yv = m0v * Tsv**b0v

kinetics.py:99: RuntimeWarning: invalid value encountered in true_divide
  xv = yv * Mv / np.sum(yv * Mv)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.