├── .gitignore ├── LICENSE ├── PyCSP ├── Functions.py ├── GScheme.py ├── Simplify.py ├── Solver.py ├── ThermoKinetics.py ├── profiling_tools.py ├── soln2ck.py ├── soln2cti.py └── utils.py ├── README.md ├── documentation └── PyCSP_documentation.pdf ├── examples ├── cspAnalysis │ ├── JacobianSIM.py │ ├── amplitudes.png │ ├── eigenvalues.png │ ├── exhM.png │ ├── exhaustedModes.py │ ├── hydrogen.yaml │ ├── indices-integral.py │ ├── indices.py │ ├── pointers.py │ ├── subspaces.py │ └── traj.png ├── cspSimplify │ ├── gri30.yaml │ ├── simp.py │ └── simp_TSR.py ├── cspSolver │ ├── cspsolver_const_p.py │ ├── cspsolver_const_v.py │ ├── gri30.yaml │ ├── gscheme_const_p.py │ ├── gscheme_const_p_reuse.py │ └── hydrogen.yaml ├── dataImportExport │ ├── export_import.py │ └── hydrogen.yaml └── tsrAnalysis │ ├── TSR.py │ ├── TSR_ext.py │ ├── chaos12.yaml │ └── hydrogen.yaml ├── logo.png ├── requirements.txt ├── setup.py └── tests ├── hydrogen.yaml ├── test_kernel.py ├── test_rhs.py └── test_rhs_constV.py /.gitignore: -------------------------------------------------------------------------------- 1 | #OSX 2 | .DS_Store 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | out/ 16 | obj/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | pip-wheel-metadata/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | *.py,cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Riccardo Malpica Galassi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PyCSP/GScheme.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Mon Sep 11 15:41:22 2023 5 | Last update on Wed Nov 20 2024 6 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 7 | """ 8 | import sys 9 | import numpy as np 10 | import pandas as pd 11 | import PyCSP.Functions as cspF 12 | from PyCSP.profiling_tools import profile_cpu_time_and_count 13 | 14 | 15 | class GScheme: 16 | def __init__(self, gas): 17 | self._initializing = True 18 | self._gas = gas 19 | self._y = [] 20 | self._t = [] 21 | self._Tc = [] 22 | self._Hc = [] 23 | self._dt = 1e+10 24 | self.csprtolT = 1e-2 25 | self.cspatolT = 1e-8 26 | self.csprtolH = 1e-4 27 | self.cspatolH = 1e-10 28 | self.jacobiantype = 'full' 29 | self._T = 0 30 | self._H = gas.nv - gas.n_elements 31 | self.factor = 0.2 32 | self._iter = 0 33 | 34 | self.reuse = False 35 | self.reuseThr = 1.e-1 36 | self._diagJac = [] 37 | self._normJac = 0.0 38 | self._skipBasis = 0 39 | self._updateBasis = 0 40 | self._basisStatus = 0 41 | 42 | self._lam = [] 43 | self._A = [] 44 | self._B = [] 45 | self._f = [] 46 | self._tau = [] 47 | 48 | self.integrate_time = 0.0 49 | self.integrate_n = 0 50 | self.CSPbasis_time = 0.0 51 | self.CSPbasis_n = 0 52 | self.calcM_time = 0.0 53 | self.calcM_n = 0 54 | self.calcH_time = 0.0 55 | self.calcH_n = 0 56 | self.RK4_time = 0.0 57 | self.RK4_n = 0 58 | 59 | self._initializing = False 60 | 61 | 62 | 63 | @property 64 | def csprtolT(self): 65 | return self._csprtolT 66 | 67 | @csprtolT.setter 68 | def csprtolT(self,value): 69 | self._csprtolT = value 70 | 71 | @property 72 | def cspatolT(self): 73 | return self._cspatolT 74 | 75 | @cspatolT.setter 76 | def cspatolT(self,value): 77 | self._cspatolT = value 78 | 79 | @property 80 | def csprtolH(self): 81 | return self._csprtolH 82 | 83 | @csprtolH.setter 84 | def csprtolH(self,value): 85 | self._csprtolH = value 86 | 87 | @property 88 | def cspatolH(self): 89 | return self._cspatolH 90 | 91 | @cspatolH.setter 92 | def cspatolH(self,value): 93 | self._cspatolH = value 94 | 95 | @property 96 | def jacobiantype(self): 97 | return self._jacobiantype 98 | 99 | @jacobiantype.setter 100 | def jacobiantype(self,value): 101 | if value == 'full': 102 | self._jacobiantype = value 103 | else: 104 | raise ValueError("Invalid jacobian type --> %s" %value) 105 | 106 | @property 107 | def Tc(self): 108 | return self._Tc 109 | 110 | @property 111 | def Hc(self): 112 | return self._Hc 113 | 114 | @property 115 | def y(self): 116 | return self._y 117 | 118 | @property 119 | def t(self): 120 | return self._t 121 | 122 | @property 123 | def dt(self): 124 | return self._dt 125 | 126 | @property 127 | def T(self): 128 | return self._T 129 | 130 | @property 131 | def H(self): 132 | return self._H 133 | 134 | @property 135 | def factor(self): 136 | return self._factor 137 | 138 | @factor.setter 139 | def factor(self,value): 140 | self._factor = value 141 | 142 | @property 143 | def reuseThr(self): 144 | return self._reuseThr 145 | 146 | @reuseThr.setter 147 | def reuseThr(self,value): 148 | self._reuseThr = value 149 | 150 | @property 151 | def iter(self): 152 | return self._iter 153 | 154 | @property 155 | def reuse(self): 156 | return self._reuse 157 | 158 | @reuse.setter 159 | def reuse(self,value): 160 | self._reuse = value 161 | 162 | @property 163 | def normJac(self): 164 | return self._normJac 165 | 166 | @normJac.setter 167 | def normJac(self,value): 168 | self._normJac = value 169 | 170 | @property 171 | def diagJac(self): 172 | return self._diagJac 173 | 174 | @diagJac.setter 175 | def diagJac(self,value): 176 | self._diagJac = value 177 | 178 | @property 179 | def skipBasis(self): 180 | return self._skipBasis 181 | 182 | @skipBasis.setter 183 | def skipBasis(self,value): 184 | self._skipBasis = value 185 | 186 | @property 187 | def updateBasis(self): 188 | return self._updateBasis 189 | 190 | @updateBasis.setter 191 | def updateBasis(self,value): 192 | self._updateBasis = value 193 | 194 | @property 195 | def basisStatus(self): 196 | return self._basisStatus 197 | 198 | @basisStatus.setter 199 | def basisStatus(self,value): 200 | self._basisStatus = value 201 | 202 | @property 203 | def lam(self): 204 | return self._lam 205 | 206 | @property 207 | def A(self): 208 | return self._A 209 | 210 | @property 211 | def B(self): 212 | return self._B 213 | 214 | @property 215 | def f(self): 216 | return self._f 217 | 218 | @property 219 | def tau(self): 220 | return self._tau 221 | 222 | """ ### Methods ### """ 223 | 224 | def CSPcore(self,y): 225 | self._gas.set_stateYT(y) 226 | if(self.reuse): self.normJac = self.calc_normJac(y) 227 | if(not self.reuse or self.iter < 3 or self.normJac > self.reuseThr or self.skipBasis > 250): 228 | self.skipBasis = 0 229 | self.basisStatus = 1 230 | self._lam,self._A,self._B,self._f = self.get_CSP_kernel() 231 | self._tau = cspF.timescales(self.lam) 232 | self.updateBasis = self.updateBasis + 1 233 | else: 234 | self._f = np.matmul(self.B,self.rhs(y)) 235 | self.basisStatus = 0 236 | self.skipBasis = self.skipBasis + 1 237 | 238 | @profile_cpu_time_and_count("CSPbasis_time", "CSPbasis_n", log=False) 239 | def get_CSP_kernel(self): 240 | lam,A,B,f = self._gas.get_kernel() 241 | return lam,A,B,f 242 | 243 | 244 | @profile_cpu_time_and_count("calcM_time", "calcM_n", log=False) 245 | def CSPexhaustedModes(self,y,lam,A,B,tau,rtol,atol): 246 | self._gas.set_stateYT(y) 247 | f = np.matmul(B,self._gas.source) 248 | M = cspF.findM(self._gas.n_elements,y,lam,A,tau,f,rtol,atol) 249 | return M 250 | 251 | @profile_cpu_time_and_count("calcH_time", "calcH_n", log=False) 252 | def CSPslowModes(self,y,lam,A,B,dt,rtol,atol,Tail): 253 | self._gas.set_stateYT(y) 254 | f = np.matmul(B,self._gas.source) 255 | H = cspF.findH(self._gas.n_elements,y,lam,A,dt,f,rtol,atol,Tail) 256 | return H 257 | 258 | def CSPamplitudes(self,y,B): 259 | f = np.matmul(B,self.rhs(y)) 260 | return f 261 | 262 | def rhs(self,y): 263 | self._gas.set_stateYT(y) 264 | dydt = np.zeros(self._gas.nv) 265 | dydt = self._gas.source 266 | return dydt 267 | 268 | def set_integrator(self,**kwargs): 269 | for key, value in kwargs.items(): 270 | if(key == 'cspRtolTail'): 271 | self.csprtolT = value 272 | elif(key == 'cspAtolTail'): 273 | self.cspatolT = value 274 | elif(key == 'cspRtolHead'): 275 | self.csprtolH = value 276 | elif(key == 'cspAtolHead'): 277 | self.cspatolH = value 278 | elif(key == 'factor'): 279 | self.factor = value 280 | elif(key == 'jacobiantype'): 281 | self.jacobiantype = value 282 | else: 283 | sys.exit('Unknown keyword in set_integrator') 284 | 285 | def set_initial_value(self,y0,t0): 286 | self._y = y0 287 | self._t = t0 288 | 289 | @profile_cpu_time_and_count("integrate_time", "integrate_n", log=False) 290 | def integrate(self): 291 | #calc CSP basis in yold 292 | self._gas.jacobiantype = self.jacobiantype 293 | yold = self.y 294 | self.CSPcore(yold) 295 | 296 | #apply tail correction and advance to ysol 297 | self._Tc = TCorr(self.A,self.f,self.lam,self.T) 298 | ysol = self.tail_correction() 299 | self._y = ysol 300 | 301 | #calc new T 302 | self._T = self.CSPexhaustedModes(ysol,self.lam,self.A,self.B,self.tau,self.csprtolT,self.cspatolT) 303 | 304 | #calc integration time 305 | self._dt = smart_timescale(self.tau,self.factor,self.T,self.lam[self.T],self.dt) 306 | 307 | #calc new H 308 | self._H = self.CSPslowModes(ysol,self.lam,self.A,self.B,self.dt,self.csprtolH,self.cspatolH,self.T) 309 | 310 | #advance in time active dynamics to ya 311 | #print('integrating %d - %d = %d modes\n' %(self.H,self.T,len(ysol[self.T:self.H]))) 312 | ya = self.RK4gsc(self.A,self.B) 313 | 314 | #calc Hc in ystar 315 | self._y = ya 316 | f = self.CSPamplitudes(ya,self.B) 317 | self._Hc = HCorr(self.A,f,self.lam,self.H,self._gas.n_elements,self.dt) 318 | #apply head correction and advance to ystar 319 | ystar = self.head_correction() 320 | 321 | #calc Tc in ystar 322 | self._y = ystar 323 | f = self.CSPamplitudes(ystar,self.B) 324 | self._Tc = TCorr(self.A,f,self.lam,self.T) 325 | #apply tail correction and advance to ynew 326 | ynew = self.tail_correction() 327 | 328 | self._y = ynew 329 | self._t = self.t + self.dt 330 | self._iter = self.iter + 1 331 | 332 | @profile_cpu_time_and_count("RK4_time", "RK4_n", log=False) 333 | def RK4gsc(self,A,B): 334 | def delcsi(csi): 335 | y2 = self.y + np.matmul(np.transpose(A[self.T:self.H]),csi) 336 | dcsi = np.matmul(B[self.T:self.H],self.rhs(y2)) 337 | return dcsi 338 | h = self.dt 339 | yn = self.y 340 | csi = np.zeros((self.H-self.T)) 341 | csi2 = csi + 0.5*h*np.array(delcsi(csi)) 342 | csi3 = csi + 0.5*h*np.array(delcsi(csi2)) 343 | csi2 = csi2 + 2.0*csi3 344 | csi3 = csi + h*np.array(delcsi(csi3)) 345 | csi2 = csi2 + csi3 346 | csi = (csi2 - csi + 0.5*h*np.array(delcsi(csi3))) / 3.0 347 | 348 | ynn = yn + np.matmul(np.transpose(A[self.T:self.H]),csi) 349 | return ynn 350 | 351 | def tail_correction(self): 352 | ynew = self.y - self.Tc 353 | return ynew 354 | 355 | def head_correction(self): 356 | ynew = self.y + self.Hc 357 | return ynew 358 | 359 | def calc_normJac(self,y): 360 | diagOld = self.diagJac.copy() 361 | self.diagJac = self._gas.jacobian_diagonal 362 | normJac = 0.0 363 | if (self.iter > 3): 364 | dum = [(self.diagJac[i]-diagOld[i])/self.diagJac[i] if(np.abs(self.diagJac[i]) > 1e-20) else 0.0 for i in range(len(diagOld)-1)] 365 | normJac = np.linalg.norm(dum) 366 | #print('norm = %10.3e' % normJac) 367 | return normJac 368 | 369 | def profiling(self): 370 | data = { 371 | "Function": ["G-Scheme Total", "Basis calculation", "M calculation", "H calculation", "RK4 solve"], 372 | "Total Time (s)": [self.integrate_time, self.CSPbasis_time, self.calcM_time, self.calcH_time, self.RK4_time], 373 | "Time %": [self.integrate_time*100/self.integrate_time, self.CSPbasis_time*100/self.integrate_time, self.calcM_time*100/self.integrate_time, self.calcH_time*100/self.integrate_time, self.RK4_time*100/self.integrate_time], 374 | "Calls": [self.integrate_n,self.CSPbasis_n,self.calcM_n,self.calcH_n,self.RK4_n], 375 | "Time per Call (s)": [self.integrate_time/self.integrate_n, self.CSPbasis_time/self.CSPbasis_n if self.CSPbasis_n != 0 else 0, self.calcM_time/self.calcM_n, self.calcH_time/self.calcH_n, self.RK4_time/self.RK4_n], 376 | } 377 | 378 | # Create the DataFrame 379 | df = pd.DataFrame(data) 380 | 381 | extra_rows = [ 382 | {"Function": "Other", "Total Time (s)": self.integrate_time - (self.CSPbasis_time + self.calcM_time + self.calcH_time + self.RK4_time), "Time %": (self.integrate_time - (self.CSPbasis_time + self.calcM_time + self.calcH_time + self.RK4_time))*100/self.integrate_time, "Calls": self.integrate_n, "Time per Call (s)": (self.integrate_time - (self.CSPbasis_time + self.calcM_time + self.calcH_time + self.RK4_time))/self.integrate_n}, 383 | ] 384 | df = pd.concat([df, pd.DataFrame(extra_rows)], ignore_index=True) 385 | 386 | # Print the table 387 | print(df.to_string(index=False)) 388 | 389 | 390 | 391 | def TCorr(A,f,lam,T): 392 | ns = A.shape[0] 393 | Tc = np.zeros((ns)) 394 | if(T > 0): 395 | #fLamVec = f[0:T]*(1.0/lam[0:T].real) #linear approximation 396 | fLamVec = f[0:T]*(1 - np.exp((1.0/abs(lam[T].real))*lam[0:T].real) )/lam[0:T].real #exponential decay approximation 397 | Tc = np.matmul(np.transpose(A[0:T]), fLamVec) 398 | return Tc 399 | 400 | def HCorr(A,f,lam,H,nel,dt): 401 | ns = A.shape[0] 402 | Hc = np.zeros((ns)) 403 | up = ns - nel 404 | if(H < up): 405 | fLamVec = dt*f[H:up]*(1 + 0.5 * dt * lam[H:up].real) #first-order 406 | Hc = np.matmul(np.transpose(A[H:up]), fLamVec) 407 | return Hc 408 | 409 | def smart_timescale(tau,factor,T,lamT,dtold): 410 | if(T==0): 411 | dt = tau[T]*factor 412 | else: 413 | dt = tau[T-1] + factor*(tau[T]-tau[T-1]) 414 | #if(lamT.real > 0): 415 | dt = min(dt,1.5*dtold) 416 | return dt 417 | -------------------------------------------------------------------------------- /PyCSP/Simplify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Jan 5 12:26:32 2021 5 | 6 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 7 | """ 8 | import numpy as np 9 | 10 | 11 | class CSPsimplify: 12 | def __init__(self, gas, dataset): 13 | self._gas = gas 14 | self._nr = self._gas.n_reactions 15 | self._ns = self._gas.n_species 16 | self._nv = self._gas.n_species+1 17 | self.csprtol = 1e-2 18 | self.cspatol = 1e-8 19 | self.scaled = True 20 | self.TSRtargetset = False 21 | self.TSRtol = 0.5 22 | self.problemtype = 'constP' 23 | self.dataset = dataset 24 | self.targetset = set() 25 | self.ImpoFast = [] 26 | self.ImpoSlow = [] 27 | self.TSRAPI = [] 28 | self.speciestype = [] 29 | self.verbose = False 30 | 31 | @property 32 | def targetset(self): 33 | return self._targetset 34 | 35 | @targetset.setter 36 | def targetset(self,items): 37 | self._targetset = items 38 | 39 | @property 40 | def TSRtargetset(self): 41 | return self._TSRtargetset 42 | 43 | @TSRtargetset.setter 44 | def TSRtargetset(self,value): 45 | self._TSRtargetset = value 46 | 47 | @property 48 | def nv(self): 49 | return self._nv 50 | 51 | @property 52 | def nr(self): 53 | return self._nr 54 | 55 | @property 56 | def ns(self): 57 | return self._ns 58 | 59 | @property 60 | def csprtol(self): 61 | return self._csprtol 62 | 63 | @csprtol.setter 64 | def csprtol(self,value): 65 | self._csprtol = value 66 | self._gas.csprtol = value 67 | 68 | @property 69 | def cspatol(self): 70 | return self._cspatol 71 | 72 | @cspatol.setter 73 | def cspatol(self,value): 74 | self._cspatol = value 75 | self._gas.cspatol = value 76 | 77 | @property 78 | def problemtype(self): 79 | return self._problemtype 80 | 81 | @problemtype.setter 82 | def problemtype(self,value): 83 | if value == 'constP' or value == 'constRho': 84 | self._problemtype = value 85 | else: 86 | raise ValueError("Invalid problem type --> %s" %value) 87 | 88 | @property 89 | def verbose(self): 90 | return self._verbose 91 | 92 | @verbose.setter 93 | def verbose(self,value): 94 | self._verbose = value 95 | 96 | 97 | def dataset_info(self): 98 | import time 99 | lenData = self.dataset.shape[0] 100 | randY = np.random.dirichlet(np.ones(self.ns),size=1) 101 | self._gas.TP = 1000,101325.0 102 | self._gas.Y = randY 103 | self._gas.constP = 101325.0 104 | start = time.time() 105 | api, tpi, ifast, islow, species_type = self._gas.calc_CSPindices(API=False,Impo=True,species_type=True,TPI=False) 106 | end = time.time() 107 | time = end-start 108 | totaltime = time*lenData 109 | print('Number of species: %i' % self.ns) 110 | print('Number of reactions: %i' % self.nr) 111 | print('Dataset length: %i' % lenData) 112 | print('Estimated data processing time: %10.3e [s]' % totaltime) 113 | 114 | 115 | def process_dataset(self): 116 | #calc CSP basis and importance indexes 117 | self._gas.jacobiantype = 'full' 118 | lenData = self.dataset.shape[0] 119 | self.ImpoFast = np.zeros((lenData,self.nv,2*self.nr), dtype=float) 120 | self.ImpoSlow = np.zeros((lenData,self.nv,2*self.nr), dtype=float) 121 | self.TSRAPI = np.zeros((lenData,2*self.nr), dtype=float) 122 | self.speciestype = np.zeros((lenData,self.nv), dtype=object) 123 | for idx in range(lenData): 124 | if(self.verbose): print("processing state %i ..." %idx) 125 | self._gas.Y = self.dataset[idx,:-2] 126 | self._gas.TP = self.dataset[idx,-2],self.dataset[idx,-1] 127 | if (self.problemtype == 'constP'): 128 | self._gas.constP = self.dataset[idx,-1] 129 | else: 130 | rho = self._gas.density 131 | self._gas.constRho = rho 132 | api, tpi, ifast, islow, species_type = self._gas.calc_CSPindices(API=False,Impo=True,species_type=True,TPI=False) 133 | self.ImpoFast[idx] = np.abs(ifast) 134 | self.ImpoSlow[idx] = np.abs(islow) 135 | self.speciestype[idx] = species_type 136 | if(self.TSRtargetset): 137 | self.TSRAPI[idx] = np.abs(self._gas.calc_TSRindices(type='amplitude')) 138 | 139 | 140 | def simplify_mechanism(self, threshold): 141 | if len(self.ImpoFast) == 0: 142 | raise ValueError("Need to process dataset first") 143 | if (len(self.targetset) == 0 and not self.TSRtargetset): 144 | raise ValueError("Need to define targetset") 145 | if threshold < 0 or threshold > 1: 146 | raise ValueError("Threshold must be between 0 and 1") 147 | lenData = self.dataset.shape[0] 148 | all_active_species = [] 149 | all_active_reacs = np.zeros((lenData,2*self.nr),dtype=int) 150 | #loop over dataset points 151 | for idx in range(lenData): 152 | if(self.verbose): print("finding active species in state %i ..." %idx) 153 | active_species = self.targetset.copy() 154 | if self.TSRtargetset: 155 | tsrtarget = self.get_tsrtarget(self.TSRAPI[idx],self.TSRtol) 156 | if(self.verbose): print( "TSR-target set = "+str(tsrtarget) ) 157 | active_species.update(tsrtarget) 158 | #print(active_species) 159 | trace,fast,slow = self.get_species_sets(self.speciestype[idx]) 160 | iter = 0 161 | while True: 162 | 163 | previous_active = active_species.copy() 164 | #update species relevant to active species 165 | #print('iteration %i' % iter) 166 | active_reactions = np.zeros(2*self.nr) 167 | for i,specname in zip(range(self.ns),self._gas.species_names): 168 | newreactions = np.zeros(2*self.nr) 169 | if specname in active_species and specname in slow: 170 | newreactions = find_active_reactions(i,self.ImpoSlow[idx],threshold,self.scaled) 171 | elif specname in active_species and specname in fast: 172 | newreactions = find_active_reactions(i,self.ImpoFast[idx],threshold,self.scaled) 173 | active_reactions[newreactions == 1] = 1 174 | 175 | #update species relevant to temperature 176 | newreactions = find_active_reactions(self.ns,self.ImpoSlow[idx],threshold,self.scaled) 177 | active_reactions[newreactions == 1] = 1 178 | 179 | newspecies = self.find_species_in_reactions(active_reactions) 180 | active_species.update(newspecies) 181 | 182 | #remove trace species 183 | active_species.difference_update(trace) 184 | 185 | if(self.verbose): print( "active species = "+str(active_species) ) 186 | 187 | iter = iter + 1 188 | if active_species == previous_active: 189 | break 190 | 191 | active_species.update(self.targetset) #add again target species, in case inerts have been removed as traces 192 | all_active_species.append(active_species) #list containing active species in each datapoint 193 | all_active_reacs[idx] = active_reactions #list containing active reactions in each datapoint 194 | 195 | 196 | #union of active reactions 197 | reactions = self.unite_active_reactions(all_active_reacs) 198 | species = set().union(*all_active_species) 199 | 200 | 201 | #recovery: grab all the reactions containing active species 202 | reactions = find_reactions_given_species(self._gas,species) 203 | species = [self._gas.species(name) for name in species] #convert to species object 204 | 205 | print('@threshold: %1.3f, Simplified mechanism contains %i species and %i reactions' % (threshold, len(species), len(reactions))) 206 | 207 | return species, reactions 208 | 209 | 210 | def get_tsrtarget(self,tsrapi,thr): 211 | tpls = [i for i in sorted(enumerate(tsrapi), reverse=True, key=lambda x:x[1])] 212 | sorted_api = [x[1] for x in tpls] 213 | if(sum(sorted_api)==0.0): 214 | return set() 215 | n = 1 216 | for i in range(len(sorted_api)): 217 | while sum(sorted_api[:i+1]) <= thr: 218 | n = n + 1 219 | break 220 | tsrset = set() 221 | for i in range(n): 222 | k = tpls[i][0] 223 | if k >= self.nr: k = k - self.nr 224 | tsrset.update(list(self._gas.reaction(k).reactants.keys())) 225 | tsrset.update(list(self._gas.reaction(k).products.keys())) 226 | return tsrset 227 | 228 | 229 | def find_species_in_reactions(self, active_reactions): 230 | species = {} 231 | for k in range(self.nr): 232 | if active_reactions[k] == 1: 233 | species.update(self._gas.reaction(k).reactants) 234 | species.update(self._gas.reaction(k).products) 235 | if active_reactions[self.nr+k] == 1: 236 | species.update(self._gas.reaction(k).reactants) 237 | species.update(self._gas.reaction(k).products) 238 | return species 239 | 240 | 241 | def get_species_sets(self,speciestype): 242 | trace = set() 243 | fast = set() 244 | slow = set() 245 | for k,item in zip(range(self.ns),speciestype): 246 | if item == 'trace': 247 | trace.add(self._gas.species_name(k)) 248 | elif item == 'slow': 249 | slow.add(self._gas.species_name(k)) 250 | elif item == 'fast': 251 | fast.add(self._gas.species_name(k)) 252 | return trace,fast,slow 253 | 254 | def unite_active_reactions(self, reacs): 255 | reactions = [] 256 | for k in range(self.nr): 257 | if np.any(reacs[:, k] == 1) or np.any(reacs[:, k+self.nr] == 1): 258 | reactions.append(self._gas.reaction(k)) 259 | return reactions 260 | 261 | 262 | def find_reactions_given_species(gas,species): 263 | reactions = [] 264 | for k in range(gas.n_reactions): 265 | if set(gas.reaction(k).reactants.keys()).issubset(species) and set(gas.reaction(k).products.keys()).issubset(species): 266 | reactions.append(gas.reaction(k)) 267 | return reactions 268 | 269 | 270 | def find_active_reactions(ivar,impo,thr,scaled): 271 | if scaled: 272 | Imax = np.max(np.abs(impo[ivar])) 273 | else: 274 | Imax = 1.0 275 | active_reactions = np.zeros(impo.shape[1]) 276 | if Imax > 0: #protects against all-zeros ImpoIndex 277 | active_reactions[impo[ivar] >= thr*Imax] = 1 278 | 279 | return active_reactions 280 | 281 | 282 | def merge_mechanisms(gasdtl,species1,species2): 283 | """ 284 | 285 | Parameters 286 | ---------- 287 | gasdtl : Cantera's Solution object 288 | Cantera's Solution object of the detailed mechanism. 289 | species1 : Python List 290 | List object containing the species of first mech, e.g. mech.species_names. 291 | species2 : Python List 292 | List object containing the species of first mech, e.g. mech.species_names. 293 | 294 | Returns 295 | ------- 296 | species : Python List 297 | List of the species of the merged mechanism. 298 | reactions : Python List 299 | List of the reactions of the merged mechanism.. 300 | 301 | """ 302 | mech1 = set(species1) 303 | mech2 = set(species2) 304 | species = set.union(mech1,mech2) 305 | #recovery: grab all the reactions containing active species 306 | reactions = find_reactions_given_species(gasdtl,species) 307 | species = [gasdtl.species(name) for name in species] #convert to species object 308 | 309 | print('Merged mechanism contains %i species and %i reactions' % (len(species), len(reactions))) 310 | 311 | return species, reactions 312 | 313 | -------------------------------------------------------------------------------- /PyCSP/Solver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Jan 5 12:26:32 2021 5 | 6 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 7 | """ 8 | import sys 9 | import numpy as np 10 | import PyCSP.Functions as cspF 11 | import pandas as pd 12 | from PyCSP.profiling_tools import profile_cpu_time_and_count 13 | 14 | 15 | class CSPsolver: 16 | def __init__(self, gas): 17 | self._gas = gas 18 | self._y = [] 19 | self._t = [] 20 | self._Qs = [] 21 | self._Rc = [] 22 | self._dt = 1e+10 23 | self.csprtol = 1e-2 24 | self.cspatol = 1e-8 25 | self.jacobiantype = 'full' 26 | self._M = 0 27 | self.factor = 0.2 28 | 29 | self.integrate_time = 0.0 30 | self.integrate_n = 0 31 | self.CSPbasis_time = 0.0 32 | self.CSPbasis_n = 0 33 | self.calcM_time = 0.0 34 | self.calcM_n = 0 35 | self.RK4_time = 0.0 36 | self.RK4_n = 0 37 | self.Qs_time = 0.0 38 | self.Qs_n = 0 39 | 40 | @property 41 | def csprtol(self): 42 | return self._csprtol 43 | 44 | @csprtol.setter 45 | def csprtol(self,value): 46 | self._csprtol = value 47 | 48 | @property 49 | def cspatol(self): 50 | return self._cspatol 51 | 52 | @cspatol.setter 53 | def cspatol(self,value): 54 | self._cspatol = value 55 | 56 | @property 57 | def jacobiantype(self): 58 | return self._jacobiantype 59 | 60 | @jacobiantype.setter 61 | def jacobiantype(self,value): 62 | if value == 'full': 63 | self._jacobiantype = value 64 | else: 65 | raise ValueError("Invalid jacobian type --> %s" %value) 66 | 67 | @property 68 | def Qs(self): 69 | return self._Qs 70 | 71 | @property 72 | def Rc(self): 73 | return self._Rc 74 | 75 | @property 76 | def y(self): 77 | return self._y 78 | 79 | @property 80 | def t(self): 81 | return self._t 82 | 83 | @property 84 | def dt(self): 85 | return self._dt 86 | 87 | @property 88 | def M(self): 89 | return self._M 90 | 91 | @property 92 | def factor(self): 93 | return self._factor 94 | 95 | @factor.setter 96 | def factor(self,value): 97 | self._factor = value 98 | 99 | 100 | @profile_cpu_time_and_count("CSPbasis_time", "CSPbasis_n", log=False) 101 | def CSPcore(self,y): 102 | self._gas.set_stateYT(y) 103 | lam,A,B,f = self._gas.get_kernel() 104 | tau = cspF.timescales(lam) 105 | return [A,B,f,lam,tau] 106 | 107 | 108 | @profile_cpu_time_and_count("calcM_time", "calcM_n", log=False) 109 | def CSPexhaustedModes(self,y,lam,A,B,tau,rtol,atol): 110 | self._gas.set_stateYT(y) 111 | f = np.matmul(B,self._gas.source) 112 | M = cspF.findM(self._gas.n_elements,y,lam,A,tau,f,rtol,atol) 113 | return M 114 | 115 | def CSPamplitudes(self,y,B): 116 | f = np.matmul(B,self.rhs(y)) 117 | return f 118 | 119 | def rhs(self,y): 120 | self._gas.set_stateYT(y) 121 | dydt = np.zeros(self._gas.nv) 122 | dydt = self._gas.source 123 | return dydt 124 | 125 | def set_integrator(self,**kwargs): 126 | for key, value in kwargs.items(): 127 | if(key == 'cspRtol'): 128 | self.csprtol = value 129 | elif(key == 'cspAtol'): 130 | self.cspatol = value 131 | elif(key == 'factor'): 132 | self.factor = value 133 | elif(key == 'jacobiantype'): 134 | self.jacobiantype = value 135 | else: 136 | sys.exit('Unknown keyword in set_integrator') 137 | 138 | def set_initial_value(self,y0,t0): 139 | self._y = y0 140 | self._t = t0 141 | 142 | @profile_cpu_time_and_count("integrate_time", "integrate_n", log=False) 143 | def integrate(self): 144 | #calc CSP basis in yold 145 | self._gas.jacobiantype = self.jacobiantype 146 | yold = self.y 147 | A,B,f,lam,tau = self.CSPcore(yold) 148 | #apply radical correction and advance to yman 149 | self._Rc = RCorr(A,f,lam,self.M) 150 | yman = self.radical_correction() 151 | self._y = yman 152 | #calc new M 153 | self._M = self.CSPexhaustedModes(yman,lam,A,B,tau,self.csprtol,self.cspatol) 154 | #calc Projection matrix with old basis and new M 155 | self._Qs = self.projection_matrix(A,B,self.M) 156 | #advance in time dydt = Qsg with RK4 to ystar 157 | self._dt = smart_timescale(tau,self.factor,self.M,lam[self.M],self.dt) 158 | ystar = self.RK4csp() 159 | #calc Rc in ystar 160 | self._y = ystar 161 | f = self.CSPamplitudes(ystar,B) 162 | self._Rc = RCorr(A,f,lam,self.M) 163 | #apply radical correction and advance to ynew 164 | ynew = self.radical_correction() 165 | self._y = ynew 166 | self._t = self.t + self.dt 167 | 168 | 169 | @profile_cpu_time_and_count("RK4_time", "RK4_n", log=False) 170 | def RK4csp(self): 171 | def f(y): 172 | Qsg = np.matmul(self.Qs, self.rhs(y)) 173 | return Qsg 174 | yn = self.y 175 | h = self.dt 176 | k1 = np.array(f(yn)) 177 | k2 = np.array(f(yn + h/2.0 * k1)) 178 | k3 = np.array(f(yn + h/2.0 * k2)) 179 | k4 = np.array(f(yn + h * k3)) 180 | ynn = yn + h / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4) 181 | return ynn 182 | 183 | def radical_correction(self): 184 | ynew = self.y - self.Rc 185 | return ynew 186 | 187 | @profile_cpu_time_and_count("Qs_time", "Qs_n", log=False) 188 | def projection_matrix(self,A,B,M): 189 | return QsMatrix(A,B,M) 190 | 191 | 192 | def profiling(self): 193 | data = { 194 | "Function": ["CSP-solver Total", "Basis calculation", "M calculation", "RK4 solve", "Qs Matrix"], 195 | "Total Time (s)": [self.integrate_time, self.CSPbasis_time, self.calcM_time, self.RK4_time, self.Qs_time], 196 | "Time %": [self.integrate_time*100/self.integrate_time, self.CSPbasis_time*100/self.integrate_time, self.calcM_time*100/self.integrate_time, self.RK4_time*100/self.integrate_time, self.Qs_time*100/self.integrate_time], 197 | "Calls": [self.integrate_n,self.CSPbasis_n,self.calcM_n,self.RK4_n,self.Qs_n], 198 | "Time per Call (s)": [self.integrate_time/self.integrate_n, self.CSPbasis_time/self.CSPbasis_n if self.CSPbasis_n != 0 else 0, self.calcM_time/self.calcM_n, self.RK4_time/self.RK4_n, self.Qs_time/self.Qs_n], 199 | } 200 | 201 | # Create the DataFrame 202 | df = pd.DataFrame(data) 203 | 204 | extra_rows = [ 205 | {"Function": "Other", "Total Time (s)": self.integrate_time - (self.CSPbasis_time + self.calcM_time + self.RK4_time + self.Qs_time), "Time %": (self.integrate_time - (self.CSPbasis_time + self.calcM_time + self.RK4_time + self.Qs_time))*100/self.integrate_time, "Calls": self.integrate_n, "Time per Call (s)": (self.integrate_time - (self.CSPbasis_time + self.calcM_time + self.RK4_time + self.Qs_time))/self.integrate_n}, 206 | ] 207 | df = pd.concat([df, pd.DataFrame(extra_rows)], ignore_index=True) 208 | 209 | # Print the table 210 | print(df.to_string(index=False)) 211 | 212 | def QsMatrix(A,B,M): 213 | ns = A.shape[0] 214 | if(M > 0): 215 | QsMat = np.identity(ns) - sum( [np.outer(A[i],B[i]) for i in range(M)]) 216 | else: 217 | QsMat = np.identity(ns) 218 | return QsMat 219 | 220 | def RCorr(A,f,lam,M): 221 | ns = A.shape[0] 222 | Rc = np.zeros((ns)) 223 | if(M > 0): 224 | #fLamVec = f[0:M]*(1.0/lam[0:M].real) #linear approximation 225 | fLamVec = f[0:M]*(1 - np.exp((1.0/abs(lam[M].real))*lam[0:M].real) )/lam[0:M].real #exponential decay approximation 226 | Rc = np.matmul(np.transpose(A[0:M]), fLamVec) 227 | return Rc 228 | 229 | def smart_timescale(tau,factor,M,lamM,dtold): 230 | if(M==0): 231 | dt = tau[M]*factor 232 | else: 233 | dt = tau[M-1] + factor*(tau[M]-tau[M-1]) 234 | #if(lamM.real > 0): 235 | dt = min(dt,1.5*dtold) 236 | return dt 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /PyCSP/ThermoKinetics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 5 | """ 6 | import numpy as np 7 | import cantera as ct 8 | 9 | 10 | class CanteraThermoKinetics(ct.Solution): 11 | def __init__(self, *args, **kwargs): 12 | super().__init__(*args, **kwargs) 13 | 14 | self.constP = 0.0001 15 | self.constRho = 0.0001 16 | self._problemtype = 'unset' 17 | self._nv = self.n_species + 1 18 | self._source = [] 19 | self._jacobian = [] 20 | self._generalized_Stoich_matrix = [] 21 | self._R_vector = [] 22 | self._jacobian_diagonal = [] 23 | 24 | 25 | """ ~~~~~~~~~~~~ PROPERTIES ~~~~~~~~~~~~~ 26 | """ 27 | @property 28 | def problemtype(self): 29 | return self._problemtype 30 | 31 | 32 | @property 33 | def constP(self): 34 | if(self.problemtype != 'const_p'): 35 | raise ValueError("Constant pressure is unset") 36 | else: 37 | return self._constP 38 | 39 | @constP.setter 40 | def constP(self,value): 41 | if value > 0: 42 | self._constP = value 43 | self._problemtype = 'const_p' 44 | else: 45 | raise ValueError("Pressure must be positive") 46 | 47 | @property 48 | def constRho(self): 49 | if(self.problemtype != 'const_v'): 50 | raise ValueError("Constant density is unset") 51 | else: 52 | return self._constRho 53 | 54 | @constRho.setter 55 | def constRho(self,value): 56 | if value > 0: 57 | self._constRho = value 58 | self._problemtype = 'const_v' 59 | else: 60 | raise ValueError("Density must be positive") 61 | 62 | @property 63 | def nv(self): 64 | return self._nv 65 | 66 | @nv.setter 67 | def nv(self,value): 68 | if self.n_species <= value <= self.n_species+1: 69 | self._nv = value 70 | else: 71 | raise ValueError("Number of variables must be Ns or Ns+1") 72 | 73 | @property 74 | def source(self): 75 | if (self.problemtype == 'const_p'): 76 | return self.rhs_const_p() 77 | elif (self.problemtype == 'const_v'): 78 | return self.rhs_const_v() 79 | else: 80 | raise ValueError("Need to set either constP or constRho value") 81 | 82 | 83 | @property 84 | def jacobian(self): 85 | if (self.problemtype == 'const_p'): 86 | return self.jacobian_const_p() 87 | elif (self.problemtype == 'const_v'): 88 | return self.jacobian_const_v() 89 | else: 90 | raise ValueError("Need to set either constP or constRho value") 91 | 92 | @property 93 | def generalized_Stoich_matrix(self): 94 | if (self.problemtype == 'const_p'): 95 | return self.generalized_Stoich_matrix_const_p() 96 | elif (self.problemtype == 'const_v'): 97 | return self.generalized_Stoich_matrix_const_v() 98 | else: 99 | raise ValueError("Need to set either constP or constRho value") 100 | 101 | @property 102 | def R_vector(self): 103 | return self.Rates_vector() 104 | 105 | @property 106 | def jacobian_diagonal(self): 107 | if (self.problemtype == 'const_p'): 108 | return self.jacobian_diagonal_const_p() 109 | elif (self.problemtype == 'const_v'): 110 | return self.jacobian_diagonal_const_v() 111 | else: 112 | raise ValueError("Need to set either constP or constRho value") 113 | 114 | 115 | """ ~~~~~~~~~~~~ METHODS ~~~~~~~~~~~~~ 116 | """ 117 | 118 | 119 | """ ~~~~~~~~~~~~ state ~~~~~~~~~~~~~ 120 | """ 121 | 122 | def set_stateYT(self,y): 123 | if (self.problemtype == 'const_p'): 124 | return self.set_stateYT_const_p(y) 125 | elif (self.problemtype == 'const_v'): 126 | return self.set_stateYT_const_v(y) 127 | else: 128 | raise ValueError("Need to set either constP or constRho value") 129 | 130 | def stateYT(self): 131 | y = np.zeros(self.n_species+1) 132 | y[-1] = self.T 133 | y[0:-1] = self.Y 134 | return y 135 | 136 | 137 | def set_stateYT_const_p(self,y): 138 | self.Y = y[0:-1] 139 | self.TP = y[-1],self.constP 140 | 141 | def set_stateYT_const_v(self,y): 142 | self.Y = y[0:-1] 143 | self.TD = y[-1],self.constRho 144 | 145 | """ ~~~~~~~~~~~~ rhs ~~~~~~~~~~~~~ 146 | """ 147 | def rhs_const_p(self): 148 | """Computes chemical RHS [shape:(ns+1)] for a constant pressure reactor. 149 | Input must be an instance of the CSPCantera class""" 150 | 151 | ns = self.n_species 152 | ydot = np.zeros(ns+1) 153 | Wk = self.molecular_weights 154 | R = ct.gas_constant 155 | 156 | wdot = self.net_production_rates #[kmol/m^3/s] 157 | orho = 1./self.density 158 | 159 | ydot[-1] = - R * self.T * np.dot(self.standard_enthalpies_RT, wdot) * orho / self.cp_mass 160 | ydot[0:-1] = wdot * Wk * orho 161 | return ydot 162 | 163 | 164 | def rhs_const_v(self): 165 | """Computes chemical RHS [shape:(ns+1)] for a constant volume reactor. 166 | Input must be an instance of the CSPCantera class""" 167 | 168 | ns = self.n_species 169 | ydot = np.zeros(ns+1) 170 | Wk = self.molecular_weights 171 | R = ct.gas_constant 172 | 173 | wdot = self.net_production_rates #[kmol/m^3/s] 174 | orho = 1./self.density 175 | cp = self.cp_mass 176 | cv = self.cv_mass 177 | wmix = self.mean_molecular_weight 178 | 179 | gamma = cp / cv 180 | 181 | ydot[-1] = - R * self.T * np.dot(self.standard_enthalpies_RT, wdot) * gamma * orho / cp + ( (gamma - 1.0) * self.T * wmix * np.sum(wdot) * orho ) 182 | ydot[0:-1] = wdot * Wk * orho 183 | return ydot 184 | 185 | """ ~~~~~~~~~~~~ Stoichiometric matrix and Rates vector ~~~~~~~~~~~~~ 186 | """ 187 | 188 | def generalized_Stoich_matrix_const_p(self): 189 | """N_v x 2*N_r matrix containing the S components in column major format, 190 | such that S dot Rvec yields RHS""" 191 | nu_p = self.product_stoich_coeffs 192 | nu_r = self.reactant_stoich_coeffs 193 | rho = self.density 194 | numat = np.concatenate((nu_p-nu_r,nu_r-nu_p),axis=1) 195 | smat = np.vstack([numat[i] * self.molecular_weights[i] for i in range(self.n_species)])/rho 196 | #compute last row (temperature) of the matrix 197 | cp = self.cp_mass #[J/Kg K] 198 | hspec = self.standard_enthalpies_RT #non-dimensional 199 | Hspec = ct.gas_constant * self.T * hspec #[J/Kmol] 200 | smatT = np.sum([- numat[i] * Hspec[i] for i in range(self.n_species)],axis=0)/(rho*cp) 201 | Smat = np.vstack((smat,smatT)) 202 | return Smat[:self.nv] 203 | 204 | 205 | def generalized_Stoich_matrix_const_v(self): 206 | """N_v x 2*N_r matrix containing the S components in column major format, 207 | such that S dot Rvec yields RHS""" 208 | Wk = self.molecular_weights 209 | R = ct.gas_constant 210 | cp = self.cp_mass 211 | cv = self.cv_mass 212 | #wmix = 1.0/(np.dot(self.Y, np.reciprocal(Wk))) 213 | wmix = self.mean_molecular_weight 214 | gamma = cp / cv 215 | 216 | nu_p = self.product_stoich_coeffs 217 | nu_r = self.reactant_stoich_coeffs 218 | rho = self.density 219 | 220 | c1g = gamma/(rho*cp) 221 | c2g = (gamma-1.0)*self.T*wmix/rho 222 | 223 | numat = np.concatenate((nu_p-nu_r,nu_r-nu_p),axis=1) 224 | smat = np.vstack([numat[i] * Wk[i] for i in range(self.n_species)])/rho 225 | #compute last row (temperature) of the matrix 226 | cp = self.cp_mass #[J/Kg K] 227 | hspec = self.standard_enthalpies_RT #non-dimensional 228 | Hspec = R * self.T * hspec #[J/Kmol] 229 | smatT = np.sum([numat[i] * (-Hspec[i] * c1g + c2g) for i in range(self.n_species)],axis=0) 230 | Smat = np.vstack((smat,smatT)) 231 | return Smat[:self.nv] 232 | 233 | def Rates_vector(self): 234 | """ 2*Nr-long vector containing the rates of progress in [Kmol/m3/s]""" 235 | rvec = np.concatenate((self.forward_rates_of_progress,self.reverse_rates_of_progress)) 236 | return rvec 237 | 238 | """ ~~~~~~~~~~~~ jacobian ~~~~~~~~~~~~~ 239 | """ 240 | 241 | def jacobian_const_p(self): 242 | """Computes numerical Jacobian. 243 | Returns a N_s+1 x N_s+1 array [jac]. Input must be an instance of the CSPCantera class""" 244 | roundoff = np.finfo(float).eps 245 | sro = np.sqrt(roundoff) 246 | #setup the state vector 247 | T = self.T 248 | p = self.P 249 | y = self.Y.copy() #ns-long 250 | ydot = self.rhs_const_p() #ns+1-long (Y1,...,Yn,T) 251 | 252 | #create a jacobian vector 253 | jac2D = np.zeros((self.n_species+1, self.n_species+1)) 254 | 255 | #evaluate the Jacobian 256 | for i in range(self.n_species): 257 | dy = np.zeros(self.n_species) 258 | dy[i] = max(sro*abs(y[i]),1e-8) 259 | self.set_unnormalized_mass_fractions(y+dy) 260 | ydotp = self.rhs_const_p() 261 | dydot = ydotp-ydot 262 | jac2D[:,i] = dydot/dy[i] 263 | 264 | self.Y = y 265 | 266 | dT = max(sro*abs(T),1e-3) 267 | self.TP = T+dT,self.P 268 | ydotp = self.rhs_const_p() 269 | dydot = ydotp-ydot 270 | jac2D[:,-1] = dydot/dT 271 | 272 | self.TP = T,p 273 | 274 | return jac2D 275 | 276 | 277 | 278 | def jacobian_const_v(self): 279 | """Computes numerical Jacobian. 280 | Returns a N_s+1 x N_s+1 array [jac]. Input must be an instance of the CSPCantera class""" 281 | roundoff = np.finfo(float).eps 282 | sro = np.sqrt(roundoff) 283 | #setup the state vector 284 | T = self.T 285 | rho = self.density 286 | y = self.Y.copy() #ns-long 287 | ydot = self.rhs_const_v() #ns+1-long (Y1,...,Yn,T) 288 | 289 | #create a jacobian vector 290 | jac2D = np.zeros((self.n_species+1, self.n_species+1)) 291 | 292 | #evaluate the Jacobian 293 | for i in range(self.n_species): 294 | dy = np.zeros(self.n_species) 295 | dy[i] = max(sro*abs(y[i]),1e-8) 296 | self.set_unnormalized_mass_fractions(y+dy) 297 | ydotp = self.rhs_const_v() 298 | dydot = ydotp-ydot 299 | jac2D[:,i] = dydot/dy[i] 300 | 301 | self.Y = y 302 | 303 | dT = max(sro*abs(T),1e-3) 304 | self.TD = T+dT,rho 305 | ydotp = self.rhs_const_v() 306 | dydot = ydotp-ydot 307 | jac2D[:,-1] = dydot/dT 308 | 309 | self.TD = T,rho 310 | 311 | return jac2D 312 | 313 | 314 | 315 | def jac_contribution(self): 316 | """Computes contributions of each reaction to numerical Jacobian. 317 | Given that g = Sr = Sum_k S_k r^k, it follows that 318 | J(g) = Sum_k^(2nr) J_k, where J_k = Jac(S_k r^k) 319 | S_k r^k is the product of the k-th column of the matrix S and the k-th 320 | component of the vector r. 321 | Returns a list of 2*Nr (N_s+1 x N_s+1) arrays [jacK]. Input must be an instance of the CSPCantera class""" 322 | roundoff = np.finfo(float).eps 323 | sro = np.sqrt(roundoff) 324 | nv = self.nv 325 | ns = self.n_species 326 | nr = self.n_reactions 327 | #setup the state vector 328 | T = self.T 329 | y = self.Y #ns-long 330 | Smat = self.generalized_Stoich_matrix # ns x 2nr 331 | rvec = self.R_vector # 2nr-long 332 | 333 | 334 | Smatp = np.zeros((nv,nv,2*nr)) 335 | rvecp = np.zeros((nv,2*nr)) 336 | #evaluate Smat and Rvec in y+dy[i] 337 | for i in range(ns): 338 | dy = np.zeros(ns) 339 | dy[i] = max(sro*abs(y[i]),1e-8) 340 | self.set_unnormalized_mass_fractions(y+dy) 341 | Smatp[i] = self.generalized_Stoich_matrix 342 | rvecp[i] = self.R_vector 343 | 344 | if(nv==ns+1): 345 | self.Y = y #reset original Y 346 | dT = max(sro*abs(T),1e-3) 347 | self.TP = T+dT,self.P 348 | Smatp[-1] = self.generalized_Stoich_matrix 349 | rvecp[-1] = self.R_vector 350 | 351 | self.TP = T,self.P #reset original T,P 352 | 353 | 354 | JacK = np.zeros((2*nr,nv,nv)) 355 | #evaluate derivatives per each reaction 356 | for k in range(2*nr): 357 | jac2D = np.zeros((nv,nv)) 358 | for i in range(ns): 359 | dy = np.zeros(ns) 360 | dy[i] = max(sro*abs(y[i]),1e-8) 361 | ydotp = Smatp[i,:,k]*rvecp[i,k] 362 | ydot = Smat[:,k]*rvec[k] 363 | dydot = ydotp-ydot 364 | jac2D[:,i] = dydot/dy[i] 365 | 366 | if(nv==ns+1): 367 | ydotp = Smatp[-1,:,k]*rvecp[-1,k] 368 | ydot = Smat[:,k]*rvec[k] 369 | dydot = ydotp-ydot 370 | dT = max(sro*abs(T),1e-3) 371 | jac2D[:,-1] = dydot/dT 372 | 373 | JacK[k] = jac2D 374 | 375 | #to check for correctness, in main program: 376 | #jack = gas.jac_contribution() 377 | #jac=np.sum(jack,axis=0) 378 | #jacn = gas.jac_numeric() 379 | #np.allclose(jac,jacn,rtol=1e-8,atol=1e-12) 380 | 381 | return JacK 382 | 383 | 384 | def jacobian_diagonal_const_p(self): 385 | """Computes (an approx. to) the diagonal of the numerical Jacobian. 386 | Returns a N_s+1 array [diagjac]. Input must be an instance of the CSPCantera class""" 387 | roundoff = np.finfo(float).eps 388 | sro = np.sqrt(roundoff) 389 | #setup the state vector 390 | T = self.T 391 | p = self.P 392 | y = self.Y.copy() #ns-long 393 | ydot = self.rhs_const_p() #ns+1-long (Y1,...,Yn,T) 394 | 395 | #create a jacobian vector 396 | diagjac = np.zeros((self.n_species+1)) 397 | 398 | #evaluate the Jacobian 399 | dy = np.zeros(self.n_species) 400 | dy = [max(sro*abs(y[i]),1e-8) for i in range(self.n_species)] 401 | dT = max(sro*abs(T),1e-3) 402 | self.set_unnormalized_mass_fractions(y+dy) 403 | self.TP = T+dT,self.P 404 | ydotp = self.rhs_const_p() 405 | dydot = ydotp-ydot 406 | diagjac[:-1] = dydot[:-1]/dy 407 | diagjac[-1] = dydot[-1]/dT 408 | 409 | self.Y = y 410 | self.TP = T,p 411 | 412 | return diagjac 413 | 414 | 415 | 416 | def jacobian_diagonal_const_v(self): 417 | """Computes (an approx. to) the diagonal of the numerical Jacobian. 418 | Returns a N_s+1 [diagjac]. Input must be an instance of the CSPCantera class""" 419 | roundoff = np.finfo(float).eps 420 | sro = np.sqrt(roundoff) 421 | #setup the state vector 422 | T = self.T 423 | rho = self.density 424 | y = self.Y.copy() #ns-long 425 | ydot = self.rhs_const_v() #ns+1-long (Y1,...,Yn,T) 426 | 427 | #create a jacobian vector 428 | diagjac = np.zeros((self.n_species+1)) 429 | 430 | #evaluate the Jacobian 431 | dy = np.zeros(self.n_species) 432 | dy = [max(sro*abs(y[i]),1e-8) for i in range(self.n_species)] 433 | dT = max(sro*abs(T),1e-3) 434 | self.set_unnormalized_mass_fractions(y+dy) 435 | self.TD = T+dT,rho 436 | ydotp = self.rhs_const_p() 437 | dydot = ydotp-ydot 438 | diagjac[:-1] = dydot[:-1]/dy 439 | diagjac[-1] = dydot[-1]/dT 440 | 441 | self.Y = y 442 | self.TD = T,rho 443 | 444 | return diagjac 445 | 446 | """ ~~~~~~~~~~~~ OTHER JAC FORMULATIONS ~~~~~~~~~~~~~ 447 | """ 448 | 449 | def jacThermal(self): 450 | ns = self.n_species 451 | R = ct.gas_constant 452 | hspec = self.standard_enthalpies_RT 453 | Hspec = hspec * R * self.T 454 | Wk = self.molecular_weights 455 | cp = self.cp_mass 456 | TJrow = Hspec / ( Wk * cp) 457 | TJcol = self.jacobian[0:ns,-1] 458 | JacThermal = np.outer(TJcol,TJrow) 459 | return JacThermal 460 | 461 | def jacKinetic(self): 462 | ns = self.n_species 463 | jacKinetic = self.jacobian[0:ns,0:ns] 464 | return jacKinetic 465 | 466 | 467 | def jacSIM(self,Jac,major_ind,Levec): 468 | nv = len(Jac) 469 | n = len(major_ind) 470 | m = nv - n 471 | 472 | tot=[*range(0, nv, 1)] 473 | minor_ind = list(set(tot) - set(major_ind)) + list(set(major_ind) - set(tot)) 474 | mask = minor_ind + major_ind 475 | #print(mask) 476 | sortedJac = Jac[mask][:,mask] 477 | 478 | # Build Jacobian constrained to SIM 479 | 480 | #First term: dg_i/du_j (n-by-n) 481 | mat1 = sortedJac[m:nv,m:nv] 482 | 483 | #Second term: dg_i/dv_alpha (n-by-m) 484 | mat2 = sortedJac[m:nv,0:m] 485 | 486 | #Third term: (m-by-m) inverse of mat3 = b^i * dg/dx_j where j spans the fast variables 487 | mat3inv=np.linalg.inv( np.matmul(Levec[0:m],Jac[:][:,mask][:,0:m]) ) 488 | 489 | #Fourth term: (m-by-(n-m)) matrix of b^i * dg/dx_k where k spans the slow variables 490 | mat4 = np.matmul(Levec[0:m],Jac[:][:,mask][:,m:nv]) 491 | 492 | # Constrained (to SIM) jacobian: mat1 - mat2*mat3inv*mat4 493 | jacSIM = mat1 - np.matmul(mat2,np.matmul(mat3inv,mat4)) 494 | 495 | return jacSIM 496 | 497 | 498 | """ ~~~~~~~~~~~~ REAC NAMES ~~~~~~~~~~~~~ 499 | """ 500 | def reaction_names(self): 501 | nr = self.n_reactions 502 | rnames = self.reaction_equations() 503 | reacnames = np.zeros(2*nr,dtype=object) 504 | reacnames[0:nr] = ['(Rf-'+str(i+1)+') '+ s for s,i in zip(rnames,range(nr)) ] 505 | reacnames[nr:2*nr] = ['(Rb-'+str(i+1)+') '+ s for s,i in zip(rnames,range(nr))] 506 | return reacnames 507 | -------------------------------------------------------------------------------- /PyCSP/profiling_tools.py: -------------------------------------------------------------------------------- 1 | import time 2 | from functools import wraps 3 | 4 | def profile_cpu_time(attribute_name, log=False): 5 | """A decorator to profile the CPU time of a function.""" 6 | def decorator(func): 7 | @wraps(func) 8 | def wrapper(self, *args, **kwargs): 9 | start_time = time.process_time() 10 | result = func(self, *args, **kwargs) 11 | elapsed_time = time.process_time() - start_time 12 | if not hasattr(self, attribute_name): 13 | setattr(self, attribute_name, 0.0) 14 | setattr(self, attribute_name, getattr(self, attribute_name) + elapsed_time) 15 | if log: 16 | print(f"Function {func.__name__} took {elapsed_time:.6f} seconds.") 17 | return result 18 | return wrapper 19 | return decorator 20 | 21 | import time 22 | from functools import wraps 23 | 24 | def profile_cpu_time_and_count(time_attribute, count_attribute, log=False): 25 | """ 26 | A decorator to profile the CPU time and count the number of calls to a function. 27 | 28 | Args: 29 | time_attribute (str): The attribute name to store the total CPU time. 30 | count_attribute (str): The attribute name to store the call count. 31 | log (bool): Whether to log the time for each call. 32 | """ 33 | def decorator(func): 34 | @wraps(func) 35 | def wrapper(self, *args, **kwargs): 36 | # Start CPU time measurement 37 | start_time = time.process_time() 38 | 39 | # Execute the original function 40 | result = func(self, *args, **kwargs) 41 | 42 | # Stop CPU time measurement 43 | elapsed_time = time.process_time() - start_time 44 | 45 | # Update the total CPU time 46 | if not hasattr(self, time_attribute): 47 | setattr(self, time_attribute, 0.0) 48 | current_time = getattr(self, time_attribute) 49 | setattr(self, time_attribute, current_time + elapsed_time) 50 | 51 | # Update the call count 52 | if not hasattr(self, count_attribute): 53 | setattr(self, count_attribute, 0) 54 | current_count = getattr(self, count_attribute) 55 | setattr(self, count_attribute, current_count + 1) 56 | 57 | # Optionally log the time for this call 58 | if log: 59 | print(f"Function {func.__name__} call #{current_count + 1} took {elapsed_time:.6f} seconds.") 60 | 61 | return result 62 | return wrapper 63 | return decorator 64 | -------------------------------------------------------------------------------- /PyCSP/soln2cti.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://github.com/Niemeyer-Research-Group/pyMARS 3 | 4 | Copyright (c) 2016-2019 Parker Clayton, Phillip Mestas, and Kyle Niemeyer 5 | 6 | writes a solution object to a cantera cti file. 7 | currently only works for Elementary, Falloff, Plog and ThreeBody Reactions 8 | Cantera development version 2.3.0a2 required 9 | """ 10 | 11 | import os 12 | import math 13 | from textwrap import fill 14 | 15 | import cantera as ct 16 | 17 | # number of calories in 1000 Joules 18 | CALORIES_CONSTANT = 4184.0 19 | 20 | # Conversion from 1 debye to coulomb-meters 21 | DEBEYE_CONVERSION = 3.33564e-30 22 | 23 | indent = ['', 24 | ' ', 25 | ' ', 26 | ' ', 27 | ' ', 28 | ' ', 29 | ' ', 30 | ' ', 31 | ' ', 32 | ' ', 33 | ' ', 34 | ' ', 35 | ' ', 36 | ' ', 37 | ' ', 38 | ' ' 39 | ] 40 | 41 | 42 | def section_break(section_title): 43 | """Return string with break and new section title 44 | Parameters 45 | ---------- 46 | section_title : str 47 | title string for next section break 48 | 49 | Returns 50 | ------- 51 | str 52 | String with section title and breaks 53 | """ 54 | return('#' + '-' * 75 + '\n' + 55 | f'# {section_title}\n' + 56 | '#' + '-' * 75 + '\n\n' 57 | ) 58 | 59 | 60 | def build_arrhenius(rate, reaction_order, reaction_type): 61 | """Builds Arrhenius coefficient string based on reaction type. 62 | Parameters 63 | ---------- 64 | rate : cantera.Arrhenius 65 | Arrhenius-form reaction rate coefficient 66 | reaction_order : int or float 67 | Order of reaction (sum of reactant stoichiometric coefficients) 68 | reaction_type : {cantera.ElementaryReaction, cantera.ThreeBodyReaction, cantera.PlogReaction} 69 | Type of reaction 70 | Returns 71 | ------- 72 | str 73 | String with Arrhenius coefficients 74 | """ 75 | if reaction_type in [ct.ElementaryReaction, ct.PlogReaction]: 76 | pre_exponential_factor = rate.pre_exponential_factor * 1e3**(reaction_order - 1) 77 | 78 | elif reaction_type == ct.ThreeBodyReaction: 79 | pre_exponential_factor = rate.pre_exponential_factor * 1e3**reaction_order 80 | 81 | elif reaction_type in [ct.FalloffReaction, ct.ChemicallyActivatedReaction]: 82 | raise ValueError('Function does not support falloff or chemically activated reactions') 83 | else: 84 | raise NotImplementedError('Reaction type not supported: ', reaction_type) 85 | 86 | arrhenius = [f'{pre_exponential_factor:.6e}', 87 | str(rate.temperature_exponent), 88 | str(rate.activation_energy / CALORIES_CONSTANT) 89 | ] 90 | return ', '.join(arrhenius) 91 | 92 | 93 | def build_falloff_arrhenius(rate, reaction_order, reaction_type, pressure_limit): 94 | """Builds Arrhenius coefficient strings for falloff and chemically-activated reactions. 95 | Parameters 96 | ---------- 97 | rate : cantera.Arrhenius 98 | Arrhenius-form reaction rate coefficient 99 | reaction_order : int or float 100 | Order of reaction (sum of reactant stoichiometric coefficients) 101 | reaction_type : {ct.FalloffReaction, ct.ChemicallyActivatedReaction} 102 | Type of reaction 103 | pressure_limit : {'high', 'low'} 104 | string designating pressure limit 105 | 106 | Returns 107 | ------- 108 | str 109 | Arrhenius coefficient string 110 | """ 111 | assert pressure_limit in ['low', 'high'], 'Pressure range needs to be high or low' 112 | 113 | # Each needs more complicated handling due if high- or low-pressure limit 114 | if reaction_type == ct.FalloffReaction: 115 | if pressure_limit == 'low': 116 | pre_exponential_factor = rate.pre_exponential_factor * 1e3**(reaction_order) 117 | elif pressure_limit == 'high': 118 | pre_exponential_factor = rate.pre_exponential_factor * 1e3**(reaction_order - 1) 119 | 120 | elif reaction_type == ct.ChemicallyActivatedReaction: 121 | if pressure_limit == 'low': 122 | pre_exponential_factor = rate.pre_exponential_factor * 1e3**(reaction_order - 1) 123 | elif pressure_limit == 'high': 124 | pre_exponential_factor = rate.pre_exponential_factor * 1e3**(reaction_order - 2) 125 | else: 126 | raise ValueError('Reaction type not supported: ', reaction_type) 127 | 128 | arrhenius = [f'{pre_exponential_factor:.6E}', 129 | str(rate.temperature_exponent), 130 | str(rate.activation_energy / CALORIES_CONSTANT) 131 | ] 132 | return '[' + ', '.join(arrhenius) + ']' 133 | 134 | 135 | def build_falloff(parameters, falloff_function): 136 | """Creates falloff reaction Troe parameter string 137 | Parameters 138 | ---------- 139 | parameters : numpy.ndarray 140 | Array of falloff parameters; length varies based on ``falloff_function`` 141 | falloff_function : {'Troe', 'SRI'} 142 | Type of falloff function 143 | Returns 144 | ------- 145 | falloff_string : str 146 | String of falloff parameters 147 | """ 148 | if falloff_function == 'Troe': 149 | falloff_string = ('Troe(' + 150 | f'A = {parameters[0]}' + 151 | f', T3 = {parameters[1]}' + 152 | f', T1 = {parameters[2]}' + 153 | f', T2 = {parameters[3]})' 154 | ) 155 | elif falloff_function == 'SRI': 156 | falloff_string = ('SRI(' + 157 | f'A = {parameters[0]}' + 158 | f', B = {parameters[1]}' + 159 | f', C = {parameters[2]}' + 160 | f', D = {parameters[3]}' + 161 | f', E = {parameters[4]})' 162 | ) 163 | else: 164 | raise NotImplementedError(f'Falloff function not supported: {falloff_function}') 165 | 166 | return falloff_string 167 | 168 | 169 | def build_efficiencies(efficiencies, species_names, default_efficiency=1.0): 170 | """Creates line with list of third-body species efficiencies. 171 | Parameters 172 | ---------- 173 | efficiencies : dict 174 | Dictionary of species efficiencies 175 | species_names : dict of str 176 | List of all species names 177 | default_efficiency : float, optional 178 | Default efficiency for all species; will be 0.0 for reactions with explicit third body 179 | Returns 180 | ------- 181 | str 182 | Line with list of efficiencies 183 | """ 184 | # Reactions with a default_efficiency of 0 and a single entry in the efficiencies dict 185 | # have an explicit third body specified. 186 | if len(efficiencies) == 1 and not default_efficiency: 187 | return '' 188 | 189 | reduced_efficiencies = {s:efficiencies[s] for s in efficiencies if s in species_names} 190 | return ' '.join([f'{s}:{v}' for s, v in reduced_efficiencies.items()]) 191 | 192 | 193 | def write(solution, output_filename='', path=''): 194 | """Function to write cantera solution object to cti file. 195 | Parameters 196 | ---------- 197 | solution : cantera.Solution 198 | Model to be written 199 | output_filename : str, optional 200 | Name of file to be written; if not provided, use ``solution.name`` 201 | path : str, optional 202 | Path for writing file. 203 | Returns 204 | ------- 205 | output_filename : str 206 | Name of output model file (.cti) 207 | Examples 208 | -------- 209 | >>> gas = cantera.Solution('gri30.cti') 210 | >>> soln2cti.write(gas, 'copy_gri30.cti') 211 | copy_gri30.cti 212 | """ 213 | if output_filename: 214 | output_filename = os.path.join(path, output_filename) 215 | else: 216 | output_filename = os.path.join(path, f'{solution.name}.cti') 217 | 218 | if os.path.isfile(output_filename): 219 | os.remove(output_filename) 220 | 221 | with open(output_filename, 'w') as the_file: 222 | 223 | # Write title block to file 224 | the_file.write(section_break('CTI file converted from solution object')) 225 | the_file.write('units(length = "cm", time = "s",' + 226 | ' quantity = "mol", act_energy = "cal/mol")' + 227 | '\n\n' 228 | ) 229 | 230 | # Write Phase definition to file 231 | element_names = ' '.join(solution.element_names) 232 | species_names = fill( 233 | ' '.join(solution.species_names), 234 | width=55, 235 | subsequent_indent=19*' ', 236 | break_long_words=False, 237 | break_on_hyphens=False 238 | ) 239 | 240 | the_file.write( 241 | f'ideal_gas(name = "{os.path.splitext(os.path.basename(output_filename))[0]}", \n' + 242 | indent[5] + f'elements = "{element_names}", \n' + 243 | indent[5] + f'species = """ {species_names} """, \n' + 244 | indent[5] + f'reactions = "all", \n' + 245 | indent[5] + f'initial_state = state(temperature = {solution.T}, ' + 246 | f'pressure = {solution.P}) )\n\n' 247 | ) 248 | 249 | # Write species data to file 250 | the_file.write(section_break('Species data')) 251 | 252 | for species in solution.species(): 253 | # build strings with low- and high-temperature 7 NASA coefficients 254 | nasa_range_low = f'[{species.thermo.min_temp}, {species.thermo.coeffs[0]}]' 255 | nasa_coeffs_low = [f'{c:.9e}' for c in species.thermo.coeffs[8:15]] 256 | nasa_coeffs_low = fill( 257 | '[' + ', '.join(nasa_coeffs_low) + ']', 258 | width=50, 259 | subsequent_indent=16*' ', 260 | break_long_words=False, 261 | break_on_hyphens=False 262 | ) 263 | 264 | nasa_range_high = f'[{species.thermo.coeffs[0]}, {species.thermo.max_temp}]' 265 | nasa_coeffs_high = [f'{c:.9e}' for c in species.thermo.coeffs[1:8]] 266 | nasa_coeffs_high = fill( 267 | '[' + ', '.join(nasa_coeffs_high) + ']', 268 | width=50, 269 | subsequent_indent=16*' ', 270 | break_long_words=False, 271 | break_on_hyphens=False 272 | ) 273 | 274 | composition = ', '.join([f'{s}:{int(v)}' for s, v in species.composition.items()]) 275 | 276 | # start writing composition and thermo data 277 | species_string = ( 278 | f'species(name = "{species.name}",\n' + 279 | f'{indent[4]}atoms = "{composition}", \n' + 280 | f'{indent[4]}thermo = (\n' + 281 | f'{indent[7]}NASA( {nasa_range_low}, {nasa_coeffs_low} ),\n' + 282 | f'{indent[7]}NASA( {nasa_range_high}, {nasa_coeffs_high} )\n' + 283 | f'{indent[15]}),\n' 284 | ) 285 | 286 | #check if species has defined transport data, and write that if so 287 | if species.transport: 288 | species_string += ( 289 | f' transport = gas_transport(\n' + 290 | indent[15] + f'geom = "{species.transport.geometry}",\n' + 291 | indent[15] + f'diam = {species.transport.diameter * 1e10}, \n' + 292 | indent[15] + f'well_depth = {species.transport.well_depth / ct.boltzmann}, \n' + 293 | indent[15] + f'polar = {species.transport.polarizability * 1e30}, \n' + 294 | indent[15] + f'rot_relax = {species.transport.rotational_relaxation}' 295 | ) 296 | if species.transport.dipole != 0: 297 | dipole = species.transport.dipole / DEBEYE_CONVERSION 298 | species_string += ', \n' + indent[15] + f'dipole= {dipole}' 299 | species_string += ')\n' 300 | 301 | species_string += ' )\n\n' 302 | the_file.write(species_string) 303 | 304 | # Write reactions to file 305 | the_file.write(section_break('Reactions')) 306 | 307 | # write data for each reaction 308 | for idx, reaction in enumerate(solution.reactions()): 309 | 310 | reaction_string = f'# Reaction {idx + 1}\n' 311 | 312 | if type(reaction) == ct.ElementaryReaction: 313 | arrhenius = build_arrhenius(reaction.rate, 314 | sum(reaction.reactants.values()), 315 | ct.ElementaryReaction 316 | ) 317 | reaction_string += f'reaction( "{reaction.equation}", [{arrhenius}]' 318 | 319 | elif type(reaction) == ct.ThreeBodyReaction: 320 | arrhenius = build_arrhenius(reaction.rate, 321 | sum(reaction.reactants.values()), 322 | ct.ThreeBodyReaction 323 | ) 324 | reaction_string += f'three_body_reaction( "{reaction.equation}", [{arrhenius}]' 325 | 326 | # potentially trimmed efficiencies list 327 | efficiencies_str = build_efficiencies(reaction.efficiencies, solution.species_names) 328 | if efficiencies_str: 329 | reaction_string += f',\n{indent[9]}efficiencies = "{efficiencies_str}"' 330 | 331 | elif type(reaction) == ct.FalloffReaction: 332 | arrhenius_high = build_falloff_arrhenius( 333 | reaction.high_rate, sum(reaction.reactants.values()), 334 | ct.FalloffReaction, 'high' 335 | ) 336 | arrhenius_low = build_falloff_arrhenius( 337 | reaction.low_rate, sum(reaction.reactants.values()), 338 | ct.FalloffReaction, 'low' 339 | ) 340 | 341 | reaction_string += (f'falloff_reaction( "{reaction.equation}",\n' + 342 | f'{indent[9]}kf = {arrhenius_high},\n' + 343 | f'{indent[9]}kf0 = {arrhenius_low}' 344 | ) 345 | 346 | # need to print additional falloff parameters if present 347 | if reaction.falloff.parameters.size > 0: 348 | falloff_str = build_falloff(reaction.falloff.parameters, reaction.falloff.type) 349 | reaction_string += ',\n' + indent[9] + 'falloff = ' + falloff_str 350 | 351 | # potentially trimmed efficiencies list 352 | efficiencies_str = build_efficiencies( 353 | reaction.efficiencies, solution.species_names, reaction.default_efficiency 354 | ) 355 | if efficiencies_str: 356 | reaction_string += f',\n{indent[9]}efficiencies = "{efficiencies_str}"' 357 | 358 | elif type(reaction) == ct.ChemicallyActivatedReaction: 359 | arrhenius_high = build_falloff_arrhenius( 360 | reaction.high_rate, sum(reaction.reactants.values()), 361 | ct.ChemicallyActivatedReaction, 'high' 362 | ) 363 | arrhenius_low = build_falloff_arrhenius( 364 | reaction.low_rate, sum(reaction.reactants.values()), 365 | ct.ChemicallyActivatedReaction, 'low' 366 | ) 367 | 368 | reaction_string += (f'chemically_activated_reaction( "{reaction.equation}",\n' + 369 | f'{indent[15]} kLow = {arrhenius_low},\n' + 370 | f'{indent[15]} kHigh = {arrhenius_high}' 371 | ) 372 | 373 | # need to print additional falloff parameters if present 374 | if reaction.falloff.parameters.size > 0: 375 | falloff_str = build_falloff(reaction.falloff.parameters, reaction.falloff.type) 376 | reaction_string += ',\n' + indent[9] + 'falloff = ' + falloff_str 377 | 378 | # potentially trimmed efficiencies list 379 | efficiencies_str = build_efficiencies( 380 | reaction.efficiencies, solution.species_names, reaction.default_efficiency 381 | ) 382 | if efficiencies_str: 383 | reaction_string += f',\n{indent[8]} efficiencies = "{efficiencies_str}"' 384 | 385 | elif type(reaction) == ct.PlogReaction: 386 | reaction_string += f'pdep_arrhenius( "{reaction.equation}",\n' 387 | 388 | rates = [] 389 | for rate in reaction.rates: 390 | pressure = f'{rate[0] / ct.one_atm}' 391 | arrhenius = build_arrhenius(rate[1], 392 | sum(reaction.reactants.values()), 393 | ct.PlogReaction 394 | ) 395 | rates.append(f'{indent[15]}[({pressure}, "atm"), {arrhenius}]') 396 | # want to get the list of rates with a comma and newline between each entry, 397 | # but not at the end. 398 | reaction_string += ',\n'.join(rates) 399 | 400 | elif type(reaction) == ct.ChebyshevReaction: 401 | reaction_string += f'chebyshev_reaction( "{reaction.equation}",\n' 402 | 403 | # need to modify first coefficient 404 | coeffs = reaction.coeffs[:] 405 | coeffs[0][0] -= math.log10(1e-3 ** (sum(reaction.reactants.values()) - 1)) 406 | 407 | coeffs_strings = [] 408 | for coeff_row in coeffs: 409 | coeffs_strings.append('[' + ', '.join([f'{c:.6e}' for c in coeff_row]) + ']') 410 | coeffs_string = f',\n{indent[15] + indent[9]}'.join(coeffs_strings) 411 | 412 | reaction_string += ( 413 | f'{indent[15]} Tmin={reaction.Tmin}, Tmax={reaction.Tmax},\n' + 414 | f'{indent[15]} Pmin=({reaction.Pmin / ct.one_atm}, "atm"), Pmax=({reaction.Pmax / ct.one_atm}, "atm"),\n' + 415 | f'{indent[15]} coeffs=[{coeffs_string}]' 416 | ) 417 | 418 | else: 419 | raise NotImplementedError(f'Unsupported reaction type: {type(reaction)}') 420 | 421 | if reaction.duplicate: 422 | reaction_string += ',\n' + indent[8] + 'options = "duplicate"' 423 | 424 | reaction_string += ')\n\n' 425 | the_file.write(reaction_string) 426 | 427 | return output_filename -------------------------------------------------------------------------------- /PyCSP/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Wed Dec 23 17:51:37 2020 5 | 6 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 7 | """ 8 | 9 | import cantera as ct 10 | import numpy as np 11 | import PyCSP.Functions as csp 12 | 13 | 14 | def select_eval(evals,indexes): 15 | """ 16 | 17 | Parameters 18 | ---------- 19 | evals : 2D numpy array containing a series of eigenvalues. 20 | indexes : 1D numpy array with integer indexes (e.g. M). Note that indexes start from zero! 21 | 22 | Returns 23 | ------- 24 | eval : 1D numpy array with selected eigenvalues 25 | 26 | 27 | """ 28 | eval = evals[range(evals.shape[0]),indexes] 29 | return eval 30 | 31 | 32 | 33 | def reorder_evals(ev,times): 34 | """ 35 | 36 | Parameters 37 | ---------- 38 | evals : 2D numpy array containing a series of eigenvalues. 39 | times : 1D numpy array containing the corresponding times 40 | 41 | Returns 42 | ------- 43 | newev : 2D numpy array with continuous (along dim=0) eigenvalues 44 | 45 | 46 | """ 47 | 48 | evals = ev.real.copy() 49 | evalsd = ev.real.copy() 50 | img = ev.imag.copy() 51 | nstep = evals.shape[0] 52 | nv = evals.shape[1] 53 | delta = np.zeros(nv) 54 | mask = np.zeros((nstep,nv), dtype=np.int8) 55 | mask[0] = np.arange(nv) 56 | mask[1] = np.arange(nv) 57 | 58 | 59 | for i in range(2,nstep): 60 | for l in range(nv): 61 | 62 | f0 = evalsd[i-2,mask[i-2]] 63 | f1 = evalsd[i-1,mask[i-1]] 64 | h1 = times[i-1]-times[i-2] 65 | h2 = times[i]-times[i-1] 66 | 67 | for j in range(nv): 68 | delta[j] = np.abs( 2* (h2*f0[l] - (h1+h2)*f1[l] + h1*evals[i,j]) / ( h1*h2*(h1+h2)) ) 69 | k = np.argmin(delta) 70 | mask[i,l] = k 71 | evals[i,k] = 1.0e+30 72 | 73 | newev = np.zeros((nstep,nv),dtype=complex) 74 | for i in range(nstep): 75 | newev[i] = ev[i,mask[i]] 76 | return newev, mask 77 | 78 | 79 | def integrate_batch_constP(gas,temperature,pressure,eqratio,FuComp,OxComp,tend, rtol=1e-9, atol=1e-15): 80 | """ 81 | Integrates the constant-pressure batch reactor. 82 | 83 | Parameters: 84 | gas (ct.Solution): Cantera gas object. 85 | temperature (float): Initial temperature [K]. 86 | pressure (float): Initial pressure [Pa]. 87 | eqratio (float): Equivalence ratio. 88 | FuComp (str): Fuel composition. 89 | OxComp (str): Oxidizer composition. 90 | tend (float): End time for the simulation [s]. 91 | rtol (float): Relative tolerance for the integrator. 92 | atol (float): Absolute tolerance for the integrator. 93 | 94 | Returns: 95 | ct.SolutionArray: Solution array containing states and additional data. 96 | """ 97 | 98 | #set the gas state 99 | T = temperature 100 | P = pressure 101 | gas.TP = T, P 102 | gas.constP = P 103 | gas.set_equivalence_ratio(eqratio,FuComp,OxComp) 104 | 105 | 106 | #integrate ODE 107 | r = ct.IdealGasConstPressureReactor(gas) 108 | sim = ct.ReactorNet([r]) 109 | sim.rtol = rtol 110 | sim.atol = atol 111 | states = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'rhsT': [0.0]}) 112 | 113 | sim.initial_time = 0.0 114 | while sim.time < tend: 115 | sim.step() 116 | states.append(r.thermo.state, t=sim.time, rhsT=gas.source[-1]) 117 | return states 118 | 119 | 120 | def integrate_batch_constV(gas,temperature,pressure,eqratio,FuComp,OxComp,tend, rtol=1e-9, atol=1e-15): 121 | """ 122 | Integrates the constant-volume batch reactor. 123 | 124 | Parameters: 125 | gas (ct.Solution): Cantera gas object. 126 | temperature (float): Initial temperature [K]. 127 | pressure (float): Initial pressure [Pa]. 128 | eqratio (float): Equivalence ratio. 129 | FuComp (str): Fuel composition. 130 | OxComp (str): Oxidizer composition. 131 | tend (float): End time for the simulation [s]. 132 | rtol (float): Relative tolerance for the integrator. 133 | atol (float): Absolute tolerance for the integrator. 134 | 135 | Returns: 136 | ct.SolutionArray: Solution array containing states and additional data. 137 | """ 138 | #set the gas state 139 | T = temperature 140 | P = pressure 141 | gas.TP = T, P 142 | rho = gas.density 143 | gas.constRho = rho 144 | gas.set_equivalence_ratio(eqratio,FuComp,OxComp) 145 | 146 | 147 | #integrate ODE 148 | r = ct.IdealGasReactor(gas) 149 | sim = ct.ReactorNet([r]) 150 | sim.rtol = rtol 151 | sim.atol = atol 152 | states = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'rhsT': [0.0]}) 153 | 154 | sim.initial_time = 0.0 155 | while sim.time < tend: 156 | sim.step() 157 | states.append(r.thermo.state, t=sim.time, rhsT=gas.source[-1]) 158 | return states 159 | 160 | def find_duplicate_reactions(R): 161 | """ 162 | 163 | Parameters 164 | ---------- 165 | R : an instance of a Reaction object, e.g.: 166 | R = ct.Reaction.list_from_file("gri30.yaml", gas) 167 | R = ct.Reaction.listFromCti(open("path/to/gri30.cti").read()) 168 | R = ct.Reaction.listFromXml(open("path/to/gri30.xml").read()) 169 | 170 | Returns 171 | ------- 172 | duplicates : a list of lists of duplicate reactions 173 | 174 | """ 175 | duplicates = [] 176 | for id,reac in zip(range(len(R)),R): 177 | if reac.duplicate: 178 | listdup = [idx for idx,r in zip(range(len(R)),R) if r.duplicate and reac.equation==r.equation] 179 | if listdup not in duplicates: 180 | duplicates.append(listdup) 181 | return duplicates -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyCSP 2 | ![Screenshot](logo.png) 3 | A collection of tools based on Computational Singular Perturbation for the analysis of chemically reacting systems. 4 | Requires cantera >= 3.0, numpy, matplotlib. (for older versions of Cantera, i.e. Cantera>=2.5, download release v1.2.1) 5 | git-lfs is needed to correctly download the example datasets. 6 | 7 | Installation in a new environment called "pycsp" with Anaconda (suggested, oterwise skip to #3): 8 | From PyCSP folder 9 | 1) conda create --name pycsp anaconda --file requirements.txt --channel default --channel anaconda --channel cantera 10 | 2) conda activate pycsp 11 | 3) pip install $PATH_TO_PyCSP_MAIN_FOLDER (e.g. pip install /Users/rmalpica/PyCSP) 12 | 13 | 14 | Testing: 15 | 1) enter the folder tests/ 16 | 2) run the command "python test_kernel.py" 17 | 18 | Several examples are available in the Examples folder to test the functionalities related to: 19 | - exhausted modes (M) 20 | - tangential stretching rate (TSR) 21 | - CSP and TSR indices (importance indices, amplitude and timescale participatio indices, TSR amplitude/timescale participation indices) 22 | 23 | Cantera chem-input files are required (.cti or .yaml, depending on the installed Cantera version). Cantera offers a utility to convert chemkin-format files into cantera-format: https://cantera.org/tutorials/ck2cti-tutorial.html 24 | 25 | FluidFoam (https://github.com/fluiddyn/fluidfoam) is suggested as a direct interface with openFOAM data. 26 | 27 | # Warning 28 | Datasets in the tsrAnalysis example folder are quite heavy. Due to limited github bandwidth, it may happen that "flamelet_state.dat" and "flamelet_rhsDiff.dat" are not correctly checked out. In that case, please write me an e-mail (riccardo.malpicagalassi [at] uniroma1.it). I will send you the files. 29 | 30 | # Documentation 31 | Can be found in the /documentation folder 32 | 33 | # How to cite? 34 | This code has an associated publication. In addition to mentioning this GitHub repository (see below), I would be grateful if you could cite the publication: 35 | - Malpica Galassi, R., PyCSP: a Python package for the analysis and simplification of chemically reacting systems based on Computational Singular Perturbation, Computer Physics Communications, 2022, https://doi.org/10.1016/j.cpc.2022.108364. 36 | (https://www.sciencedirect.com/science/article/pii/S0010465522000832) 37 | 38 | BibTex citation of this publication: 39 | ```bibtex 40 | @article{MALPICAGALASSI2022108364, 41 | title = {PyCSP: a Python package for the analysis and simplification of chemically reacting systems based on Computational Singular Perturbation}, 42 | journal = {Computer Physics Communications}, 43 | pages = {108364}, 44 | year = {2022}, 45 | issn = {0010-4655}, 46 | doi = {https://doi.org/10.1016/j.cpc.2022.108364}, 47 | url = {https://www.sciencedirect.com/science/article/pii/S0010465522000832}, 48 | author = {Riccardo {Malpica Galassi}} 49 | } 50 | 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /documentation/PyCSP_documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmalpica/PyCSP/1817a59bb44250fa15f97273e77e8ab4bb7e950c/documentation/PyCSP_documentation.pdf -------------------------------------------------------------------------------- /examples/cspAnalysis/JacobianSIM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import matplotlib.colors as clr 9 | import PyCSP.Functions as csp 10 | import PyCSP.ThermoKinetics as ctt 11 | 12 | 13 | #create gas from original mechanism file hydrogen.cti 14 | gas = csp.CanteraCSP('hydrogen.yaml') 15 | 16 | #set the gas state 17 | T = 1000 18 | P = ct.one_atm 19 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 20 | gas.TP = T, P 21 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 22 | 23 | #push pressure 24 | gas.constP = P 25 | 26 | #edit CSP parameters 27 | gas.jacobiantype='full' 28 | gas.rtol=1.0e-3 29 | gas.atol=1.0e-10 30 | 31 | #very important for SIM-constrained Jac 32 | gas.classify_traces=False 33 | 34 | #integrate ODE 35 | r = ct.IdealGasConstPressureReactor(gas) 36 | sim = ct.ReactorNet([r]) 37 | time = 0.0 38 | states = ct.SolutionArray(gas, extra=['t']) 39 | 40 | 41 | evals = [] 42 | evalsSIM = [] 43 | 44 | 45 | sim.initial_time = 0.0 46 | while sim.time < 0.001: 47 | sim.step() 48 | states.append(r.thermo.state, t=sim.time) 49 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 50 | lam,R,L,f = gas.get_kernel() 51 | api, tpi, ifast, islow, species_type = gas.calc_CSPindices(API=False,Impo=False,species_type=True,TPI=False) 52 | major = [i for i, x in enumerate(species_type) if x == "slow"] 53 | 54 | jacSIM = gas.jacSIM(gas.jac,major,L) 55 | lamSIM = np.zeros(len(lam)) 56 | lSIM,RSIM,LSIM = csp.eigsys(jacSIM) 57 | lamSIM[:len(lSIM)]=lSIM 58 | 59 | evals.append(lam) 60 | evalsSIM.append(lamSIM) 61 | 62 | 63 | 64 | evals = np.array(evals) 65 | evalsSIM = np.array(evalsSIM) 66 | 67 | #plot eigenvalues of constrained model 68 | #evalM = utils.select_eval(evalsFull,MFull) 69 | logevals = np.clip(np.log10(1.0+np.abs(evals)),0,100)*np.sign(evals.real) 70 | #logevalM = np.clip(np.log10(1.0+np.abs(evalM)),0,100)*np.sign(evalM.real) 71 | logevalsC = np.clip(np.log10(1.0+np.abs(evalsSIM)),0,100)*np.sign(evalsSIM.real) 72 | print('plotting eigenvalues of full and constrained model...') 73 | fig, ax = plt.subplots(figsize=(6,4)) 74 | for idx in range(evals.shape[1]): 75 | ax.plot(states.t, logevals[:,idx], color='black', marker='.', markersize = 5,linestyle = 'None') 76 | for idx in range(evalsSIM.shape[1]): 77 | ax.plot(states.t, logevalsC[:,idx], color='red', marker='.', markersize = 2,linestyle = 'None') 78 | #ax.plot(time, logevalM, color='orange', marker='.', markersize = 3,linestyle = 'None', label='lam(M+1) rtol e-2; atol e-8') 79 | ax.set_xlabel('time (s)') 80 | ax.set_ylabel('evals') 81 | ax.set_ylim([-9, 6]) 82 | ax.set_xlim([0., max(states.t)]) 83 | ax.grid(False) 84 | #ax.legend() 85 | plt.savefig('jacobianSIM.png',dpi=800) 86 | 87 | -------------------------------------------------------------------------------- /examples/cspAnalysis/amplitudes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmalpica/PyCSP/1817a59bb44250fa15f97273e77e8ab4bb7e950c/examples/cspAnalysis/amplitudes.png -------------------------------------------------------------------------------- /examples/cspAnalysis/eigenvalues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmalpica/PyCSP/1817a59bb44250fa15f97273e77e8ab4bb7e950c/examples/cspAnalysis/eigenvalues.png -------------------------------------------------------------------------------- /examples/cspAnalysis/exhM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmalpica/PyCSP/1817a59bb44250fa15f97273e77e8ab4bb7e950c/examples/cspAnalysis/exhM.png -------------------------------------------------------------------------------- /examples/cspAnalysis/exhaustedModes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as csp 9 | import PyCSP.utils as utils 10 | 11 | #create gas from original mechanism file hydrogen.cti 12 | gas = csp.CanteraCSP('hydrogen.yaml') 13 | 14 | #set the gas state 15 | T = 1000 16 | P = ct.one_atm 17 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 18 | gas.TP = T, P 19 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 20 | 21 | #push pressure 22 | gas.constP = P 23 | #set jacobiantype 24 | gas.jacobiantype = 'full' 25 | 26 | #integrate ODE 27 | r = ct.IdealGasConstPressureReactor(gas) 28 | sim = ct.ReactorNet([r]) 29 | sim.rtol=1.0e-12 30 | sim.atol=1.0e-14 31 | time = 0.0 32 | states = ct.SolutionArray(gas, extra=['t']) 33 | 34 | 35 | evals = [] 36 | Revec = [] 37 | Levec = [] 38 | fvec = [] 39 | Mr2a8 = [] 40 | Mr3a9 = [] 41 | Mr4a1 = [] 42 | 43 | sim.initial_time = 0.0 44 | while sim.time < 0.001: 45 | sim.step() 46 | states.append(r.thermo.state, t=sim.time) 47 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 48 | lam,R,L,f = gas.get_kernel() 49 | NofDM28 = gas.calc_exhausted_modes(rtol=1.0e-2,atol=1.0e-8) 50 | NofDM39 = gas.calc_exhausted_modes(rtol=1.0e-3,atol=1.0e-9) 51 | NofDM41 = gas.calc_exhausted_modes(rtol=1.0e-4,atol=1.0e-10) 52 | evals.append(lam) 53 | Revec.append(R) 54 | Levec.append(L) 55 | fvec.append(f) 56 | Mr2a8.append(NofDM28) 57 | Mr3a9.append(NofDM39) 58 | Mr4a1.append(NofDM41) 59 | 60 | evals = np.array(evals) 61 | Revec = np.array(Revec) 62 | Levec = np.array(Levec) 63 | fvec = np.array(fvec) 64 | Mr2a8 = np.array(Mr2a8) 65 | Mr3a9 = np.array(Mr3a9) 66 | Mr4a1 = np.array(Mr4a1) 67 | 68 | 69 | #plot solution 70 | print('plotting ODE solution...') 71 | plt.clf() 72 | plt.subplot(2, 2, 1) 73 | plt.plot(states.t, states.T) 74 | plt.xlabel('Time (s)') 75 | plt.ylabel('Temperature (K)') 76 | plt.xlim(0., 0.002) 77 | plt.subplot(2, 2, 2) 78 | plt.plot(states.t, states.X[:,gas.species_index('OH')]) 79 | plt.xlabel('Time (s)') 80 | plt.ylabel('OH Mole Fraction') 81 | plt.xlim(0., 0.002) 82 | plt.subplot(2, 2, 3) 83 | plt.plot(states.t, states.X[:,gas.species_index('H')]) 84 | plt.xlabel('Time (s)') 85 | plt.ylabel('H Mole Fraction') 86 | plt.xlim(0., 0.002) 87 | plt.subplot(2, 2, 4) 88 | plt.plot(states.t, states.X[:,gas.species_index('H2')]) 89 | plt.xlabel('Time (s)') 90 | plt.ylabel('H2 Mole Fraction') 91 | plt.xlim(0., 0.002) 92 | plt.tight_layout() 93 | plt.show(block = False) 94 | plt.savefig('traj.png', dpi=500, transparent=False) 95 | 96 | #plot eigenvalues and lambda_M+1 97 | evalMr2a8 = utils.select_eval(evals,Mr2a8) 98 | evalMr3a9 = utils.select_eval(evals,Mr3a9) 99 | evalMr4a1 = utils.select_eval(evals,Mr4a1) 100 | logevals = np.clip(np.log10(1.0+np.abs(evals)),0,100)*np.sign(evals.real) 101 | logevalMr2a8 = np.clip(np.log10(1.0+np.abs(evalMr2a8.real)),0,100)*np.sign(evalMr2a8.real) 102 | logevalMr3a9 = np.clip(np.log10(1.0+np.abs(evalMr3a9.real)),0,100)*np.sign(evalMr3a9.real) 103 | logevalMr4a1 = np.clip(np.log10(1.0+np.abs(evalMr4a1.real)),0,100)*np.sign(evalMr4a1.real) 104 | print('plotting eigenvalues...') 105 | fig, ax = plt.subplots(figsize=(6,4)) 106 | for idx in range(evals.shape[1]): 107 | #color = next(ax._get_lines.prop_cycler)['color'] 108 | ax.plot(states.t, logevals[:,idx], color='black', marker='.', markersize = 5,linestyle = 'None') 109 | ax.plot(states.t, logevalMr2a8, color='orange', marker='.', markersize = 4,linestyle = 'None', label='lam(M+1) rtol e-2; atol e-8') 110 | ax.plot(states.t, logevalMr3a9, color='blue', marker='.', markersize = 3,linestyle = 'None', label='lam(M+1) rtol e-3; atol e-9') 111 | ax.plot(states.t, logevalMr4a1, color='red', marker='.', markersize = 2,linestyle = 'None', label='lam(M+1) rtol e-4; atol e-10') 112 | ax.set_xlabel('time (s)') 113 | ax.set_ylabel('evals') 114 | ax.set_ylim([-9, 6]) 115 | ax.set_xlim([0., 0.001]) 116 | ax.grid(False) 117 | ax.legend() 118 | plt.show(block = False) 119 | plt.savefig('eigenvalues.png', dpi=500, transparent=False) 120 | 121 | 122 | #plot exhausted modes 123 | print('plotting exhausted modes...') 124 | fig, ax = plt.subplots(figsize=(6,4)) 125 | #ax.plot(states.t, M, color='black') 126 | ax.plot(Mr2a8, color='orange', label='rtol e-2; atol e-8') 127 | ax.plot(Mr3a9, color='red', label='rtol e-3; atol e-9') 128 | ax.plot(Mr4a1, color='blue', label='rtol e-4; atol e-10') 129 | #ax.set_xlabel('time (s)') 130 | ax.set_xlabel('# timestep') 131 | ax.set_ylabel('M') 132 | ax.set_ylim([0,10]) 133 | #ax.set_xlim([0., 0.001]) 134 | ax.grid(False) 135 | ax.legend() 136 | plt.show(block = False) 137 | plt.savefig('exhM.png', dpi=500, transparent=False) 138 | 139 | #plot amplitudes 140 | print('plotting mode amplitudes...') 141 | fig, ax = plt.subplots(figsize=(6,4)) 142 | for idx in range(fvec.shape[1]): 143 | #color = next(ax._get_lines.prop_cycler)['color'] 144 | ax.plot(np.log10(1e-10+fvec[:,idx]), label='Mode %d' %(idx+1), marker='.', markersize = 2,linestyle = 'None') 145 | ax.set_xlabel('time (s)') 146 | ax.set_ylabel('Amplitude') 147 | ax.set_ylim([-8, 10]) 148 | #ax.set_xlim([0., 0.001]) 149 | ax.grid(False) 150 | ax.legend() 151 | plt.show(block = False) 152 | plt.savefig('amplitudes.png', dpi=500, transparent=False) 153 | -------------------------------------------------------------------------------- /examples/cspAnalysis/hydrogen.yaml: -------------------------------------------------------------------------------- 1 | description: |- 2 | "" 3 | 4 | generator: cti2yaml 5 | cantera-version: 3.0.0 6 | date: Mon, 11 Sep 2023 10:01:55 +0200 7 | input-files: [hydrogen.cti] 8 | 9 | units: {length: cm, quantity: mol, activation-energy: cal/mol} 10 | 11 | phases: 12 | - name: gas 13 | thermo: ideal-gas 14 | elements: [H, O, N] 15 | species: [H2, O2, O, OH, H2O, H, HO2, H2O2, N2] 16 | kinetics: gas 17 | reactions: all 18 | state: 19 | T: 300.0 20 | P: 1.01325e+05 21 | 22 | species: 23 | - name: H2 24 | composition: {H: 2} 25 | thermo: 26 | model: NASA7 27 | temperature-ranges: [300.0, 1000.0, 5000.0] 28 | data: 29 | - [3.298124, 8.249441e-04, -8.143015e-07, -9.475434e-11, 4.134872e-13, 30 | -1012.5209, -3.294094] 31 | - [2.991423, 7.000644e-04, -5.633828e-08, -9.231578e-12, 1.5827519e-15, 32 | -835.034, -1.3551101] 33 | note: '121286' 34 | - name: O2 35 | composition: {O: 2} 36 | thermo: 37 | model: NASA7 38 | temperature-ranges: [300.0, 1000.0, 5000.0] 39 | data: 40 | - [3.212936, 1.1274864e-03, -5.75615e-07, 1.3138773e-09, -8.768554e-13, 41 | -1005.249, 6.034737] 42 | - [3.697578, 6.135197e-04, -1.258842e-07, 1.775281e-11, -1.1364354e-15, 43 | -1233.9301, 3.189165] 44 | note: '121386' 45 | - name: O 46 | composition: {O: 1} 47 | thermo: 48 | model: NASA7 49 | temperature-ranges: [300.0, 1000.0, 5000.0] 50 | data: 51 | - [2.946428, -1.6381665e-03, 2.421031e-06, -1.6028431e-09, 3.890696e-13, 52 | 2.914764e+04, 2.963995] 53 | - [2.542059, -2.755061e-05, -3.102803e-09, 4.551067e-12, -4.368051e-16, 54 | 2.92308e+04, 4.920308] 55 | note: '120186' 56 | - name: OH 57 | composition: {H: 1, O: 1} 58 | thermo: 59 | model: NASA7 60 | temperature-ranges: [300.0, 1000.0, 5000.0] 61 | data: 62 | - [3.637266, 1.85091e-04, -1.6761646e-06, 2.387202e-09, -8.431442e-13, 63 | 3606.781, 1.3588605] 64 | - [2.88273, 1.0139743e-03, -2.276877e-07, 2.174683e-11, -5.126305e-16, 65 | 3886.888, 5.595712] 66 | note: '121286' 67 | - name: H2O 68 | composition: {H: 2, O: 1} 69 | thermo: 70 | model: NASA7 71 | temperature-ranges: [300.0, 1000.0, 5000.0] 72 | data: 73 | - [3.386842, 3.474982e-03, -6.354696e-06, 6.968581e-09, -2.506588e-12, 74 | -3.020811e+04, 2.590232] 75 | - [2.672145, 3.056293e-03, -8.73026e-07, 1.2009964e-10, -6.391618e-15, 76 | -2.989921e+04, 6.862817] 77 | note: '20387' 78 | - name: H 79 | composition: {H: 1} 80 | thermo: 81 | model: NASA7 82 | temperature-ranges: [300.0, 1000.0, 5000.0] 83 | data: 84 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 85 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 86 | note: '120186' 87 | - name: HO2 88 | composition: {H: 1, O: 2} 89 | thermo: 90 | model: NASA7 91 | temperature-ranges: [300.0, 1000.0, 5000.0] 92 | data: 93 | - [2.979963, 4.996697e-03, -3.790997e-06, 2.354192e-09, -8.089024e-13, 94 | 176.2273, 9.222724] 95 | - [4.072191, 2.131296e-03, -5.308145e-07, 6.112269e-11, -2.841164e-15, 96 | -157.9727, 3.476029] 97 | note: '20387' 98 | - name: H2O2 99 | composition: {H: 2, O: 2} 100 | thermo: 101 | model: NASA7 102 | temperature-ranges: [300.0, 1000.0, 5000.0] 103 | data: 104 | - [3.388753, 6.569226e-03, -1.4850125e-07, -4.625805e-09, 2.471514e-12, 105 | -1.766314e+04, 6.785363] 106 | - [4.573167, 4.336136e-03, -1.4746888e-06, 2.348903e-10, -1.4316536e-14, 107 | -1.800696e+04, 0.5011369] 108 | note: '120186' 109 | - name: N2 110 | composition: {N: 2} 111 | thermo: 112 | model: NASA7 113 | temperature-ranges: [300.0, 1000.0, 5000.0] 114 | data: 115 | - [3.298677, 1.4082404e-03, -3.963222e-06, 5.641515e-09, -2.444854e-12, 116 | -1020.8999, 3.950372] 117 | - [2.92664, 1.4879768e-03, -5.68476e-07, 1.0097038e-10, -6.753351e-15, 118 | -922.7977, 5.980528] 119 | note: '121286' 120 | 121 | reactions: 122 | - equation: H + O2 <=> O + OH # Reaction 1 123 | rate-constant: {A: 1.915e+14, b: 0.0, Ea: 1.644e+04} 124 | - equation: O + H2 <=> H + OH # Reaction 2 125 | rate-constant: {A: 5.08e+04, b: 2.67, Ea: 6290.0} 126 | - equation: H2 + OH <=> H2O + H # Reaction 3 127 | rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} 128 | - equation: OH + OH <=> O + H2O # Reaction 4 129 | rate-constant: {A: 1.23e+04, b: 2.62, Ea: -1880.0} 130 | - equation: H2 + M <=> H + H + M # Reaction 5 131 | type: three-body 132 | rate-constant: {A: 4.577e+19, b: -1.4, Ea: 1.044e+05} 133 | efficiencies: {H2: 2.5, H2O: 12.0} 134 | - equation: O + O + M <=> O2 + M # Reaction 6 135 | type: three-body 136 | rate-constant: {A: 6.165e+15, b: -0.5, Ea: 0.0} 137 | efficiencies: {H2: 2.5, H2O: 12.0} 138 | - equation: O + H + M <=> OH + M # Reaction 7 139 | type: three-body 140 | rate-constant: {A: 4.714e+18, b: -1.0, Ea: 0.0} 141 | efficiencies: {H2: 2.5, H2O: 12.0} 142 | - equation: H + OH + M <=> H2O + M # Reaction 8 143 | type: three-body 144 | rate-constant: {A: 2.24e+22, b: -2.0, Ea: 0.0} 145 | efficiencies: {H2: 2.5, H2O: 6.3} 146 | - equation: H + O2 + M <=> HO2 + M # Reaction 9 147 | type: three-body 148 | rate-constant: {A: 6.17e+19, b: -1.42, Ea: 0.0} 149 | efficiencies: {H2: 2.5, H2O: 12.0} 150 | - equation: HO2 + H <=> H2 + O2 # Reaction 10 151 | rate-constant: {A: 6.63e+13, b: 0.0, Ea: 2130.0} 152 | - equation: HO2 + H <=> OH + OH # Reaction 11 153 | rate-constant: {A: 1.69e+14, b: 0.0, Ea: 874.0} 154 | - equation: HO2 + O <=> O2 + OH # Reaction 12 155 | rate-constant: {A: 1.81e+13, b: 0.0, Ea: -400.0} 156 | - equation: HO2 + OH <=> H2O + O2 # Reaction 13 157 | rate-constant: {A: 1.45e+16, b: -1.0, Ea: 0.0} 158 | - equation: HO2 + HO2 <=> H2O2 + O2 # Reaction 14 159 | rate-constant: {A: 3.02e+12, b: 0.0, Ea: 1390.0} 160 | - equation: H2O2 + M <=> OH + OH + M # Reaction 15 161 | type: three-body 162 | rate-constant: {A: 1.202e+17, b: 0.0, Ea: 4.55e+04} 163 | efficiencies: {H2: 2.5, H2O: 12.0} 164 | - equation: H2O2 + H <=> H2O + OH # Reaction 16 165 | rate-constant: {A: 1.0e+13, b: 0.0, Ea: 3590.0} 166 | - equation: H2O2 + H <=> HO2 + H2 # Reaction 17 167 | rate-constant: {A: 4.82e+13, b: 0.0, Ea: 7950.0} 168 | - equation: H2O2 + O <=> OH + HO2 # Reaction 18 169 | rate-constant: {A: 9.55e+06, b: 2.0, Ea: 3970.0} 170 | - equation: H2O2 + OH <=> HO2 + H2O # Reaction 19 171 | rate-constant: {A: 7.0e+12, b: 0.0, Ea: 1430.0} 172 | -------------------------------------------------------------------------------- /examples/cspAnalysis/indices-integral.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import matplotlib.colors as clr 9 | import PyCSP.Functions as csp 10 | 11 | 12 | #create gas from original mechanism file hydrogen.cti 13 | gas = csp.CanteraCSP('hydrogen.yaml') 14 | 15 | #set the gas state 16 | T = 1000 17 | P = ct.one_atm 18 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 19 | gas.TP = T, P 20 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 21 | 22 | #push pressure 23 | gas.constP = P 24 | 25 | #edit CSP parameters 26 | gas.jacobiantype='full' 27 | gas.rtol=1.0e-3 28 | gas.atol=1.0e-10 29 | 30 | gas.index_norm='none' #needed for integral formulation 31 | 32 | 33 | #integrate ODE 34 | r = ct.IdealGasConstPressureReactor(gas) 35 | sim = ct.ReactorNet([r]) 36 | time = 0.0 37 | states = ct.SolutionArray(gas, extra=['t']) 38 | 39 | 40 | API = [] 41 | TPI = [] 42 | Ifast = [] 43 | Islow = [] 44 | classify = [] 45 | TSRAPI = [] 46 | TSRTPI = [] 47 | 48 | 49 | sim.initial_time = 0.0 50 | while sim.time < 0.001: 51 | sim.step() 52 | states.append(r.thermo.state, t=sim.time) 53 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 54 | api, tpi, ifast, islow, species_type = gas.calc_CSPindices(API=True,Impo=True,species_type=True,TPI=False) 55 | tsrapi = gas.calc_TSRindices(type='amplitude') 56 | tsrtpi = gas.calc_TSRindices(type='timescale') 57 | API.append(api) 58 | TPI.append(tpi) 59 | Ifast.append(ifast) 60 | Islow.append(islow) 61 | classify.append(species_type) 62 | TSRAPI.append(tsrapi) 63 | TSRTPI.append(tsrtpi) 64 | 65 | 66 | API = np.array(API) 67 | TPI = np.array(TPI) 68 | Ifast = np.array(Ifast) 69 | Islow = np.array(Islow) 70 | classify = np.array(classify) 71 | TSRAPI = np.array(TSRAPI) 72 | TSRTPI = np.array(TSRTPI) 73 | 74 | 75 | grid = states.t 76 | 77 | #Islow 78 | integral = np.reshape([np.trapezoid(np.abs(Islow[:,i,j]), x=grid) for i in range(gas.nv) for j in range(2*gas.n_reactions)],(gas.nv,2*gas.n_reactions)) 79 | norm = np.sum(np.abs(integral),axis=1) 80 | IslowIntegralnorm = np.transpose(np.divide(np.transpose(integral), norm, out=np.zeros_like(np.transpose(integral)), where=norm!=0)) 81 | 82 | #Ifast 83 | integral = np.reshape([np.trapezoid(np.abs(Ifast[:,i,j]), x=grid) for i in range(gas.nv) for j in range(2*gas.n_reactions)],(gas.nv,2*gas.n_reactions)) 84 | norm = np.sum(np.abs(integral),axis=1) 85 | IfastIntegralnorm = np.transpose(np.divide(np.transpose(integral), norm, out=np.zeros_like(np.transpose(integral)), where=norm!=0)) 86 | 87 | #TSRAPI 88 | integral = np.array([np.trapezoid(np.abs(TSRAPI[:,j]), x=grid) for j in range(2*gas.n_reactions)]) 89 | norm = np.sum(np.abs(integral)) 90 | TSRAPIIntegralnorm = np.transpose(np.divide(np.transpose(integral), norm, out=np.zeros_like(np.transpose(integral)), where=norm!=0)) 91 | 92 | 93 | spec = -1 94 | thr = 0.05 95 | indextype = IslowIntegralnorm 96 | indexname='Islow integral' 97 | idx = np.argsort(-indextype,axis=1)[spec] 98 | values=indextype[spec][idx][indextype[spec][idx] >= thr] 99 | names=gas.reaction_names()[idx][indextype[spec][idx] >= thr] 100 | y_pos = np.arange(len(values)) 101 | labels=[str(names[i]) for i in range(len(names))] 102 | varnames = np.array(gas.species_names+['Temperature']) 103 | 104 | plt.rcdefaults() 105 | fig, ax = plt.subplots(figsize = (8, 5)) 106 | ax.barh(y_pos, values, align='center') 107 | ax.set_yticks(y_pos) 108 | ax.set_yticklabels(labels) 109 | ax.invert_yaxis() # labels read top-to-bottom 110 | ax.set_xlabel(indexname) 111 | ax.set_title(varnames[spec]) 112 | plt.subplots_adjust(left=0.4) 113 | 114 | plt.savefig('IslowTint.png', dpi=800, transparent=False) 115 | plt.show() 116 | -------------------------------------------------------------------------------- /examples/cspAnalysis/indices.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib as mpl 8 | import matplotlib.pyplot as plt 9 | import itertools 10 | import PyCSP.Functions as csp 11 | 12 | 13 | #create gas from original mechanism file hydrogen.cti 14 | gas = csp.CanteraCSP('hydrogen.yaml') 15 | 16 | #set the gas state 17 | T = 1000 18 | P = ct.one_atm 19 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 20 | gas.TP = T, P 21 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 22 | 23 | #push pressure 24 | gas.constP = P 25 | 26 | #edit CSP parameters 27 | gas.jacobiantype='full' 28 | gas.rtol=1.0e-3 29 | gas.atol=1.0e-10 30 | 31 | 32 | #integrate ODE 33 | r = ct.IdealGasConstPressureReactor(gas) 34 | sim = ct.ReactorNet([r]) 35 | time = 0.0 36 | states = ct.SolutionArray(gas, extra=['t']) 37 | 38 | 39 | API = [] 40 | TPI = [] 41 | Ifast = [] 42 | Islow = [] 43 | classify = [] 44 | TSRAPI = [] 45 | TSRTPI = [] 46 | 47 | 48 | sim.initial_time = 0.0 49 | while sim.time < 0.001: 50 | sim.step() 51 | states.append(r.thermo.state, t=sim.time) 52 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 53 | api, tpi, ifast, islow, species_type = gas.calc_CSPindices(API=True,Impo=True,species_type=True,TPI=False) 54 | tsrapi = gas.calc_TSRindices(type='amplitude') 55 | tsrtpi = gas.calc_TSRindices(type='timescale') 56 | API.append(api) 57 | TPI.append(tpi) 58 | Ifast.append(ifast) 59 | Islow.append(islow) 60 | classify.append(species_type) 61 | TSRAPI.append(tsrapi) 62 | TSRTPI.append(tsrtpi) 63 | 64 | 65 | API = np.array(API) 66 | TPI = np.array(TPI) 67 | Ifast = np.array(Ifast) 68 | Islow = np.array(Islow) 69 | classify = np.array(classify) 70 | TSRAPI = np.array(TSRAPI) 71 | TSRTPI = np.array(TSRTPI) 72 | 73 | 74 | def plot(grid,cspindex,thr,gas,outname): 75 | 76 | print('plotting indices...') 77 | 78 | l_styles = ['-','--','-.',':'] 79 | m_styles = ['','.','o','^','*'] 80 | colormap = mpl.cm.Dark2.colors # Qualitative colormap 81 | 82 | gridIdx = np.argsort(grid) 83 | reacIdx = np.unique(np.nonzero(np.abs(cspindex) > thr)[1]) #indexes of reactions with TSRapi > thr 84 | reac = cspindex[:,reacIdx][gridIdx] 85 | 86 | fig, ax = plt.subplots(figsize=(8,4)) 87 | for idx,(marker,linestyle,color) in zip(range(len(reacIdx)),itertools.product(m_styles,l_styles, colormap)): 88 | plt.plot(grid[gridIdx], reac[:,idx], color=color, linestyle=linestyle,marker=marker,label=gas.reaction_names()[reacIdx[idx]]) 89 | plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) 90 | 91 | ax.set_xlabel('time [s]') 92 | ax.set_ylabel('Index') 93 | ax.grid(False) 94 | box = ax.get_position() 95 | ax.set_position([box.x0, box.y0, box.width * 0.6, box.height]) 96 | ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) 97 | plt.show(block = False) 98 | plt.savefig(outname, dpi=800, transparent=False) 99 | 100 | 101 | spec = -1 102 | thr = 0.2 103 | plot(states.t,Islow[:,spec,:],thr,gas,'IslowTemp.png') 104 | plot(states.t,Ifast[:,spec,:],thr,gas,'IfastTemp.png') 105 | 106 | spec = gas.species_names.index('H2O') 107 | plot(states.t,Islow[:,spec,:],thr,gas,'IslowH2O.png') 108 | plot(states.t,Ifast[:,spec,:],thr,gas,'IfastH2O.png') 109 | 110 | plot(states.t,TSRAPI[:,:],thr,gas,'TSRAPI.png') 111 | plot(states.t,TSRTPI[:,:],thr,gas,'TSRTPI.png') 112 | -------------------------------------------------------------------------------- /examples/cspAnalysis/pointers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as csp 9 | import PyCSP.utils as utils 10 | 11 | species_to_be_pointed = 'NO' 12 | threshold_on_pointer_magnitude = 0.05 #picks only modes whose pointer is larger than this value 13 | 14 | #create gas from original mechanism file hydrogen.cti 15 | gas = csp.CanteraCSP('gri30.yaml') 16 | 17 | #set the gas state 18 | T = 1000 19 | P = ct.one_atm 20 | gas.TP = T, P 21 | gas.set_equivalence_ratio(1.0, 'CH4', 'O2:1, N2:3.76') 22 | 23 | #push pressure 24 | gas.constP = P 25 | 26 | #integrate ODE 27 | r = ct.IdealGasConstPressureReactor(gas) 28 | sim = ct.ReactorNet([r]) 29 | sim.rtol=1.0e-12 30 | sim.atol=1.0e-14 31 | time = 0.0 32 | states = ct.SolutionArray(gas, extra=['t']) 33 | 34 | 35 | evals = [] 36 | pointed_modes = [] 37 | pointer_values = [] 38 | 39 | sim.initial_time = 0.0 40 | while sim.time < 10: 41 | sim.step() 42 | states.append(r.thermo.state, t=sim.time) 43 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 44 | lam,R,L,f = gas.get_kernel() 45 | pointers = csp.CSP_pointers(R,L) 46 | pointed = [i for i, pointer in enumerate(pointers) if np.abs(pointer[gas.species_index(species_to_be_pointed)]) > threshold_on_pointer_magnitude ] 47 | ptrs_values = pointers[pointed,gas.species_index(species_to_be_pointed)] 48 | evals.append(lam) 49 | pointed_modes.append(pointed) 50 | pointer_values.append(ptrs_values) 51 | 52 | evals = np.array(evals) 53 | pointed_modes = np.array(pointed_modes, dtype=object) 54 | pointer_values = np.array(pointer_values, dtype=object) 55 | pointed_evals = [evals[i, idx_list] for i, idx_list in enumerate(pointed_modes)] 56 | 57 | # Apply the log transformation to each array in pointed_evals 58 | log_pointed_evals = [np.clip(np.log10(1.0 + np.abs(values.real)), 0, 100) * np.sign(values.real) for values in pointed_evals] 59 | 60 | # Convert pointer_values to ensure it is a flat, numeric array 61 | pointer_values = np.array([val[0] if isinstance(val, (list, np.ndarray)) else val for val in pointer_values], dtype=float) 62 | 63 | # Flattening 64 | flat_log_pointed_evals = np.concatenate(log_pointed_evals) 65 | flat_time_values = np.repeat(states.t, [len(values) for values in log_pointed_evals]) 66 | flat_color_values = np.repeat(pointer_values, [len(values) for values in log_pointed_evals]) 67 | 68 | # Plot with color shading based on `flat_color_values` 69 | plt.figure(figsize=(10, 6)) 70 | sc = plt.scatter(flat_time_values, flat_log_pointed_evals, c=np.abs(flat_color_values), cmap="viridis", alpha=0.6) 71 | 72 | plt.xlim(1, 1.2) 73 | plt.xlabel("Time") 74 | plt.ylabel("Log Evals") 75 | plt.title("Evals pointing %s" %species_to_be_pointed) 76 | 77 | # Add a colorbar 78 | plt.colorbar(sc, label="Pointer Magnitude") 79 | plt.show() -------------------------------------------------------------------------------- /examples/cspAnalysis/subspaces.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as csp 9 | import PyCSP.utils as utils 10 | 11 | #create gas from original mechanism file hydrogen.cti 12 | gas = csp.CanteraCSP('gri30.yaml') 13 | 14 | #set the gas state 15 | T = 1000 16 | P = ct.one_atm 17 | gas.TP = T, P 18 | gas.set_equivalence_ratio(1.0, 'CH4', 'O2:1, N2:3.76') 19 | 20 | #push pressure 21 | gas.constP = P 22 | 23 | #integrate ODE 24 | r = ct.IdealGasConstPressureReactor(gas) 25 | sim = ct.ReactorNet([r]) 26 | sim.rtol=1.0e-12 27 | sim.atol=1.0e-14 28 | time = 0.0 29 | states = ct.SolutionArray(gas, extra=['t']) 30 | 31 | 32 | evals = [] 33 | Revec = [] 34 | Levec = [] 35 | fvec = [] 36 | Tail = [] 37 | Head = [] 38 | 39 | sim.initial_time = 0.0 40 | while sim.time < 10: 41 | sim.step() 42 | states.append(r.thermo.state, t=sim.time) 43 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 44 | lam,R,L,f = gas.get_kernel() 45 | M, H = gas.calc_subspaces(rtolTail=1.0e-3,atolTail=1.0e-9,rtolHead=1.0e-3,atolHead=1.0e-9) 46 | evals.append(lam) 47 | Revec.append(R) 48 | Levec.append(L) 49 | fvec.append(f) 50 | Tail.append(M) 51 | Head.append(H) 52 | 53 | evals = np.array(evals) 54 | Revec = np.array(Revec) 55 | Levec = np.array(Levec) 56 | fvec = np.array(fvec) 57 | Tail = np.array(Tail) 58 | Head = np.array(Head) 59 | 60 | 61 | 62 | #plot solution 63 | print('plotting ODE solution...') 64 | plt.clf() 65 | plt.subplot(2, 2, 1) 66 | plt.plot(states.t, states.T) 67 | plt.xlabel('Time (s)') 68 | plt.ylabel('Temperature (K)') 69 | plt.xlim(1., 1.15) 70 | plt.subplot(2, 2, 2) 71 | plt.plot(states.t, states.X[:,gas.species_index('OH')]) 72 | plt.xlabel('Time (s)') 73 | plt.ylabel('OH Mole Fraction') 74 | plt.xlim(1., 1.15) 75 | plt.subplot(2, 2, 3) 76 | plt.plot(states.t, states.X[:,gas.species_index('H')]) 77 | plt.xlabel('Time (s)') 78 | plt.ylabel('H Mole Fraction') 79 | plt.xlim(1., 1.15) 80 | plt.subplot(2, 2, 4) 81 | plt.plot(states.t, states.X[:,gas.species_index('CH4')]) 82 | plt.xlabel('Time (s)') 83 | plt.ylabel('CH4 Mole Fraction') 84 | plt.xlim(1., 1.15) 85 | plt.tight_layout() 86 | plt.show(block = False) 87 | plt.savefig('traj.png', dpi=500, transparent=False) 88 | 89 | #plot eigenvalues and lambda_M+1 90 | evalM = utils.select_eval(evals,Tail) 91 | evalH = utils.select_eval(evals,Head-1) 92 | logevals = np.clip(np.log10(1.0+np.abs(evals)),0,100)*np.sign(evals.real) 93 | logevalM = np.clip(np.log10(1.0+np.abs(evalM.real)),0,100)*np.sign(evalM.real) 94 | logevalH = np.clip(np.log10(1.0+np.abs(evalH.real)),0,100)*np.sign(evalH.real) 95 | print('plotting eigenvalues...') 96 | fig, ax = plt.subplots(figsize=(6,4)) 97 | for idx in range(evals.shape[1]): 98 | #color = next(ax._get_lines.prop_cycler)['color'] 99 | ax.plot(states.t, logevals[:,idx], color='black', marker='.', markersize = 5,linestyle = 'None') 100 | ax.plot(states.t, logevalM, color='orange', marker='.', markersize = 4,linestyle = 'None', label='lam(M+1)') 101 | ax.plot(states.t, logevalH, color='blue', marker='.', markersize = 3,linestyle = 'None', label='lam(H)') 102 | ax.set_xlabel('time (s)') 103 | ax.set_ylabel('evals') 104 | ax.set_ylim([-9, 6]) 105 | ax.set_xlim([1, 1.15]) 106 | ax.grid(False) 107 | ax.legend() 108 | plt.show(block = False) 109 | plt.savefig('eigenvalues_subspaces.png', dpi=500, transparent=False) 110 | 111 | from matplotlib.patches import Rectangle 112 | fig, ax = plt.subplots(figsize=(6,4)) 113 | tailplot=ax.plot(states.t, Tail, color='orange', label='M+1')[0] 114 | headplot=ax.plot(states.t, Head, color='blue', label='H')[0] 115 | ax.fill_between(states.t, Tail, Head, color='deepskyblue', alpha=0.5) 116 | shade=Rectangle((0, 0), 1, 1, fc="deepskyblue", alpha=0.5, label='active modes') 117 | ax.set_xlabel('time (s)') 118 | ax.set_ylabel('modes') 119 | ax.set_xlim([1, 1.15]) 120 | ax.set_ylim([0, 54]) 121 | plt.ticklabel_format(axis="x", style="sci", scilimits=(0,0)) 122 | plt.legend([tailplot, headplot, shade],['tail','head','active'],fontsize=7) 123 | #plt.show() 124 | fig.savefig('modes.png', dpi=800, transparent=False) 125 | 126 | fig, ax = plt.subplots(figsize=(6,4)) 127 | ax.plot(states.t, Head - Tail,color='deepskyblue' ) 128 | ax.set_xlabel('time (s)') 129 | ax.set_ylabel('dim(Active)') 130 | ax.set_xlim([1, 1.15]) 131 | ax.set_ylim([0, 54]) 132 | plt.ticklabel_format(axis="x", style="sci", scilimits=(0,0)) 133 | #plt.show() 134 | fig.savefig('dim_Active.png', dpi=800, transparent=False) -------------------------------------------------------------------------------- /examples/cspAnalysis/traj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmalpica/PyCSP/1817a59bb44250fa15f97273e77e8ab4bb7e950c/examples/cspAnalysis/traj.png -------------------------------------------------------------------------------- /examples/cspSimplify/simp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import matplotlib.colors as clr 9 | import PyCSP.Functions as csp 10 | import PyCSP.Simplify as simp 11 | 12 | 13 | #-------CREATE DATASET--------- 14 | #create gas from original mechanism file hydrogen.cti 15 | dtl_mech = csp.CanteraCSP('gri30.yaml') 16 | 17 | #set the gas state 18 | T = 1000 19 | P = ct.one_atm 20 | dtl_mech.TP = T, P 21 | dtl_mech.constP = P 22 | dtl_mech.set_equivalence_ratio(1.0, 'CH4', 'O2:1, N2:3.76') 23 | 24 | 25 | #integrate ODE with detailed mech 26 | r = ct.IdealGasConstPressureReactor(dtl_mech) 27 | sim = ct.ReactorNet([r]) 28 | time = 0.0 29 | states = ct.SolutionArray(dtl_mech, 1, extra={'t': [0.0], 'rhsT': [0.0]}) 30 | 31 | sim.initial_time = 0.0 32 | while sim.time < 1000: 33 | sim.step() 34 | states.append(r.thermo.state, t=sim.time, rhsT=dtl_mech.source[-1]) 35 | #print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 36 | 37 | dataset = np.concatenate((states.Y,states.T[:,np.newaxis],states.P[:,np.newaxis]),axis=1) 38 | print('Dataset created, start processing...') 39 | 40 | #coarsen dataset (to speed up) 41 | dataset = dataset[::64] 42 | 43 | #init simplifier 44 | simplifier = simp.CSPsimplify(dtl_mech,dataset) 45 | 46 | #simplifier settings 47 | simplifier.targetset = {'CH4','O2','N2','HCO','H2O','CO2'} 48 | simplifier.problemtype = 'constP' 49 | simplifier.scaled = True 50 | simplifier.csprtol = 1.0e-2 51 | simplifier.cspatol = 1.0e-8 52 | 53 | simplifier.dataset_info() 54 | 55 | #process dataset 56 | simplifier.process_dataset() 57 | 58 | print('Done processing') 59 | 60 | print('Start pruning...may take a while') 61 | #loop over thresholds 62 | thr = np.arange(0.01, 1, 0.01) 63 | 64 | simp_mech = [] 65 | prev_species = dtl_mech.species_names 66 | for i in range(len(thr)): 67 | species, reactions = simplifier.simplify_mechanism(thr[i]) 68 | simp = csp.CanteraCSP(thermo='ideal-gas', kinetics='gas', species=species, reactions=reactions) 69 | if simp.species_names != prev_species: #append only if different from previous one 70 | simp_mech.append(simp) 71 | prev_species = simp.species_names 72 | 73 | nmech = len(simp_mech) 74 | print('%i mechanisms found' % (nmech)) 75 | 76 | all_states_simp = [] 77 | for i in range(nmech): 78 | # Re-run the ignition problem with the simplified mechanisms 79 | simp_mech[i].TP = T,P 80 | simp_mech[i].constP = P 81 | simp_mech[i].set_equivalence_ratio(1.0, 'CH4', 'O2:1, N2:3.76') 82 | r = ct.IdealGasConstPressureReactor(simp_mech[i]) 83 | sim = ct.ReactorNet([r]) 84 | states_simp = ct.SolutionArray(simp_mech[i], 1, extra={'t': [0.0], 'rhsT': [0.0]}) 85 | 86 | sim.initial_time = 0.0 87 | while sim.time < 1000: 88 | sim.step() 89 | states_simp.append(r.thermo.state, t=sim.time, rhsT=simp_mech[i].source[-1]) 90 | all_states_simp.append(states_simp) 91 | 92 | 93 | 94 | C = [np.random.choice(list(clr.CSS4_COLORS.values())) for i in range(nmech)] 95 | fig, ax = plt.subplots(figsize=(6,4)) 96 | ax.plot(states.t, states.T, lw=2, color='b', label='Detailed') 97 | for i in range(nmech): 98 | ax.plot(all_states_simp[i].t, all_states_simp[i].T, lw=2, color = C[i], 99 | label='M#{0}, Ns={1}, Nr={2}'.format(i, simp_mech[i].n_species, simp_mech[i].n_reactions)) 100 | ax.set_xlabel('Time (s)') 101 | ax.set_ylabel('Temperature (K)') 102 | ax.legend(loc='lower right') 103 | ax.set_xlim([0, 2]) 104 | plt.savefig('ignition.png', dpi=800, transparent=False) 105 | 106 | dtl_idt = states.t[states.rhsT.argmax()] 107 | simp_idt = [all_states_simp[i].t[all_states_simp[i].rhsT.argmax()] for i in range(nmech)] 108 | err_idt = abs((simp_idt-dtl_idt)/dtl_idt)*100 109 | 110 | fig, ax = plt.subplots(figsize=(6,4)) 111 | ax.plot([simp_mech[i].n_species for i in range(nmech)], err_idt, color='red', marker='.') 112 | ax.set_xlabel('# of species') 113 | ax.set_ylabel('ignition delay time relative Error [%]') 114 | ax.set_yscale('log') 115 | ax.set_ylim([1e-3, 1e2]) 116 | plt.savefig('ign_delay_error.png', dpi=800, transparent=False) 117 | 118 | 119 | -------------------------------------------------------------------------------- /examples/cspSimplify/simp_TSR.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import os 6 | import cantera as ct 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | import PyCSP.Functions as csp 10 | import PyCSP.Simplify as simp 11 | import PyCSP.utils as utils 12 | #from PyCSP.soln2cti import write 13 | 14 | parent_mech = 'gri30.yaml' 15 | 16 | FuComp = 'CH4' 17 | OxComp = 'O2:1, N2:3.76' 18 | 19 | #-------CREATE DATASET--------- 20 | #create gas from original mechanism file hydrogen.cti 21 | dtl_mech = csp.CanteraCSP(parent_mech) 22 | 23 | T_list = [1000] 24 | phi_list = [1.0] 25 | P_list = [ct.one_atm] 26 | 27 | 28 | states_db = [] 29 | for eqratio in phi_list: 30 | for temperature in T_list: 31 | for pressure in P_list: 32 | # Re-run the ignition problem with the detailed mechanisms 33 | states = utils.integrate_batch_constP(dtl_mech,temperature,pressure,eqratio,FuComp,OxComp,1000) 34 | states_db.append(states) 35 | 36 | allT = np.concatenate([states_db[i].T for i in range(len(states_db))]) 37 | allY = np.concatenate([states_db[i].Y for i in range(len(states_db))]) 38 | allP = np.concatenate([states_db[i].P for i in range(len(states_db))]) 39 | 40 | dataset = np.concatenate((allY,allT[:,np.newaxis],allP[:,np.newaxis]),axis=1) 41 | print('Dataset created, start processing...') 42 | 43 | #coarsen dataset (to speed up) 44 | datasetc = dataset[::32] 45 | print('Number of states in dataset: %i' %len(datasetc)) 46 | 47 | #init simplifier 48 | simplifier = simp.CSPsimplify(dtl_mech,datasetc) 49 | 50 | #simplifier settings 51 | simplifier.TSRtargetset = True 52 | simplifier.TSRtol = 0.5 53 | simplifier.targetset = {'N2','AR'} 54 | simplifier.problemtype = 'constP' 55 | simplifier.scaled = False 56 | simplifier.csprtol = 1.0e-2 57 | simplifier.cspatol = 1.0e-8 58 | 59 | simplifier.dataset_info() 60 | 61 | #process dataset 62 | simplifier.process_dataset() 63 | 64 | print('Done processing') 65 | 66 | print('Start pruning...may take a while') 67 | #loop over thresholds 68 | thr = np.arange(0.01, 1, 0.01) 69 | 70 | simp_mech = [] 71 | prev_species = sorted(dtl_mech.species_names) 72 | for i in range(len(thr)): 73 | species, reactions = simplifier.simplify_mechanism(thr[i]) 74 | simp = csp.CanteraCSP(thermo='ideal-gas', kinetics='gas', species=species, reactions=reactions) 75 | if sorted(simp.species_names) != prev_species: #append only if different from previous one 76 | simp_mech.append(simp) 77 | #write(simp, output_filename='skeletal_N'+str(simp.n_species)+'_R'+str(simp.n_reactions)+'.inp', skip_thermo=True, skip_transport=True) 78 | #write(simp, output_filename='skeletal_N'+str(simp.n_species)+'_R'+str(simp.n_reactions)+'.inp') 79 | simp.write_yaml('skeletal_N'+str(simp.n_species)+'_R'+str(simp.n_reactions)+'.yaml') 80 | prev_species = sorted(simp.species_names) 81 | 82 | nmech = len(simp_mech) 83 | print('%i mechanisms found' % (nmech)) 84 | 85 | 86 | print('Assessing skeletal mechanisms performance over a range of conditions...') 87 | 88 | #import DTL 89 | dtl_mech = csp.CanteraCSP(parent_mech) 90 | 91 | import_simp_mech = False 92 | if import_simp_mech: 93 | #import Skeletal(s) 94 | read_mech = sorted([f for f in os.listdir('./') if f.startswith('skeletal')]) 95 | simp_mech = [] 96 | for i in range(len(read_mech)): 97 | simp_mech.append(csp.CanteraCSP(read_mech[i])) 98 | print('%i mechanisms found' % len(simp_mech)) 99 | 100 | T_list = np.arange(1000,1800,100) 101 | phi_list = [0.5, 0.7, 1.0, 1.2, 1.5] 102 | P = ct.one_atm 103 | 104 | nTemp = len(T_list) 105 | nPhi = len(phi_list) 106 | 107 | all_states_dtl = [] 108 | all_idt_dtl = [] 109 | all_equi_dtl = [] 110 | for eqratio in phi_list: 111 | for temperature in T_list: 112 | # Re-run the ignition problem with the detailed mechanisms 113 | states = utils.integrate_batch_constP(dtl_mech,temperature,P,eqratio,FuComp,OxComp,1000) 114 | idt = states.t[states.rhsT.argmax()] 115 | equi = states.T[-1] 116 | all_states_dtl.append(states) 117 | all_idt_dtl.append(idt) 118 | all_equi_dtl.append(equi) 119 | 120 | 121 | all_states_simp = [] 122 | all_idt_simp = [] 123 | all_equi_simp = [] 124 | for i in range(nmech): 125 | for eqratio in phi_list: 126 | for temperature in T_list: 127 | # Re-run the ignition problem with the simplified mechanisms 128 | states = utils.integrate_batch_constP(simp_mech[i],temperature,P,eqratio,FuComp,OxComp,1000) 129 | idt = states.t[states.rhsT.argmax()] 130 | equi = states.T[-1] 131 | all_states_simp.append(states) 132 | all_idt_simp.append(idt) 133 | all_equi_simp.append(equi) 134 | 135 | 136 | #compute ignition delay errors 137 | all_idt_err = [] 138 | for i in range(nmech): 139 | for j in range(nPhi): 140 | for k in range(nTemp): 141 | err_idt = abs((all_idt_simp[i*nPhi*nTemp +j*nTemp +k]-all_idt_dtl[j*nTemp+k])/all_idt_dtl[j*nTemp+k])*100 142 | all_idt_err.append(err_idt) 143 | 144 | all_idt_err = np.array(all_idt_err).reshape(nmech,nPhi,nTemp) 145 | 146 | 147 | #compute equilibrium errors 148 | all_equi_err = [] 149 | for i in range(nmech): 150 | for j in range(nPhi): 151 | for k in range(nTemp): 152 | err_equi = abs((all_equi_simp[i*nPhi*nTemp +j*nTemp +k]-all_equi_dtl[j*nTemp+k])/all_equi_dtl[j*nTemp+k])*100 153 | all_equi_err.append(err_equi) 154 | 155 | all_equi_err = np.array(all_equi_err).reshape(nmech,nPhi,nTemp) 156 | 157 | plt.ioff() 158 | for i in range(nmech): 159 | fig, ax = plt.subplots(figsize=(6,4)) 160 | for j in range(nPhi): 161 | label='phi = '+str(phi_list[j]) 162 | ax.plot(1000/T_list, all_idt_err[i,j], marker='.', label=label) 163 | title='# of species: '+str(simp_mech[i].n_species) 164 | ax.set_title(title) 165 | ax.set_xlabel('1000/T [1/K]') 166 | ax.set_ylabel('ignition delay time relative Error [%]') 167 | ax.set_yscale('log') 168 | ax.set_ylim([1e-3, 1e2]) 169 | ax.legend() 170 | plt.savefig('ign_delay_error_sk'+str(simp_mech[i].n_species)+'_'+str(simp_mech[i].n_reactions)+'.png', dpi=800, transparent=False) 171 | plt.close() 172 | 173 | 174 | for i in range(nmech): 175 | fig, ax = plt.subplots(figsize=(6,4)) 176 | for j in range(nPhi): 177 | label='phi = '+str(phi_list[j]) 178 | ax.plot(1000/T_list, all_equi_err[i,j], marker='.', label=label) 179 | title='# of species: '+str(simp_mech[i].n_species) 180 | ax.set_title(title) 181 | ax.set_xlabel('1000/T [1/K]') 182 | ax.set_ylabel('Equilibrium Temp relative Error [%]') 183 | ax.set_yscale('log') 184 | ax.set_ylim([1e-6, 1e2]) 185 | ax.legend() 186 | plt.savefig('equilibrium_error_sk'+str(simp_mech[i].n_species)+'_'+str(simp_mech[i].n_reactions)+'.png', dpi=800, transparent=False) 187 | plt.close() 188 | 189 | 190 | 191 | # ============================================================================= 192 | # C = [np.random.choice(list(clr.CSS4_COLORS.values())) for i in range(nmech)] 193 | # fig, ax = plt.subplots(figsize=(6,4)) 194 | # ax.plot(states.t, states.T, lw=2, color='b', label='Detailed') 195 | # for i in range(nmech): 196 | # ax.plot(all_states_simp[i].t, all_states_simp[i].T, lw=2, color = C[i], 197 | # label='M#{0}, Ns={1}, Nr={2}'.format(i, simp_mech[i].n_species, simp_mech[i].n_reactions)) 198 | # ax.set_xlabel('Time (s)') 199 | # ax.set_ylabel('Temperature (K)') 200 | # ax.legend(loc='lower right') 201 | # ax.set_xlim([0, 2]) 202 | # plt.savefig('ignition.png', dpi=800, transparent=False) 203 | # ============================================================================= 204 | -------------------------------------------------------------------------------- /examples/cspSolver/cspsolver_const_p.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as cspF 9 | import PyCSP.Solver as cspS 10 | import time 11 | 12 | #create gas from mechanism file 13 | gas = cspF.CanteraCSP('gri30.yaml') 14 | 15 | #user-set initial condition 16 | T = 1000 17 | P = ct.one_atm 18 | fuel = 'CH4' 19 | oxid = 'O2:1, N2:3.76' 20 | eqratio = 1.0 21 | 22 | #user-set CSP-solver settings 23 | rtol=1e-3 24 | atol=1e-9 25 | gamma = 0.25 26 | t_end = 2.0 27 | 28 | 29 | #set the gas state 30 | gas.TP = T, P 31 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 32 | gas.constP = P 33 | 34 | #set initial condition 35 | y0 = np.hstack((gas.Y,gas.T)) 36 | t0 = 0.0 37 | 38 | #integrate ODE with CSP solver 39 | solver = cspS.CSPsolver(gas) 40 | solver.set_integrator(cspRtol=rtol,cspAtol=atol,factor=gamma,jacobiantype='full') 41 | solver.set_initial_value(y0,t0) 42 | 43 | states = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'M': 0, 'dt': [0.0]}) 44 | 45 | starttimecsp = time.time() 46 | while solver.t < t_end: 47 | solver.integrate() 48 | states.append(gas.state, t=solver.t, M=solver.M, dt=solver.dt) 49 | #print('%10.3e %10.3f %10.3e %10.3e %10.3e %2i' % (solver.t, solver.y[-1], solver.dt, gas.P, gas.density, solver.M)) 50 | endtimecsp = time.time() 51 | 52 | print('\n CSP-solver profiling:') 53 | solver.profiling() 54 | 55 | #integrate ODE with CVODE 56 | 57 | #reset the gas state 58 | gas.TP = T, P 59 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 60 | 61 | r = ct.IdealGasConstPressureReactor(gas) 62 | sim = ct.ReactorNet([r]) 63 | 64 | statesCV = ct.SolutionArray(gas, extra=['t']) 65 | #sim.set_initial_time(t0) 66 | 67 | starttimeCV = time.time() 68 | while sim.time < t_end: 69 | sim.step() 70 | statesCV.append(r.thermo.state, t=sim.time) 71 | endtimeCV = time.time() 72 | dt_cvode=statesCV.t[1:]-statesCV.t[0:-1] 73 | 74 | elapsedcsp = endtimecsp - starttimecsp 75 | print('CSP solver elapsed time [s]: %10.3e, # of steps taken: %5i, # of kernel evaluations: %5i' % (elapsedcsp,states.t.shape[0],gas.nUpdates)) 76 | 77 | elapsedCV = endtimeCV - starttimeCV 78 | print('CVODE elapsed time [s]: %10.3e, # of steps taken: %5i' % (elapsedCV,statesCV.t.shape[0])) 79 | 80 | #plot solution 81 | t_start_plot = 1 82 | t_end_plot = 1.15 83 | print('plotting ODE solution...') 84 | 85 | # Enlarge the figure 86 | plt.figure(figsize=(12, 8)) 87 | 88 | # Subplot 1: Temperature 89 | plt.subplot(2, 3, 1) 90 | plt.plot(statesCV.t, statesCV.T, label='CVODE') 91 | plt.plot(states.t, states.T, label='CSP-solver') 92 | plt.xlabel('Time (s)') 93 | plt.ylabel('Temperature (K)') 94 | plt.xlim(t_start_plot, t_end_plot) 95 | plt.legend() 96 | 97 | # Subplot 2: OH Mole Fraction 98 | plt.subplot(2, 3, 2) 99 | plt.plot(statesCV.t, statesCV.X[:, gas.species_index('OH')], label='CVODE') 100 | plt.plot(states.t, states.X[:, gas.species_index('OH')], label='CSP-solver') 101 | plt.xlabel('Time (s)') 102 | plt.ylabel('OH Mole Fraction') 103 | plt.xlim(t_start_plot, t_end_plot) 104 | plt.legend() 105 | 106 | # Subplot 3: H Mole Fraction 107 | plt.subplot(2, 3, 3) 108 | plt.plot(statesCV.t, statesCV.X[:, gas.species_index('H')], label='CVODE') 109 | plt.plot(states.t, states.X[:, gas.species_index('H')], label='CSP-solver') 110 | plt.xlabel('Time (s)') 111 | plt.ylabel('H Mole Fraction') 112 | plt.xlim(t_start_plot, t_end_plot) 113 | plt.legend() 114 | 115 | # Subplot 4: Modes 116 | plt.subplot(2, 3, 4) 117 | plt.plot(states.t, states.M-1, label='Fast-subspace dim', color='red') 118 | plt.xlabel('Time (s)') 119 | plt.ylabel('M') 120 | plt.xlim(t_start_plot, t_end_plot) 121 | plt.legend() 122 | 123 | # Subplot 5: Density 124 | plt.subplot(2, 3, 5) 125 | plt.plot(statesCV.t, statesCV.density, label='CVODE') 126 | plt.plot(states.t, states.density, label='CSP-solver') 127 | plt.xlabel('Time (s)') 128 | plt.ylabel('Density (kg/m3)') 129 | plt.xlim(t_start_plot, t_end_plot) 130 | plt.legend() 131 | 132 | # Subplot 6: dt 133 | plt.subplot(2, 3, 6) 134 | plt.plot(statesCV.t[0:-1], dt_cvode, label='CVODE Time Step') 135 | plt.plot(states.t, states.dt, label='CSP-solver Time Step') 136 | plt.xlabel('Time (s)') 137 | plt.ylabel('dt') 138 | plt.yscale('log') 139 | plt.xlim(t_start_plot, t_end_plot) 140 | plt.legend() 141 | 142 | # Adjust layout and show the plot 143 | plt.tight_layout() 144 | plt.show() -------------------------------------------------------------------------------- /examples/cspSolver/cspsolver_const_v.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as cspF 9 | import PyCSP.Solver as cspS 10 | import time 11 | 12 | #create gas from mechanism file 13 | gas = cspF.CanteraCSP('gri30.yaml') 14 | 15 | #user-set initial condition 16 | T = 1000 17 | P = ct.one_atm 18 | fuel = 'CH4' 19 | oxid = 'O2:1, N2:3.76' 20 | eqratio = 1.0 21 | 22 | #user-set CSP-solver settings 23 | rtol=1e-3 24 | atol=1e-9 25 | gamma = 0.25 26 | t_end = 2.0 27 | 28 | #set the gas state 29 | gas.TP = T, P 30 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 31 | rho = gas.density 32 | gas.constRho = rho 33 | 34 | #initial condition 35 | y0 = np.hstack((gas.Y,gas.T)) 36 | t0 = 0.0 37 | 38 | #integrate ODE with CSP solver 39 | solver = cspS.CSPsolver(gas) 40 | solver.set_integrator(cspRtol=rtol,cspAtol=atol,factor=gamma,jacobiantype='full') 41 | solver.set_initial_value(y0,t0) 42 | 43 | states = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'M': 0, 'dt': [0.0]}) 44 | 45 | starttimecsp = time.time() 46 | while solver.t < t_end: 47 | solver.integrate() 48 | states.append(gas.state, t=solver.t, M=solver.M, dt=solver.dt) 49 | #print('%10.3e %10.3f %10.3e %10.3e %10.3e %2i' % (solver.t, solver.y[-1], solver.dt, gas.P, gas.density, solver.M)) 50 | endtimecsp = time.time() 51 | 52 | print('\n CSP-solver profiling:') 53 | solver.profiling() 54 | 55 | #integrate ODE with CVODE 56 | 57 | #reset the gas state 58 | gas.TP = T, P 59 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 60 | 61 | #integrate ODE with CVODE 62 | r = ct.IdealGasReactor(gas) 63 | sim = ct.ReactorNet([r]) 64 | 65 | statesCV = ct.SolutionArray(gas, extra=['t']) 66 | #sim.set_initial_time(t0) 67 | 68 | starttimeCV = time.time() 69 | while sim.time < t_end: 70 | sim.step() 71 | statesCV.append(r.thermo.state, t=sim.time) 72 | endtimeCV = time.time() 73 | dt_cvode=statesCV.t[1:]-statesCV.t[0:-1] 74 | 75 | elapsedcsp = endtimecsp - starttimecsp 76 | print('CSP solver elapsed time [s]: %10.3e, # of steps taken: %5i, # of kernel evaluations: %5i' % (elapsedcsp,states.t.shape[0],gas.nUpdates)) 77 | 78 | elapsedCV = endtimeCV - starttimeCV 79 | print('CVODE elapsed time [s]: %10.3e, # of steps taken: %5i' % (elapsedCV,statesCV.t.shape[0])) 80 | 81 | #plot solution 82 | t_start_plot = 1 83 | t_end_plot = 1.15 84 | print('plotting ODE solution...') 85 | 86 | # Enlarge the figure 87 | plt.figure(figsize=(12, 8)) 88 | 89 | # Subplot 1: Temperature 90 | plt.subplot(2, 3, 1) 91 | plt.plot(statesCV.t, statesCV.T, label='CVODE') 92 | plt.plot(states.t, states.T, label='CSP-solver') 93 | plt.xlabel('Time (s)') 94 | plt.ylabel('Temperature (K)') 95 | plt.xlim(t_start_plot, t_end_plot) 96 | plt.legend() 97 | 98 | # Subplot 2: OH Mole Fraction 99 | plt.subplot(2, 3, 2) 100 | plt.plot(statesCV.t, statesCV.X[:, gas.species_index('OH')], label='CVODE') 101 | plt.plot(states.t, states.X[:, gas.species_index('OH')], label='CSP-solver') 102 | plt.xlabel('Time (s)') 103 | plt.ylabel('OH Mole Fraction') 104 | plt.xlim(t_start_plot, t_end_plot) 105 | plt.legend() 106 | 107 | # Subplot 3: H Mole Fraction 108 | plt.subplot(2, 3, 3) 109 | plt.plot(statesCV.t, statesCV.X[:, gas.species_index('H')], label='CVODE') 110 | plt.plot(states.t, states.X[:, gas.species_index('H')], label='CSP-solver') 111 | plt.xlabel('Time (s)') 112 | plt.ylabel('H Mole Fraction') 113 | plt.xlim(t_start_plot, t_end_plot) 114 | plt.legend() 115 | 116 | # Subplot 4: Modes 117 | plt.subplot(2, 3, 4) 118 | plt.plot(states.t, states.M-1, label='Fast-subspace dim', color='red') 119 | plt.xlabel('Time (s)') 120 | plt.ylabel('M') 121 | plt.xlim(t_start_plot, t_end_plot) 122 | plt.legend() 123 | 124 | # Subplot 5: Pressure 125 | plt.subplot(2, 3, 5) 126 | plt.plot(statesCV.t, statesCV.P, label='CVODE') 127 | plt.plot(states.t, states.P, label='CSP-solver') 128 | plt.xlabel('Time (s)') 129 | plt.ylabel('Pressure (Pa)') 130 | plt.xlim(t_start_plot, t_end_plot) 131 | plt.legend() 132 | 133 | # Subplot 6: dt 134 | plt.subplot(2, 3, 6) 135 | plt.plot(statesCV.t[0:-1], dt_cvode, label='CVODE Time Step') 136 | plt.plot(states.t, states.dt, label='CSP-solver Time Step') 137 | plt.xlabel('Time (s)') 138 | plt.ylabel('dt') 139 | plt.yscale('log') 140 | plt.xlim(t_start_plot, t_end_plot) 141 | plt.legend() 142 | 143 | # Adjust layout and show the plot 144 | plt.tight_layout() 145 | plt.show() -------------------------------------------------------------------------------- /examples/cspSolver/gscheme_const_p.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as cspF 9 | import PyCSP.GScheme as gsc 10 | import time 11 | 12 | #create gas from mechanism file 13 | gas = cspF.CanteraCSP('gri30.yaml') 14 | 15 | #user-set initial condition 16 | T = 1000 17 | P = ct.one_atm 18 | fuel = 'CH4' 19 | oxid = 'O2:1, N2:3.76' 20 | eqratio = 1.0 21 | 22 | # GScheme settings 23 | rtolTail = 1e-3 24 | atolTail = 1e-9 25 | rtolHead = 1e-4 26 | atolHead = 1e-10 27 | gamma = 0.25 28 | reuse = False 29 | reusetol = 2e-1 30 | t_end = 2.0 31 | 32 | #set the gas state 33 | gas.TP = T, P 34 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 35 | gas.constP = P 36 | 37 | #set initial condition 38 | y0 = np.hstack((gas.Y,gas.T)) 39 | t0 = 0.0 40 | 41 | #integrate ODE with G-Scheme 42 | solver = gsc.GScheme(gas) 43 | solver.set_integrator(cspRtolTail=rtolTail,cspAtolTail=atolTail,cspRtolHead=rtolHead,cspAtolHead=atolHead,factor=gamma,jacobiantype='full') 44 | solver.set_initial_value(y0,t0) 45 | 46 | states = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'Tail': 0, 'Head':0, 'dt': [0.0]}) 47 | 48 | while solver.t < t_end: 49 | solver.integrate() 50 | states.append(gas.state, t=solver.t, Tail=solver.T, Head=solver.H, dt=solver.dt) 51 | #print('%10.3e %10.3f %10.3e %10.3e %10.3e %2i' % (solver.t, solver.y[-1], solver.dt, gas.P, gas.density, solver.M)) 52 | 53 | print('\n G-Scheme profiling:') 54 | solver.profiling() 55 | 56 | #integrate ODE with CVODE 57 | 58 | #reset the gas state 59 | gas.TP = T, P 60 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 61 | 62 | r = ct.IdealGasConstPressureReactor(gas) 63 | sim = ct.ReactorNet([r]) 64 | 65 | statesCV = ct.SolutionArray(gas, extra=['t']) 66 | #sim.set_initial_time(t0) 67 | 68 | while sim.time < t_end: 69 | sim.step() 70 | statesCV.append(r.thermo.state, t=sim.time) 71 | dt_cvode=statesCV.t[1:]-statesCV.t[0:-1] 72 | 73 | #plot solution 74 | t_start_plot = 1 75 | t_end_plot = 1.15 76 | print('plotting ODE solution...') 77 | 78 | # Enlarge the figure 79 | plt.figure(figsize=(12, 8)) 80 | 81 | # Subplot 1: Temperature 82 | plt.subplot(2, 3, 1) 83 | plt.plot(statesCV.t, statesCV.T, label='CVODE') 84 | plt.plot(states.t, states.T, label='G-Scheme') 85 | plt.xlabel('Time (s)') 86 | plt.ylabel('Temperature (K)') 87 | plt.xlim(t_start_plot, t_end_plot) 88 | plt.legend() 89 | 90 | # Subplot 2: OH Mole Fraction 91 | plt.subplot(2, 3, 2) 92 | plt.plot(statesCV.t, statesCV.X[:, gas.species_index('OH')], label='CVODE') 93 | plt.plot(states.t, states.X[:, gas.species_index('OH')], label='G-Scheme') 94 | plt.xlabel('Time (s)') 95 | plt.ylabel('OH Mole Fraction') 96 | plt.xlim(t_start_plot, t_end_plot) 97 | plt.legend() 98 | 99 | # Subplot 3: H Mole Fraction 100 | plt.subplot(2, 3, 3) 101 | plt.plot(statesCV.t, statesCV.X[:, gas.species_index('H')], label='CVODE') 102 | plt.plot(states.t, states.X[:, gas.species_index('H')], label='G-Scheme') 103 | plt.xlabel('Time (s)') 104 | plt.ylabel('H Mole Fraction') 105 | plt.xlim(t_start_plot, t_end_plot) 106 | plt.legend() 107 | 108 | # Subplot 4: Modes 109 | plt.subplot(2, 3, 4) 110 | plt.plot(states.t, states.Tail, label='Tail', color='red') 111 | plt.plot(states.t, states.Head, label='Head', color='green') 112 | plt.fill_between(states.t, states.Tail, states.Head, color='grey', alpha=0.2, label='Active Modes') 113 | plt.xlabel('Time (s)') 114 | plt.ylabel('Modes') 115 | plt.xlim(t_start_plot, t_end_plot) 116 | plt.legend() 117 | 118 | # Subplot 5: Active Modes 119 | plt.subplot(2, 3, 5) 120 | plt.plot(states.t, states.Head - states.Tail, color='orange', label='Active Modes') 121 | plt.xlabel('Time (s)') 122 | plt.ylabel('Active Modes') 123 | plt.xlim(t_start_plot, t_end_plot) 124 | plt.legend() 125 | 126 | # Subplot 6: dt 127 | plt.subplot(2, 3, 6) 128 | plt.plot(statesCV.t[0:-1], dt_cvode, label='CVODE Time Step') 129 | plt.plot(states.t, states.dt, label='G-Scheme Time Step') 130 | plt.xlabel('Time (s)') 131 | plt.ylabel('dt') 132 | plt.yscale('log') 133 | plt.xlim(t_start_plot, t_end_plot) 134 | plt.legend() 135 | 136 | # Adjust layout and show the plot 137 | plt.tight_layout() 138 | plt.show() -------------------------------------------------------------------------------- /examples/cspSolver/gscheme_const_p_reuse.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as cspF 9 | import PyCSP.GScheme as gsc 10 | import time 11 | 12 | #create gas from mechanism file 13 | gas = cspF.CanteraCSP('gri30.yaml') 14 | 15 | #user-set initial condition 16 | T = 1000 17 | P = ct.one_atm 18 | fuel = 'CH4' 19 | oxid = 'O2:1, N2:3.76' 20 | eqratio = 1.0 21 | 22 | # GScheme settings 23 | rtolTail = 1e-3 24 | atolTail = 1e-9 25 | rtolHead = 1e-4 26 | atolHead = 1e-10 27 | gamma = 0.25 28 | reusetol = 2e-1 29 | t_end = 2.0 30 | 31 | #set the gas state 32 | gas.TP = T, P 33 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 34 | gas.constP = P 35 | 36 | #set initial condition 37 | y0 = np.hstack((gas.Y,gas.T)) 38 | t0 = 0.0 39 | 40 | #integrate ODE with G-Scheme 41 | solver = gsc.GScheme(gas) 42 | solver.set_integrator(cspRtolTail=rtolTail,cspAtolTail=atolTail,cspRtolHead=rtolHead,cspAtolHead=atolHead,factor=gamma,jacobiantype='full') 43 | solver.set_initial_value(y0,t0) 44 | 45 | states = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'Tail': 0, 'Head':0, 'dt': [0.0]}) 46 | 47 | while solver.t < t_end: 48 | solver.integrate() 49 | states.append(gas.state, t=solver.t, Tail=solver.T, Head=solver.H, dt=solver.dt) 50 | #print('%10.3e %10.3f %10.3e %10.3e %10.3e %2i' % (solver.t, solver.y[-1], solver.dt, gas.P, gas.density, solver.M)) 51 | nupB = gas.nUpdates 52 | 53 | print('\n G-Scheme without Reuse profiling:') 54 | solver.profiling() 55 | 56 | #integrate ODE with G-Scheme with basis reuse 57 | 58 | #reset the gas state 59 | gas.TP = T, P 60 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 61 | 62 | solver = gsc.GScheme(gas) 63 | solver.set_integrator(cspRtolTail=rtolTail,cspAtolTail=atolTail,cspRtolHead=rtolHead,cspAtolHead=atolHead,factor=gamma,jacobiantype='full') 64 | solver.reuse = True 65 | solver.reuseThr = reusetol 66 | solver.set_initial_value(y0,t0) 67 | 68 | statesRU = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'Tail': 0, 'Head':0, 'dt': [0.0], 'bs':0, 'norm': [0.0]}) 69 | 70 | while solver.t < t_end: 71 | solver.integrate() 72 | statesRU.append(gas.state, t=solver.t, Tail=solver.T, Head=solver.H, dt=solver.dt, bs=solver.basisStatus, norm=solver.normJac) 73 | #print('%10.3e %10.3f %10.3e %10.3e' % (solver.t, solver.y[-1], solver.dt, solver.normJac)) 74 | nupBRU = solver.updateBasis 75 | 76 | print('\n G-Scheme with Reuse profiling:') 77 | solver.profiling() 78 | 79 | #reset the gas state 80 | gas.TP = T, P 81 | gas.set_equivalence_ratio(eqratio, fuel, oxid) 82 | 83 | #integrate ODE with CVODE 84 | r = ct.IdealGasConstPressureReactor(gas) 85 | sim = ct.ReactorNet([r]) 86 | 87 | statesCV = ct.SolutionArray(gas, extra=['t']) 88 | #sim.set_initial_time(t0) 89 | 90 | while sim.time < t_end: 91 | sim.step() 92 | statesCV.append(r.thermo.state, t=sim.time) 93 | dt_cvode=statesCV.t[1:]-statesCV.t[0:-1] 94 | 95 | #plot solution 96 | t_start_plot = 1 97 | t_end_plot = 1.15 98 | print('plotting ODE solution...') 99 | plt.figure(figsize=(12, 8)) 100 | 101 | # Subplot 1: Temperature 102 | plt.subplot(2, 3, 1) 103 | plt.plot(statesCV.t, statesCV.T, color = 'blue', label='cvode') 104 | plt.plot(states.t, states.T, color = 'orange', label='gsc') 105 | plt.plot(statesRU.t, statesRU.T, color = 'green', label='gsc-ru') 106 | plt.xlabel('Time (s)') 107 | plt.ylabel('Temperature (K)') 108 | plt.legend(loc="upper left") 109 | plt.xlim(t_start_plot, t_end_plot) 110 | 111 | # Subplot 2: OH Mole Fraction 112 | plt.subplot(2, 3, 2) 113 | plt.plot(statesCV.t, statesCV.X[:,gas.species_index('OH')], color = 'blue', label='cvode') 114 | plt.plot(states.t, states.X[:,gas.species_index('OH')], color = 'orange', label='gsc') 115 | plt.plot(statesRU.t, statesRU.X[:,gas.species_index('OH')], color = 'green', label='gsc-ru') 116 | plt.xlabel('Time (s)') 117 | plt.ylabel('OH Mole Fraction') 118 | plt.legend(loc="upper left") 119 | plt.xlim(t_start_plot, t_end_plot) 120 | 121 | # Subplot 3: H Mole Fraction 122 | plt.subplot(2, 3, 3) 123 | plt.plot(statesRU.t, statesRU.norm) 124 | calc = statesRU.norm[statesRU.bs == 1] 125 | reuse = statesRU.norm[statesRU.bs == 0] 126 | plt.plot(statesRU.t[statesRU.bs == 1], calc, 'ko', markersize=2, label='compute') 127 | plt.plot(statesRU.t[statesRU.bs == 0], reuse, '+r', markersize=2, label='reuse') 128 | plt.xlabel('Time (s)') 129 | plt.ylabel('norm Jac') 130 | plt.legend(loc="upper left") 131 | plt.xlim(t_start_plot, t_end_plot) 132 | 133 | # Subplot 4: Modes 134 | plt.subplot(2, 3, 4) 135 | plt.plot(states.t, states.Tail, statesRU.t, statesRU.Tail, states.t, states.Head, statesRU.t, statesRU.Head) 136 | plt.fill_between(states.t, states.Tail, states.Head, color='grey', alpha=0.2) 137 | plt.fill_between(statesRU.t, statesRU.Tail, statesRU.Head, color='orange', alpha=0.2) 138 | plt.xlabel('Time (s)') 139 | plt.ylabel('Modes') 140 | plt.xlim(t_start_plot, t_end_plot) 141 | 142 | # Subplot 5: Active Modes 143 | plt.subplot(2, 3, 5) 144 | plt.plot(states.t, states.Head - states.Tail, color='orange', label = 'gsc') 145 | plt.plot(statesRU.t, statesRU.Head - statesRU.Tail, color='green', label='gsc-ru') 146 | plt.xlabel('Time (s)') 147 | plt.ylabel('active modes') 148 | plt.legend(loc="upper left") 149 | plt.xlim(t_start_plot, t_end_plot) 150 | 151 | # Subplot 6: dt 152 | plt.subplot(2, 3, 6) 153 | plt.plot(statesCV.t[0:-1], dt_cvode, label='CVODE Time Step') 154 | plt.plot(states.t, states.dt,color='orange', label = 'gsc') 155 | plt.plot(statesRU.t, statesRU.dt, color='green', label='gsc-ru') 156 | plt.xlabel('Time (s)') 157 | plt.ylabel('dt') 158 | plt.yscale('log') 159 | plt.legend(loc="upper left") 160 | plt.xlim(t_start_plot, t_end_plot) 161 | 162 | plt.tight_layout() 163 | plt.show() 164 | -------------------------------------------------------------------------------- /examples/cspSolver/hydrogen.yaml: -------------------------------------------------------------------------------- 1 | description: |- 2 | "" 3 | 4 | generator: cti2yaml 5 | cantera-version: 3.0.0 6 | date: Mon, 11 Sep 2023 10:08:51 +0200 7 | input-files: [hydrogen.cti] 8 | 9 | units: {length: cm, quantity: mol, activation-energy: cal/mol} 10 | 11 | phases: 12 | - name: gas 13 | thermo: ideal-gas 14 | elements: [H, O, N] 15 | species: [H2, O2, O, OH, H2O, H, HO2, H2O2, N2] 16 | kinetics: gas 17 | reactions: all 18 | state: 19 | T: 300.0 20 | P: 1.01325e+05 21 | 22 | species: 23 | - name: H2 24 | composition: {H: 2} 25 | thermo: 26 | model: NASA7 27 | temperature-ranges: [300.0, 1000.0, 5000.0] 28 | data: 29 | - [3.298124, 8.249441e-04, -8.143015e-07, -9.475434e-11, 4.134872e-13, 30 | -1012.5209, -3.294094] 31 | - [2.991423, 7.000644e-04, -5.633828e-08, -9.231578e-12, 1.5827519e-15, 32 | -835.034, -1.3551101] 33 | note: '121286' 34 | - name: O2 35 | composition: {O: 2} 36 | thermo: 37 | model: NASA7 38 | temperature-ranges: [300.0, 1000.0, 5000.0] 39 | data: 40 | - [3.212936, 1.1274864e-03, -5.75615e-07, 1.3138773e-09, -8.768554e-13, 41 | -1005.249, 6.034737] 42 | - [3.697578, 6.135197e-04, -1.258842e-07, 1.775281e-11, -1.1364354e-15, 43 | -1233.9301, 3.189165] 44 | note: '121386' 45 | - name: O 46 | composition: {O: 1} 47 | thermo: 48 | model: NASA7 49 | temperature-ranges: [300.0, 1000.0, 5000.0] 50 | data: 51 | - [2.946428, -1.6381665e-03, 2.421031e-06, -1.6028431e-09, 3.890696e-13, 52 | 2.914764e+04, 2.963995] 53 | - [2.542059, -2.755061e-05, -3.102803e-09, 4.551067e-12, -4.368051e-16, 54 | 2.92308e+04, 4.920308] 55 | note: '120186' 56 | - name: OH 57 | composition: {H: 1, O: 1} 58 | thermo: 59 | model: NASA7 60 | temperature-ranges: [300.0, 1000.0, 5000.0] 61 | data: 62 | - [3.637266, 1.85091e-04, -1.6761646e-06, 2.387202e-09, -8.431442e-13, 63 | 3606.781, 1.3588605] 64 | - [2.88273, 1.0139743e-03, -2.276877e-07, 2.174683e-11, -5.126305e-16, 65 | 3886.888, 5.595712] 66 | note: '121286' 67 | - name: H2O 68 | composition: {H: 2, O: 1} 69 | thermo: 70 | model: NASA7 71 | temperature-ranges: [300.0, 1000.0, 5000.0] 72 | data: 73 | - [3.386842, 3.474982e-03, -6.354696e-06, 6.968581e-09, -2.506588e-12, 74 | -3.020811e+04, 2.590232] 75 | - [2.672145, 3.056293e-03, -8.73026e-07, 1.2009964e-10, -6.391618e-15, 76 | -2.989921e+04, 6.862817] 77 | note: '20387' 78 | - name: H 79 | composition: {H: 1} 80 | thermo: 81 | model: NASA7 82 | temperature-ranges: [300.0, 1000.0, 5000.0] 83 | data: 84 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 85 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 86 | note: '120186' 87 | - name: HO2 88 | composition: {H: 1, O: 2} 89 | thermo: 90 | model: NASA7 91 | temperature-ranges: [300.0, 1000.0, 5000.0] 92 | data: 93 | - [2.979963, 4.996697e-03, -3.790997e-06, 2.354192e-09, -8.089024e-13, 94 | 176.2273, 9.222724] 95 | - [4.072191, 2.131296e-03, -5.308145e-07, 6.112269e-11, -2.841164e-15, 96 | -157.9727, 3.476029] 97 | note: '20387' 98 | - name: H2O2 99 | composition: {H: 2, O: 2} 100 | thermo: 101 | model: NASA7 102 | temperature-ranges: [300.0, 1000.0, 5000.0] 103 | data: 104 | - [3.388753, 6.569226e-03, -1.4850125e-07, -4.625805e-09, 2.471514e-12, 105 | -1.766314e+04, 6.785363] 106 | - [4.573167, 4.336136e-03, -1.4746888e-06, 2.348903e-10, -1.4316536e-14, 107 | -1.800696e+04, 0.5011369] 108 | note: '120186' 109 | - name: N2 110 | composition: {N: 2} 111 | thermo: 112 | model: NASA7 113 | temperature-ranges: [300.0, 1000.0, 5000.0] 114 | data: 115 | - [3.298677, 1.4082404e-03, -3.963222e-06, 5.641515e-09, -2.444854e-12, 116 | -1020.8999, 3.950372] 117 | - [2.92664, 1.4879768e-03, -5.68476e-07, 1.0097038e-10, -6.753351e-15, 118 | -922.7977, 5.980528] 119 | note: '121286' 120 | 121 | reactions: 122 | - equation: H + O2 <=> O + OH # Reaction 1 123 | rate-constant: {A: 1.915e+14, b: 0.0, Ea: 1.644e+04} 124 | - equation: O + H2 <=> H + OH # Reaction 2 125 | rate-constant: {A: 5.08e+04, b: 2.67, Ea: 6290.0} 126 | - equation: H2 + OH <=> H2O + H # Reaction 3 127 | rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} 128 | - equation: OH + OH <=> O + H2O # Reaction 4 129 | rate-constant: {A: 1.23e+04, b: 2.62, Ea: -1880.0} 130 | - equation: H2 + M <=> H + H + M # Reaction 5 131 | type: three-body 132 | rate-constant: {A: 4.577e+19, b: -1.4, Ea: 1.044e+05} 133 | efficiencies: {H2: 2.5, H2O: 12.0} 134 | - equation: O + O + M <=> O2 + M # Reaction 6 135 | type: three-body 136 | rate-constant: {A: 6.165e+15, b: -0.5, Ea: 0.0} 137 | efficiencies: {H2: 2.5, H2O: 12.0} 138 | - equation: O + H + M <=> OH + M # Reaction 7 139 | type: three-body 140 | rate-constant: {A: 4.714e+18, b: -1.0, Ea: 0.0} 141 | efficiencies: {H2: 2.5, H2O: 12.0} 142 | - equation: H + OH + M <=> H2O + M # Reaction 8 143 | type: three-body 144 | rate-constant: {A: 2.24e+22, b: -2.0, Ea: 0.0} 145 | efficiencies: {H2: 2.5, H2O: 6.3} 146 | - equation: H + O2 + M <=> HO2 + M # Reaction 9 147 | type: three-body 148 | rate-constant: {A: 6.17e+19, b: -1.42, Ea: 0.0} 149 | efficiencies: {H2: 2.5, H2O: 12.0} 150 | - equation: HO2 + H <=> H2 + O2 # Reaction 10 151 | rate-constant: {A: 6.63e+13, b: 0.0, Ea: 2130.0} 152 | - equation: HO2 + H <=> OH + OH # Reaction 11 153 | rate-constant: {A: 1.69e+14, b: 0.0, Ea: 874.0} 154 | - equation: HO2 + O <=> O2 + OH # Reaction 12 155 | rate-constant: {A: 1.81e+13, b: 0.0, Ea: -400.0} 156 | - equation: HO2 + OH <=> H2O + O2 # Reaction 13 157 | rate-constant: {A: 1.45e+16, b: -1.0, Ea: 0.0} 158 | - equation: HO2 + HO2 <=> H2O2 + O2 # Reaction 14 159 | rate-constant: {A: 3.02e+12, b: 0.0, Ea: 1390.0} 160 | - equation: H2O2 + M <=> OH + OH + M # Reaction 15 161 | type: three-body 162 | rate-constant: {A: 1.202e+17, b: 0.0, Ea: 4.55e+04} 163 | efficiencies: {H2: 2.5, H2O: 12.0} 164 | - equation: H2O2 + H <=> H2O + OH # Reaction 16 165 | rate-constant: {A: 1.0e+13, b: 0.0, Ea: 3590.0} 166 | - equation: H2O2 + H <=> HO2 + H2 # Reaction 17 167 | rate-constant: {A: 4.82e+13, b: 0.0, Ea: 7950.0} 168 | - equation: H2O2 + O <=> OH + HO2 # Reaction 18 169 | rate-constant: {A: 9.55e+06, b: 2.0, Ea: 3970.0} 170 | - equation: H2O2 + OH <=> HO2 + H2O # Reaction 19 171 | rate-constant: {A: 7.0e+12, b: 0.0, Ea: 1430.0} 172 | -------------------------------------------------------------------------------- /examples/dataImportExport/export_import.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import sys 9 | import PyCSP.Functions as csp 10 | import PyCSP.utils as utils 11 | 12 | #-------CREATE DATA--------- 13 | #create gas from original mechanism file hydrogen.cti 14 | gas = csp.CanteraCSP('hydrogen.yaml') 15 | 16 | #set the gas state 17 | T = 1000 18 | P = ct.one_atm 19 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 20 | gas.TP = T, P 21 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 22 | 23 | 24 | #integrate ODE and dump test data 25 | r = ct.IdealGasConstPressureReactor(gas) 26 | sim = ct.ReactorNet([r]) 27 | time = 0.0 28 | states = ct.SolutionArray(gas, extra=['t']) 29 | 30 | sim.set_initial_time(0.0) 31 | while sim.time < 0.001: 32 | sim.step() 33 | states.append(r.thermo.state, t=sim.time) 34 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 35 | 36 | dataout = np.concatenate((states.t[:,np.newaxis],states.T[:,np.newaxis],states.P[:,np.newaxis],states.Y),axis=1) 37 | with open('testdata.dat','w') as f: 38 | np.savetxt(f,dataout,fmt='%.16e', delimiter=' ') 39 | 40 | 41 | 42 | 43 | #-------IMPORT DATA--------- 44 | #read data from file 45 | data = np.loadtxt('testdata.dat') 46 | time = data[:,0] 47 | Temp = data[:,1] 48 | Pressure = data[:,2] 49 | Y = data[:,3:] 50 | 51 | gas = csp.CanteraCSP('hydrogen.cti') 52 | 53 | #set jacobiantype 54 | gas.jacobiantype = 'full' 55 | 56 | evals = [] 57 | Revec = [] 58 | Levec = [] 59 | fvec = [] 60 | M = [] 61 | 62 | for step in range(time.shape[0]): 63 | gas.constP = Pressure[step] 64 | state = np.append(Y[step],Temp[step]) 65 | gas.set_stateYT(state) 66 | lam,R,L,f = gas.get_kernel() 67 | NofDM = gas.calc_exhausted_modes(rtol=1.0e-2,atol=1.0e-8) 68 | 69 | evals.append(lam) 70 | Revec.append(R) 71 | Levec.append(L) 72 | fvec.append(f) 73 | M.append(NofDM) 74 | 75 | 76 | evals = np.array(evals) 77 | Revec = np.array(Revec) 78 | Levec = np.array(Levec) 79 | fvec = np.array(fvec) 80 | M = np.array(M) 81 | 82 | 83 | 84 | #plot solution 85 | print('plotting ODE solution...') 86 | plt.clf() 87 | plt.subplot(2, 2, 1) 88 | plt.plot(states.t, states.T) 89 | plt.xlabel('Time (s)') 90 | plt.ylabel('Temperature (K)') 91 | plt.xlim(0., 0.002) 92 | plt.subplot(2, 2, 2) 93 | plt.plot(states.t, states.X[:,gas.species_index('OH')]) 94 | plt.xlabel('Time (s)') 95 | plt.ylabel('OH Mole Fraction') 96 | plt.xlim(0., 0.002) 97 | plt.subplot(2, 2, 3) 98 | plt.plot(states.t, states.X[:,gas.species_index('H')]) 99 | plt.xlabel('Time (s)') 100 | plt.ylabel('H Mole Fraction') 101 | plt.xlim(0., 0.002) 102 | plt.subplot(2, 2, 4) 103 | plt.plot(states.t, states.X[:,gas.species_index('H2')]) 104 | plt.xlabel('Time (s)') 105 | plt.ylabel('H2 Mole Fraction') 106 | plt.xlim(0., 0.002) 107 | plt.tight_layout() 108 | plt.show() 109 | 110 | #plot eigenvalues and lambda_M+1 111 | evalM = utils.select_eval(evals,M) 112 | logevals = np.clip(np.log10(1.0+np.abs(evals)),0,100)*np.sign(evals.real) 113 | logevalM = np.clip(np.log10(1.0+np.abs(evalM)),0,100)*np.sign(evalM.real) 114 | print('plotting eigenvalues...') 115 | fig, ax = plt.subplots(figsize=(6,4)) 116 | for idx in range(evals.shape[1]): 117 | #color = next(ax._get_lines.prop_cycler)['color'] 118 | ax.plot(states.t, logevals[:,idx], color='black', marker='.', markersize = 5,linestyle = 'None') 119 | ax.plot(states.t, logevalM, color='orange', marker='.', markersize = 4,linestyle = 'None', label='lam(M+1) rtol e-2; atol e-8') 120 | ax.set_xlabel('time (s)') 121 | ax.set_ylabel('evals') 122 | ax.set_ylim([-9, 6]) 123 | ax.set_xlim([0., 0.001]) 124 | ax.grid(False) 125 | ax.legend() 126 | plt.show() 127 | plt.savefig('eigenvalues.png', dpi=500, transparent=False) 128 | 129 | 130 | #plot exhausted modes 131 | print('plotting exhausted modes...') 132 | fig, ax = plt.subplots(figsize=(6,4)) 133 | #ax.plot(states.t, M, color='black') 134 | ax.plot(M, color='orange', label='rtol e-2; atol e-8') 135 | #ax.set_xlabel('time (s)') 136 | ax.set_xlabel('# timestep') 137 | ax.set_ylabel('M') 138 | ax.set_ylim([0,10]) 139 | #ax.set_xlim([0., 0.001]) 140 | ax.grid(False) 141 | ax.legend() 142 | plt.show() 143 | 144 | #plot amplitudes 145 | print('plotting mode amplitudes...') 146 | fig, ax = plt.subplots(figsize=(6,4)) 147 | for idx in range(fvec.shape[1]): 148 | #color = next(ax._get_lines.prop_cycler)['color'] 149 | ax.plot(states.t, np.log10(1e-10+fvec[:,idx]), label='Mode %d' %(idx+1), marker='.', markersize = 2,linestyle = 'None') 150 | ax.set_xlabel('time (s)') 151 | ax.set_ylabel('Amplitude') 152 | ax.set_ylim([-8, 10]) 153 | ax.set_xlim([0., 0.001]) 154 | ax.grid(False) 155 | ax.legend() 156 | plt.show() 157 | #plt.savefig('figures/prediction_combined.png', dpi=500, transparent=False) 158 | -------------------------------------------------------------------------------- /examples/dataImportExport/hydrogen.yaml: -------------------------------------------------------------------------------- 1 | description: |- 2 | "" 3 | 4 | generator: cti2yaml 5 | cantera-version: 3.0.0 6 | date: Mon, 11 Sep 2023 10:22:29 +0200 7 | input-files: [hydrogen.cti] 8 | 9 | units: {length: cm, quantity: mol, activation-energy: cal/mol} 10 | 11 | phases: 12 | - name: gas 13 | thermo: ideal-gas 14 | elements: [H, O, N] 15 | species: [H2, O2, O, OH, H2O, H, HO2, H2O2, N2] 16 | kinetics: gas 17 | reactions: all 18 | state: 19 | T: 300.0 20 | P: 1.01325e+05 21 | 22 | species: 23 | - name: H2 24 | composition: {H: 2} 25 | thermo: 26 | model: NASA7 27 | temperature-ranges: [300.0, 1000.0, 5000.0] 28 | data: 29 | - [3.298124, 8.249441e-04, -8.143015e-07, -9.475434e-11, 4.134872e-13, 30 | -1012.5209, -3.294094] 31 | - [2.991423, 7.000644e-04, -5.633828e-08, -9.231578e-12, 1.5827519e-15, 32 | -835.034, -1.3551101] 33 | note: '121286' 34 | - name: O2 35 | composition: {O: 2} 36 | thermo: 37 | model: NASA7 38 | temperature-ranges: [300.0, 1000.0, 5000.0] 39 | data: 40 | - [3.212936, 1.1274864e-03, -5.75615e-07, 1.3138773e-09, -8.768554e-13, 41 | -1005.249, 6.034737] 42 | - [3.697578, 6.135197e-04, -1.258842e-07, 1.775281e-11, -1.1364354e-15, 43 | -1233.9301, 3.189165] 44 | note: '121386' 45 | - name: O 46 | composition: {O: 1} 47 | thermo: 48 | model: NASA7 49 | temperature-ranges: [300.0, 1000.0, 5000.0] 50 | data: 51 | - [2.946428, -1.6381665e-03, 2.421031e-06, -1.6028431e-09, 3.890696e-13, 52 | 2.914764e+04, 2.963995] 53 | - [2.542059, -2.755061e-05, -3.102803e-09, 4.551067e-12, -4.368051e-16, 54 | 2.92308e+04, 4.920308] 55 | note: '120186' 56 | - name: OH 57 | composition: {H: 1, O: 1} 58 | thermo: 59 | model: NASA7 60 | temperature-ranges: [300.0, 1000.0, 5000.0] 61 | data: 62 | - [3.637266, 1.85091e-04, -1.6761646e-06, 2.387202e-09, -8.431442e-13, 63 | 3606.781, 1.3588605] 64 | - [2.88273, 1.0139743e-03, -2.276877e-07, 2.174683e-11, -5.126305e-16, 65 | 3886.888, 5.595712] 66 | note: '121286' 67 | - name: H2O 68 | composition: {H: 2, O: 1} 69 | thermo: 70 | model: NASA7 71 | temperature-ranges: [300.0, 1000.0, 5000.0] 72 | data: 73 | - [3.386842, 3.474982e-03, -6.354696e-06, 6.968581e-09, -2.506588e-12, 74 | -3.020811e+04, 2.590232] 75 | - [2.672145, 3.056293e-03, -8.73026e-07, 1.2009964e-10, -6.391618e-15, 76 | -2.989921e+04, 6.862817] 77 | note: '20387' 78 | - name: H 79 | composition: {H: 1} 80 | thermo: 81 | model: NASA7 82 | temperature-ranges: [300.0, 1000.0, 5000.0] 83 | data: 84 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 85 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 86 | note: '120186' 87 | - name: HO2 88 | composition: {H: 1, O: 2} 89 | thermo: 90 | model: NASA7 91 | temperature-ranges: [300.0, 1000.0, 5000.0] 92 | data: 93 | - [2.979963, 4.996697e-03, -3.790997e-06, 2.354192e-09, -8.089024e-13, 94 | 176.2273, 9.222724] 95 | - [4.072191, 2.131296e-03, -5.308145e-07, 6.112269e-11, -2.841164e-15, 96 | -157.9727, 3.476029] 97 | note: '20387' 98 | - name: H2O2 99 | composition: {H: 2, O: 2} 100 | thermo: 101 | model: NASA7 102 | temperature-ranges: [300.0, 1000.0, 5000.0] 103 | data: 104 | - [3.388753, 6.569226e-03, -1.4850125e-07, -4.625805e-09, 2.471514e-12, 105 | -1.766314e+04, 6.785363] 106 | - [4.573167, 4.336136e-03, -1.4746888e-06, 2.348903e-10, -1.4316536e-14, 107 | -1.800696e+04, 0.5011369] 108 | note: '120186' 109 | - name: N2 110 | composition: {N: 2} 111 | thermo: 112 | model: NASA7 113 | temperature-ranges: [300.0, 1000.0, 5000.0] 114 | data: 115 | - [3.298677, 1.4082404e-03, -3.963222e-06, 5.641515e-09, -2.444854e-12, 116 | -1020.8999, 3.950372] 117 | - [2.92664, 1.4879768e-03, -5.68476e-07, 1.0097038e-10, -6.753351e-15, 118 | -922.7977, 5.980528] 119 | note: '121286' 120 | 121 | reactions: 122 | - equation: H + O2 <=> O + OH # Reaction 1 123 | rate-constant: {A: 1.915e+14, b: 0.0, Ea: 1.644e+04} 124 | - equation: O + H2 <=> H + OH # Reaction 2 125 | rate-constant: {A: 5.08e+04, b: 2.67, Ea: 6290.0} 126 | - equation: H2 + OH <=> H2O + H # Reaction 3 127 | rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} 128 | - equation: OH + OH <=> O + H2O # Reaction 4 129 | rate-constant: {A: 1.23e+04, b: 2.62, Ea: -1880.0} 130 | - equation: H2 + M <=> H + H + M # Reaction 5 131 | type: three-body 132 | rate-constant: {A: 4.577e+19, b: -1.4, Ea: 1.044e+05} 133 | efficiencies: {H2: 2.5, H2O: 12.0} 134 | - equation: O + O + M <=> O2 + M # Reaction 6 135 | type: three-body 136 | rate-constant: {A: 6.165e+15, b: -0.5, Ea: 0.0} 137 | efficiencies: {H2: 2.5, H2O: 12.0} 138 | - equation: O + H + M <=> OH + M # Reaction 7 139 | type: three-body 140 | rate-constant: {A: 4.714e+18, b: -1.0, Ea: 0.0} 141 | efficiencies: {H2: 2.5, H2O: 12.0} 142 | - equation: H + OH + M <=> H2O + M # Reaction 8 143 | type: three-body 144 | rate-constant: {A: 2.24e+22, b: -2.0, Ea: 0.0} 145 | efficiencies: {H2: 2.5, H2O: 6.3} 146 | - equation: H + O2 + M <=> HO2 + M # Reaction 9 147 | type: three-body 148 | rate-constant: {A: 6.17e+19, b: -1.42, Ea: 0.0} 149 | efficiencies: {H2: 2.5, H2O: 12.0} 150 | - equation: HO2 + H <=> H2 + O2 # Reaction 10 151 | rate-constant: {A: 6.63e+13, b: 0.0, Ea: 2130.0} 152 | - equation: HO2 + H <=> OH + OH # Reaction 11 153 | rate-constant: {A: 1.69e+14, b: 0.0, Ea: 874.0} 154 | - equation: HO2 + O <=> O2 + OH # Reaction 12 155 | rate-constant: {A: 1.81e+13, b: 0.0, Ea: -400.0} 156 | - equation: HO2 + OH <=> H2O + O2 # Reaction 13 157 | rate-constant: {A: 1.45e+16, b: -1.0, Ea: 0.0} 158 | - equation: HO2 + HO2 <=> H2O2 + O2 # Reaction 14 159 | rate-constant: {A: 3.02e+12, b: 0.0, Ea: 1390.0} 160 | - equation: H2O2 + M <=> OH + OH + M # Reaction 15 161 | type: three-body 162 | rate-constant: {A: 1.202e+17, b: 0.0, Ea: 4.55e+04} 163 | efficiencies: {H2: 2.5, H2O: 12.0} 164 | - equation: H2O2 + H <=> H2O + OH # Reaction 16 165 | rate-constant: {A: 1.0e+13, b: 0.0, Ea: 3590.0} 166 | - equation: H2O2 + H <=> HO2 + H2 # Reaction 17 167 | rate-constant: {A: 4.82e+13, b: 0.0, Ea: 7950.0} 168 | - equation: H2O2 + O <=> OH + HO2 # Reaction 18 169 | rate-constant: {A: 9.55e+06, b: 2.0, Ea: 3970.0} 170 | - equation: H2O2 + OH <=> HO2 + H2O # Reaction 19 171 | rate-constant: {A: 7.0e+12, b: 0.0, Ea: 1430.0} 172 | -------------------------------------------------------------------------------- /examples/tsrAnalysis/TSR.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import sys 9 | import PyCSP.Functions as csp 10 | import PyCSP.utils as utils 11 | 12 | #create gas from original mechanism file hydrogen.cti 13 | gas = csp.CanteraCSP('hydrogen.yaml') 14 | 15 | #set the gas state 16 | T = 1000 17 | P = ct.one_atm 18 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 19 | gas.TP = T, P 20 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 21 | gas.constP = P 22 | #set jacobiantype 23 | gas.jacobiantype = 'full' 24 | 25 | #integrate ODE 26 | r = ct.IdealGasConstPressureReactor(gas) 27 | sim = ct.ReactorNet([r]) 28 | time = 0.0 29 | states = ct.SolutionArray(gas, extra=['t']) 30 | 31 | 32 | evals = [] 33 | Revec = [] 34 | Levec = [] 35 | fvec = [] 36 | M = [] 37 | tsr = [] 38 | 39 | sim.initial_time = 0.0 40 | while sim.time < 10: 41 | sim.step() 42 | states.append(r.thermo.state, t=sim.time) 43 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 44 | lam,R,L,f = gas.get_kernel() 45 | NofDM = gas.calc_exhausted_modes(rtol=1.0e-3,atol=1.0e-10) 46 | omegatau = gas.calc_TSR() 47 | evals.append(lam) 48 | Revec.append(R) 49 | Levec.append(L) 50 | fvec.append(f) 51 | M.append(NofDM) 52 | tsr.append(omegatau) 53 | 54 | 55 | evals = np.array(evals) 56 | Revec = np.array(Revec) 57 | Levec = np.array(Levec) 58 | fvec = np.array(fvec) 59 | M = np.array(M) 60 | tsr = np.array(tsr) 61 | 62 | 63 | #plot solution 64 | print('plotting ODE solution...') 65 | plt.clf() 66 | plt.subplot(2, 2, 1) 67 | plt.plot(states.t, states.T) 68 | plt.xlabel('Time (s)') 69 | plt.ylabel('Temperature (K)') 70 | plt.xlim(0., 0.002) 71 | plt.subplot(2, 2, 2) 72 | plt.plot(states.t, states.X[:,gas.species_index('OH')]) 73 | plt.xlabel('Time (s)') 74 | plt.ylabel('OH Mole Fraction') 75 | plt.xlim(0., 0.002) 76 | plt.subplot(2, 2, 3) 77 | plt.plot(states.t, states.X[:,gas.species_index('H')]) 78 | plt.xlabel('Time (s)') 79 | plt.ylabel('H Mole Fraction') 80 | plt.xlim(0., 0.002) 81 | plt.subplot(2, 2, 4) 82 | plt.plot(states.t, states.X[:,gas.species_index('H2')]) 83 | plt.xlabel('Time (s)') 84 | plt.ylabel('H2 Mole Fraction') 85 | plt.xlim(0., 0.002) 86 | plt.tight_layout() 87 | plt.show(block = False) 88 | plt.savefig('traj.png', dpi=500, transparent=False) 89 | 90 | #plot eigenvalues and lambda_M+1 91 | evalM = utils.select_eval(evals,M) 92 | logevals = np.clip(np.log10(1.0+np.abs(evals.real)),0,100)*np.sign(evals.real) 93 | logevalM = np.clip(np.log10(1.0+np.abs(evalM.real)),0,100)*np.sign(evalM.real) 94 | logTSR = np.clip(np.log10(1.0+np.abs(tsr)),0,100)*np.sign(tsr) 95 | print('plotting eigenvalues...') 96 | fig, ax = plt.subplots(figsize=(6,4)) 97 | for idx in range(evals.shape[1]): 98 | #color = next(ax._get_lines.prop_cycler)['color'] 99 | ax.plot(states.t, logevals[:,idx], color='black', marker='.', markersize = 5,linestyle = 'None') 100 | ax.plot(states.t, logevalM, color='orange', marker='.', markersize = 4,linestyle = 'None', label='lam(M+1)') 101 | ax.plot(states.t, logTSR, color='green', marker='.', markersize = 2,linestyle = 'None', label='TSR') 102 | ax.set_xlabel('time (s)') 103 | ax.set_ylabel('evals') 104 | ax.set_ylim([-9, 6]) 105 | ax.set_xlim([0., 0.001]) 106 | ax.grid(False) 107 | ax.legend() 108 | plt.show(block = False) 109 | plt.savefig('TSR.png', dpi=500, transparent=False) 110 | 111 | -------------------------------------------------------------------------------- /examples/tsrAnalysis/TSR_ext.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import matplotlib.colors as clr 9 | import sys 10 | import PyCSP.Functions as csp 11 | import PyCSP.utils as utils 12 | 13 | #create gas from original mechanism file hydrogen.cti 14 | gas = csp.CanteraCSP('chaos12.yaml') 15 | 16 | #-------IMPORT DATA--------- 17 | #read data from file 18 | try: 19 | state = np.loadtxt('flamelet_state.dat') 20 | except FileNotFoundError: 21 | print(f"It looks like flamelet_state.dat is not in this folder. ") 22 | print(f"A likely cause is that the data file coulds not be checked out due to github bandwidth limits. Shoot me an e-mail at riccardo.malpicagalassi [at] uniroma1.it and I will send you the file.") 23 | exit() 24 | 25 | time = state[:,0] 26 | counter = state[:,1] 27 | zeta = state[:,2] 28 | Pressure = state[:,3] 29 | Temp = state[:,4] 30 | Y = state[:,5:] 31 | 32 | try: 33 | rhsD = np.loadtxt('flamelet_rhsDiff.dat') 34 | except FileNotFoundError: 35 | print(f"It looks like flamelet_rhsDiff.dat is not in this folder. ") 36 | print(f"A likely cause is that the data file coulds not be checked out due to github bandwidth limits. Shoot me an e-mail at riccardo.malpicagalassi [at] uniroma1.it and I will send you the file.") 37 | exit() 38 | 39 | 40 | diffTemp = rhsD[:,3] 41 | diffY = rhsD[:,4:] 42 | 43 | #set jacobiantype 44 | gas.jacobiantype = 'full' 45 | 46 | evals = [] 47 | M = [] 48 | tsr = [] 49 | Mext = [] 50 | tsrext = [] 51 | 52 | 53 | print('Analyzing %i data points, may take a while...' %len(state)) 54 | 55 | for step in range(time.shape[0]): 56 | #for step in range(begin,end): 57 | gas.constP = Pressure[step] 58 | stateYT = np.append(Y[step],Temp[step]) 59 | rhsdiffYT = np.append(diffY[step],diffTemp[step]) 60 | gas.set_stateYT(stateYT) 61 | lam,R,L,f = gas.get_kernel() 62 | omegatau, NofDM = gas.calc_TSR(getM=True) 63 | omegatauext, NofDMext = gas.calc_extended_TSR(getMext=True,diff=rhsdiffYT) 64 | 65 | evals.append(lam) 66 | M.append(NofDM) 67 | tsr.append(omegatau) 68 | Mext.append(NofDMext) 69 | tsrext.append(omegatauext) 70 | 71 | evals = np.array(evals) 72 | Mext = np.array(Mext) 73 | tsrext = np.array(tsrext) 74 | M = np.array(M) 75 | tsr = np.array(tsr) 76 | 77 | 78 | chosenslice = np.isclose(time,4.059e-3,atol=1e-8) 79 | 80 | beginslice=np.where(chosenslice)[0][0] 81 | endslice=np.where(chosenslice)[0][-1]+1 82 | 83 | TSRAPI = [] 84 | 85 | for step in range(beginslice,endslice): 86 | gas.constP = Pressure[step] 87 | stateYT = np.append(Y[step],Temp[step]) 88 | rhsdiffYT = np.append(diffY[step],diffTemp[step]) 89 | gas.set_stateYT(stateYT) 90 | gas.update_kernel() 91 | omegatauext, api = gas.calc_extended_TSRindices(diff=rhsdiffYT, getTSRext=True) 92 | 93 | TSRAPI.append(api) 94 | 95 | TSRAPI = np.array(TSRAPI) 96 | 97 | #plot eigenvalues and lambda_M+1 98 | evalM = utils.select_eval(evals,M) 99 | logevals = np.clip(np.log10(1.0+np.abs(evals.real)),0,100)*np.sign(evals.real) 100 | logTSR = np.clip(np.log10(1.0+np.abs(tsr)),0,100)*np.sign(tsr) 101 | logTSRext = np.clip(np.log10(1.0+np.abs(tsrext)),0,100)*np.sign(tsrext) 102 | print('plotting eigenvalues...') 103 | fig, ax = plt.subplots(figsize=(6,4)) 104 | for idx in range(evals.shape[1]): 105 | ax.plot(zeta[beginslice:endslice], logevals[beginslice:endslice,idx], color='gray', marker='.', markersize = 3,linestyle = 'None') 106 | ax.plot(zeta[beginslice:endslice], logTSR[beginslice:endslice], color='red', marker='.', markersize = 6,linestyle = '-', label='TSR') 107 | ax.plot(zeta[beginslice:endslice], logTSRext[beginslice:endslice], color='green', marker='.', markersize = 5,linestyle = '-', label='TSRext') 108 | 109 | ax.set_xlabel('mixture fraction z') 110 | ax.set_ylabel('evals') 111 | ax.set_ylim([-9, 6]) 112 | ax.set_xlim([0.16, 0.24]) 113 | ax.grid(False) 114 | ax.legend() 115 | plt.show(block = False) 116 | plt.savefig('TSR.png', dpi=500, transparent=False) 117 | 118 | 119 | #plot TSR-API 120 | procnames = np.concatenate((gas.reaction_names(),np.char.add("conv-",gas.species_names+["Temperature"]),np.char.add("diff-",gas.species_names+["Temperature"]))) 121 | nr=len(gas.reaction_names()) 122 | nv=len(gas.species_names)+1 123 | thr = 0.15 124 | TSRreacIdx = np.unique(np.nonzero(np.abs(TSRAPI) > thr)[1]) #indexes of processes with TSRapi > thr 125 | TSRreac = TSRAPI[:,TSRreacIdx] 126 | 127 | source_cmap=plt.cm.get_cmap('tab20') 128 | c_list=[] 129 | for color in np.arange(0,1.05,0.05): 130 | c_list.append( source_cmap(color) ) 131 | c_list.reverse() 132 | c_list = c_list[1::4]+c_list[::4]+c_list[2::4]+c_list[3::4] 133 | 134 | print('plotting TSR-API indices...') 135 | fig, ax1 = plt.subplots(figsize=(9,4)) 136 | ax2 = ax1.twinx() 137 | for idx in range(len(TSRreacIdx)): 138 | #color = next(ax._get_lines.prop_cycler)['color'] 139 | ax1.plot(zeta[beginslice:endslice], TSRreac[:,idx], c=c_list[idx] , label=procnames[TSRreacIdx[idx]], linestyle='-' if TSRreacIdx[idx] < nr else '--' if nr < TSRreacIdx[idx] < nr+nv else '-.') 140 | ax2.plot(zeta[beginslice:endslice],Temp[beginslice:endslice], label="Temperature", c='black') 141 | ax1.set_xlabel('mixture fraction Z [-]') 142 | ax1.set_ylabel('TSR API') 143 | ax2.set_ylabel('Temperature [K]') 144 | ax1.set_xlim([0.16, 0.24]) 145 | ax2.set_ylim([900, 1700]) 146 | ax1.grid(False) 147 | box = ax.get_position() 148 | ax1.set_position([box.x0, box.y0, box.width * 1.2, box.height]) 149 | ax1.legend(loc='center right', bbox_to_anchor=(2.5, 0.5)) 150 | plt.show(block = False) 151 | plt.savefig('TSR-API.png', dpi=800, transparent=False) 152 | 153 | #contour plot 154 | t = list(set(time)) 155 | z = list(set(zeta)) 156 | t.sort() 157 | z.sort() 158 | tsr2d = logTSR.reshape((len(t),len(z))) 159 | tsrext2d = logTSRext.reshape((len(t),len(z))) 160 | temp2d = Temp.reshape((len(t),len(z))) 161 | x1, y1 = np.meshgrid(z, t) 162 | 163 | 164 | fig, ax = plt.subplots(figsize=(8,6)) 165 | im=plt.contourf(x1, y1, tsr2d, 20, cmap='RdYlBu_r', vmin = -6, vmax = 6) 166 | plt.xlim(0, 0.5) 167 | plt.ylim(0.0039, 0.00415) 168 | plt.ticklabel_format(axis="y", style="sci", scilimits=(0,0)) 169 | plt.xlabel("mixture fraction Z [-]") 170 | plt.ylabel("time [s]") 171 | plt.title("TSR") 172 | cb = plt.colorbar(im) 173 | cb.set_ticks((-6,-4,-2,0,2,4,6)) 174 | plt.show(block = False) 175 | plt.savefig('TSRcontour.png', dpi=500, transparent=False) 176 | 177 | fig, ax = plt.subplots(figsize=(8,6)) 178 | im=plt.contourf(x1, y1, tsrext2d, 20, cmap='RdYlBu_r', vmin = -6, vmax = 6) 179 | plt.xlim(0, 0.5) 180 | plt.ylim(0.0039, 0.00415) 181 | plt.ticklabel_format(axis="y", style="sci", scilimits=(0,0)) 182 | plt.xlabel("mixture fraction Z [-]") 183 | plt.ylabel("time [s]") 184 | plt.title("ext-TSR") 185 | cb = plt.colorbar(im) 186 | cb.set_ticks((-6,-4,-2,0,2,4,6)) 187 | plt.show(block = False) 188 | plt.savefig('TSRextcontour.png', dpi=500, transparent=False) 189 | 190 | fig, ax = plt.subplots(figsize=(8,6)) 191 | img3 = ax.contour(x1, y1, temp2d, cmap='Greys', levels=[1000,1100,1200,1300,1400,1500,1600,1700,1800],linewidths=0.75, alpha=0.6) 192 | img1 = plt.contourf(x1, y1, tsrext2d, 20, cmap='Reds', vmin = 2, vmax = 6, levels=[4,5,6]) 193 | img2 = plt.contourf(x1, y1, tsr2d, 20, cmap='Greens', vmin = 4, vmax = 6, levels=[4,5,6]) 194 | #ax.clabel(img3, img3.levels, inline=True, fontsize=10) 195 | plt.xlim(0, 0.5) 196 | plt.ylim(0.0039, 0.00415) 197 | plt.ticklabel_format(axis="y", style="sci", scilimits=(0,0)) 198 | plt.xlabel("mixture fraction Z [-]") 199 | plt.ylabel("time [s]") 200 | plt.show(block = False) 201 | plt.savefig('fronts.png', dpi=500, transparent=False) 202 | -------------------------------------------------------------------------------- /examples/tsrAnalysis/chaos12.yaml: -------------------------------------------------------------------------------- 1 | description: |- 2 | "" 3 | 4 | generator: cti2yaml 5 | cantera-version: 3.0.0 6 | date: Mon, 11 Sep 2023 10:12:26 +0200 7 | input-files: [chaos12.cti] 8 | 9 | units: {length: cm, quantity: mol, activation-energy: cal/mol} 10 | 11 | phases: 12 | - name: gas 13 | thermo: ideal-gas 14 | elements: [C, H, O, N] 15 | species: [H, H2, O, OH, H2O, CO, HCO, O2, HO2, H2O2, CO2, N2] 16 | kinetics: gas 17 | reactions: all 18 | state: 19 | T: 300.0 20 | P: 1.01325e+05 21 | 22 | species: 23 | - name: H 24 | composition: {H: 1} 25 | thermo: 26 | model: NASA7 27 | temperature-ranges: [300.0, 1000.0, 5000.0] 28 | data: 29 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547163e+04, -0.4601176] 30 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547163e+04, -0.4601176] 31 | note: '120186' 32 | - name: H2 33 | composition: {H: 2} 34 | thermo: 35 | model: NASA7 36 | temperature-ranges: [300.0, 1000.0, 5000.0] 37 | data: 38 | - [3.298124, 8.249442e-04, -8.143015e-07, -9.475434e-11, 4.134872e-13, 39 | -1012.521, -3.294094] 40 | - [2.991423, 7.000644e-04, -5.633829e-08, -9.231578e-12, 1.582752e-15, 41 | -835.034, -1.35511] 42 | note: '121286' 43 | - name: O 44 | composition: {O: 1} 45 | thermo: 46 | model: NASA7 47 | temperature-ranges: [300.0, 1000.0, 5000.0] 48 | data: 49 | - [2.946429, -1.638166e-03, 2.421032e-06, -1.602843e-09, 3.890696e-13, 50 | 2.914764e+04, 2.963995] 51 | - [2.54206, -2.755062e-05, -3.102803e-09, 4.551067e-12, -4.368052e-16, 52 | 2.92308e+04, 4.920308] 53 | note: '120186' 54 | - name: OH 55 | composition: {H: 1, O: 1} 56 | thermo: 57 | model: NASA7 58 | temperature-ranges: [200.0, 1000.0, 6000.0] 59 | data: 60 | - [4.12530561, -3.22544939e-03, 6.52764691e-06, -5.79853643e-09, 2.06237379e-12, 61 | 3346.30913, -0.69043296] 62 | - [2.86472886, 1.05650448e-03, -2.59082758e-07, 3.05218674e-11, -1.33195876e-15, 63 | 3683.62875, 5.70164073] 64 | note: S9/01 65 | - name: H2O 66 | composition: {H: 2, O: 1} 67 | thermo: 68 | model: NASA7 69 | temperature-ranges: [300.0, 1000.0, 5000.0] 70 | data: 71 | - [3.386842, 3.474982e-03, -6.354696e-06, 6.968581e-09, -2.506588e-12, 72 | -3.020811e+04, 2.590233] 73 | - [2.672146, 3.056293e-03, -8.73026e-07, 1.200996e-10, -6.391618e-15, 74 | -2.989921e+04, 6.862817] 75 | note: '20387' 76 | - name: CO 77 | composition: {C: 1, O: 1} 78 | thermo: 79 | model: NASA7 80 | temperature-ranges: [300.0, 1000.0, 5000.0] 81 | data: 82 | - [3.262452, 1.511941e-03, -3.881755e-06, 5.581944e-09, -2.474951e-12, 83 | -1.431054e+04, 4.848897] 84 | - [3.025078, 1.442689e-03, -5.630828e-07, 1.018581e-10, -6.910952e-15, 85 | -1.426835e+04, 6.108218] 86 | note: '121286' 87 | - name: HCO 88 | composition: {C: 1, H: 1, O: 1} 89 | thermo: 90 | model: NASA7 91 | temperature-ranges: [300.0, 1000.0, 5000.0] 92 | data: 93 | - [2.89833, 6.199147e-03, -9.623084e-06, 1.089825e-08, -4.574885e-12, 94 | 4159.922, 8.983614] 95 | - [3.557271, 3.345573e-03, -1.335006e-06, 2.470573e-10, -1.713851e-14, 96 | 3916.324, 5.552299] 97 | note: '121286' 98 | - name: O2 99 | composition: {O: 2} 100 | thermo: 101 | model: NASA7 102 | temperature-ranges: [300.0, 1000.0, 5000.0] 103 | data: 104 | - [3.212936, 1.127486e-03, -5.75615e-07, 1.313877e-09, -8.768554e-13, 105 | -1005.249, 6.034738] 106 | - [3.697578, 6.135197e-04, -1.258842e-07, 1.775281e-11, -1.136435e-15, 107 | -1233.93, 3.189166] 108 | note: '121386' 109 | - name: HO2 110 | composition: {H: 1, O: 2} 111 | thermo: 112 | model: NASA7 113 | temperature-ranges: [200.0, 1000.0, 3500.0] 114 | data: 115 | - [4.30179801, -4.74912051e-03, 2.11582891e-05, -2.42763894e-08, 9.29225124e-12, 116 | 294.80804, 3.71666245] 117 | - [4.0172109, 2.23982013e-03, -6.3365815e-07, 1.1424637e-10, -1.07908535e-14, 118 | 111.856713, 3.78510215] 119 | note: L5/89 120 | - name: H2O2 121 | composition: {H: 2, O: 2} 122 | thermo: 123 | model: NASA7 124 | temperature-ranges: [300.0, 1000.0, 5000.0] 125 | data: 126 | - [3.388754, 6.569226e-03, -1.485013e-07, -4.625806e-09, 2.471515e-12, 127 | -1.766315e+04, 6.785363] 128 | - [4.573167, 4.336136e-03, -1.474689e-06, 2.348904e-10, -1.431654e-14, 129 | -1.800696e+04, 0.501137] 130 | note: '120186' 131 | - name: CO2 132 | composition: {C: 1, O: 2} 133 | thermo: 134 | model: NASA7 135 | temperature-ranges: [300.0, 1000.0, 5000.0] 136 | data: 137 | - [2.275725, 9.922072e-03, -1.040911e-05, 6.866687e-09, -2.11728e-12, 138 | -4.837314e+04, 10.18849] 139 | - [4.453623, 3.140169e-03, -1.278411e-06, 2.393997e-10, -1.669033e-14, 140 | -4.896696e+04, -0.9553959] 141 | note: '121286' 142 | - name: N2 143 | composition: {N: 2} 144 | thermo: 145 | model: NASA7 146 | temperature-ranges: [300.0, 1000.0, 5000.0] 147 | data: 148 | - [3.298677, 1.40824e-03, -3.963222e-06, 5.641515e-09, -2.444855e-12, 149 | -1020.9, 3.950372] 150 | - [2.92664, 1.487977e-03, -5.684761e-07, 1.009704e-10, -6.753351e-15, 151 | -922.7977, 5.980528] 152 | note: '121286' 153 | 154 | reactions: 155 | - equation: H + O2 <=> O + OH # Reaction 1 156 | rate-constant: {A: 3.547e+15, b: -0.406, Ea: 1.6599e+04} 157 | - equation: O + H2 <=> H + OH # Reaction 2 158 | rate-constant: {A: 5.08e+04, b: 2.67, Ea: 6290.0} 159 | - equation: H2 + OH <=> H2O + H # Reaction 3 160 | rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} 161 | - equation: O + H2O <=> OH + OH # Reaction 4 162 | rate-constant: {A: 2.97e+06, b: 2.02, Ea: 1.34e+04} 163 | - equation: H2 + M <=> H + H + M # Reaction 5 164 | type: three-body 165 | rate-constant: {A: 4.577e+19, b: -1.4, Ea: 1.0438e+05} 166 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.5, H2O: 12.0} 167 | - equation: O + O + M <=> O2 + M # Reaction 6 168 | type: three-body 169 | rate-constant: {A: 6.165e+15, b: -0.5, Ea: 0.0} 170 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.5, H2O: 12.0} 171 | - equation: O + H + M <=> OH + M # Reaction 7 172 | type: three-body 173 | rate-constant: {A: 4.714e+18, b: -1.0, Ea: 0.0} 174 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.5, H2O: 12.0} 175 | - equation: H + OH + M <=> H2O + M # Reaction 8 176 | type: three-body 177 | rate-constant: {A: 3.8e+22, b: -2.0, Ea: 0.0} 178 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.5, H2O: 12.0} 179 | - equation: H + O2 (+ M) <=> HO2 (+ M) # Reaction 9 180 | type: falloff 181 | low-P-rate-constant: {A: 6.366e+20, b: -1.72, Ea: 524.8} 182 | high-P-rate-constant: {A: 1.475e+12, b: 0.6, Ea: 0.0} 183 | Troe: {A: 0.8, T3: 1.0e-30, T1: 1.0e+30} 184 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.0, H2O: 11.0, O2: 0.78} 185 | - equation: HO2 + H <=> H2 + O2 # Reaction 10 186 | rate-constant: {A: 1.66e+13, b: 0.0, Ea: 823.0} 187 | - equation: HO2 + H <=> OH + OH # Reaction 11 188 | rate-constant: {A: 7.079e+13, b: 0.0, Ea: 295.0} 189 | - equation: HO2 + O <=> O2 + OH # Reaction 12 190 | rate-constant: {A: 3.25e+13, b: 0.0, Ea: 0.0} 191 | - equation: HO2 + OH <=> H2O + O2 # Reaction 13 192 | rate-constant: {A: 2.89e+13, b: 0.0, Ea: -497.0} 193 | - equation: HO2 + HO2 <=> H2O2 + O2 # Reaction 14 194 | rate-constant: {A: 4.2e+14, b: 0.0, Ea: 1.1982e+04} 195 | duplicate: true 196 | - equation: HO2 + HO2 <=> H2O2 + O2 # Reaction 15 197 | rate-constant: {A: 1.3e+11, b: 0.0, Ea: -1629.3} 198 | duplicate: true 199 | - equation: H2O2 (+ M) <=> OH + OH (+ M) # Reaction 16 200 | type: falloff 201 | low-P-rate-constant: {A: 1.202e+17, b: 0.0, Ea: 4.55e+04} 202 | high-P-rate-constant: {A: 2.951e+14, b: 0.0, Ea: 4.843e+04} 203 | Troe: {A: 0.5, T3: 1.0e-30, T1: 1.0e+30} 204 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.5, H2O: 12.0} 205 | - equation: H2O2 + H <=> H2O + OH # Reaction 17 206 | rate-constant: {A: 2.41e+13, b: 0.0, Ea: 3970.0} 207 | - equation: H2O2 + H <=> HO2 + H2 # Reaction 18 208 | rate-constant: {A: 4.82e+13, b: 0.0, Ea: 7950.0} 209 | - equation: H2O2 + O <=> OH + HO2 # Reaction 19 210 | rate-constant: {A: 9.55e+06, b: 2.0, Ea: 3970.0} 211 | - equation: H2O2 + OH <=> HO2 + H2O # Reaction 20 212 | rate-constant: {A: 1.0e+12, b: 0.0, Ea: 0.0} 213 | duplicate: true 214 | - equation: H2O2 + OH <=> HO2 + H2O # Reaction 21 215 | rate-constant: {A: 5.8e+14, b: 0.0, Ea: 9557.0} 216 | duplicate: true 217 | - equation: CO + O (+ M) <=> CO2 (+ M) # Reaction 22 218 | type: falloff 219 | low-P-rate-constant: {A: 1.55e+24, b: -2.79, Ea: 4191.0} 220 | high-P-rate-constant: {A: 1.8e+10, b: 0.0, Ea: 2384.0} 221 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.5, H2O: 12.0} 222 | - equation: CO + O2 <=> CO2 + O # Reaction 23 223 | rate-constant: {A: 2.53e+12, b: 0.0, Ea: 4.77e+04} 224 | - equation: CO + HO2 <=> CO2 + OH # Reaction 24 225 | rate-constant: {A: 1.57e+05, b: 2.18, Ea: 1.794e+04} 226 | - equation: CO + OH <=> CO2 + H # Reaction 25 227 | rate-constant: {A: 2.229e+05, b: 1.89, Ea: -1158.7} 228 | - equation: HCO + M <=> H + CO + M # Reaction 26 229 | type: three-body 230 | rate-constant: {A: 4.7485e+11, b: 0.659, Ea: 1.4874e+04} 231 | efficiencies: {CO: 1.9, CO2: 3.8, H2: 2.5, H2O: 12.0} 232 | - equation: HCO + O2 <=> CO + HO2 # Reaction 27 233 | rate-constant: {A: 7.58e+12, b: 0.0, Ea: 410.0} 234 | - equation: HCO + H <=> CO + H2 # Reaction 28 235 | rate-constant: {A: 7.23e+13, b: 0.0, Ea: 0.0} 236 | - equation: HCO + O <=> CO + OH # Reaction 29 237 | rate-constant: {A: 3.02e+13, b: 0.0, Ea: 0.0} 238 | - equation: HCO + OH <=> CO + H2O # Reaction 30 239 | rate-constant: {A: 3.02e+13, b: 0.0, Ea: 0.0} 240 | - equation: HCO + O <=> CO2 + H # Reaction 31 241 | rate-constant: {A: 3.0e+13, b: 0.0, Ea: 0.0} 242 | - equation: HCO + HO2 <=> CO2 + OH + H # Reaction 32 243 | rate-constant: {A: 3.0e+13, b: 0.0, Ea: 0.0} 244 | - equation: HCO + HCO <=> H2 + CO + CO # Reaction 33 245 | rate-constant: {A: 3.0e+12, b: 0.0, Ea: 0.0} 246 | -------------------------------------------------------------------------------- /examples/tsrAnalysis/hydrogen.yaml: -------------------------------------------------------------------------------- 1 | description: |- 2 | "" 3 | 4 | generator: cti2yaml 5 | cantera-version: 3.0.0 6 | date: Mon, 11 Sep 2023 10:12:22 +0200 7 | input-files: [hydrogen.cti] 8 | 9 | units: {length: cm, quantity: mol, activation-energy: cal/mol} 10 | 11 | phases: 12 | - name: gas 13 | thermo: ideal-gas 14 | elements: [H, O, N] 15 | species: [H2, O2, O, OH, H2O, H, HO2, H2O2, N2] 16 | kinetics: gas 17 | reactions: all 18 | state: 19 | T: 300.0 20 | P: 1.01325e+05 21 | 22 | species: 23 | - name: H2 24 | composition: {H: 2} 25 | thermo: 26 | model: NASA7 27 | temperature-ranges: [300.0, 1000.0, 5000.0] 28 | data: 29 | - [3.298124, 8.249441e-04, -8.143015e-07, -9.475434e-11, 4.134872e-13, 30 | -1012.5209, -3.294094] 31 | - [2.991423, 7.000644e-04, -5.633828e-08, -9.231578e-12, 1.5827519e-15, 32 | -835.034, -1.3551101] 33 | note: '121286' 34 | - name: O2 35 | composition: {O: 2} 36 | thermo: 37 | model: NASA7 38 | temperature-ranges: [300.0, 1000.0, 5000.0] 39 | data: 40 | - [3.212936, 1.1274864e-03, -5.75615e-07, 1.3138773e-09, -8.768554e-13, 41 | -1005.249, 6.034737] 42 | - [3.697578, 6.135197e-04, -1.258842e-07, 1.775281e-11, -1.1364354e-15, 43 | -1233.9301, 3.189165] 44 | note: '121386' 45 | - name: O 46 | composition: {O: 1} 47 | thermo: 48 | model: NASA7 49 | temperature-ranges: [300.0, 1000.0, 5000.0] 50 | data: 51 | - [2.946428, -1.6381665e-03, 2.421031e-06, -1.6028431e-09, 3.890696e-13, 52 | 2.914764e+04, 2.963995] 53 | - [2.542059, -2.755061e-05, -3.102803e-09, 4.551067e-12, -4.368051e-16, 54 | 2.92308e+04, 4.920308] 55 | note: '120186' 56 | - name: OH 57 | composition: {H: 1, O: 1} 58 | thermo: 59 | model: NASA7 60 | temperature-ranges: [300.0, 1000.0, 5000.0] 61 | data: 62 | - [3.637266, 1.85091e-04, -1.6761646e-06, 2.387202e-09, -8.431442e-13, 63 | 3606.781, 1.3588605] 64 | - [2.88273, 1.0139743e-03, -2.276877e-07, 2.174683e-11, -5.126305e-16, 65 | 3886.888, 5.595712] 66 | note: '121286' 67 | - name: H2O 68 | composition: {H: 2, O: 1} 69 | thermo: 70 | model: NASA7 71 | temperature-ranges: [300.0, 1000.0, 5000.0] 72 | data: 73 | - [3.386842, 3.474982e-03, -6.354696e-06, 6.968581e-09, -2.506588e-12, 74 | -3.020811e+04, 2.590232] 75 | - [2.672145, 3.056293e-03, -8.73026e-07, 1.2009964e-10, -6.391618e-15, 76 | -2.989921e+04, 6.862817] 77 | note: '20387' 78 | - name: H 79 | composition: {H: 1} 80 | thermo: 81 | model: NASA7 82 | temperature-ranges: [300.0, 1000.0, 5000.0] 83 | data: 84 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 85 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 86 | note: '120186' 87 | - name: HO2 88 | composition: {H: 1, O: 2} 89 | thermo: 90 | model: NASA7 91 | temperature-ranges: [300.0, 1000.0, 5000.0] 92 | data: 93 | - [2.979963, 4.996697e-03, -3.790997e-06, 2.354192e-09, -8.089024e-13, 94 | 176.2273, 9.222724] 95 | - [4.072191, 2.131296e-03, -5.308145e-07, 6.112269e-11, -2.841164e-15, 96 | -157.9727, 3.476029] 97 | note: '20387' 98 | - name: H2O2 99 | composition: {H: 2, O: 2} 100 | thermo: 101 | model: NASA7 102 | temperature-ranges: [300.0, 1000.0, 5000.0] 103 | data: 104 | - [3.388753, 6.569226e-03, -1.4850125e-07, -4.625805e-09, 2.471514e-12, 105 | -1.766314e+04, 6.785363] 106 | - [4.573167, 4.336136e-03, -1.4746888e-06, 2.348903e-10, -1.4316536e-14, 107 | -1.800696e+04, 0.5011369] 108 | note: '120186' 109 | - name: N2 110 | composition: {N: 2} 111 | thermo: 112 | model: NASA7 113 | temperature-ranges: [300.0, 1000.0, 5000.0] 114 | data: 115 | - [3.298677, 1.4082404e-03, -3.963222e-06, 5.641515e-09, -2.444854e-12, 116 | -1020.8999, 3.950372] 117 | - [2.92664, 1.4879768e-03, -5.68476e-07, 1.0097038e-10, -6.753351e-15, 118 | -922.7977, 5.980528] 119 | note: '121286' 120 | 121 | reactions: 122 | - equation: H + O2 <=> O + OH # Reaction 1 123 | rate-constant: {A: 1.915e+14, b: 0.0, Ea: 1.644e+04} 124 | - equation: O + H2 <=> H + OH # Reaction 2 125 | rate-constant: {A: 5.08e+04, b: 2.67, Ea: 6290.0} 126 | - equation: H2 + OH <=> H2O + H # Reaction 3 127 | rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} 128 | - equation: OH + OH <=> O + H2O # Reaction 4 129 | rate-constant: {A: 1.23e+04, b: 2.62, Ea: -1880.0} 130 | - equation: H2 + M <=> H + H + M # Reaction 5 131 | type: three-body 132 | rate-constant: {A: 4.577e+19, b: -1.4, Ea: 1.044e+05} 133 | efficiencies: {H2: 2.5, H2O: 12.0} 134 | - equation: O + O + M <=> O2 + M # Reaction 6 135 | type: three-body 136 | rate-constant: {A: 6.165e+15, b: -0.5, Ea: 0.0} 137 | efficiencies: {H2: 2.5, H2O: 12.0} 138 | - equation: O + H + M <=> OH + M # Reaction 7 139 | type: three-body 140 | rate-constant: {A: 4.714e+18, b: -1.0, Ea: 0.0} 141 | efficiencies: {H2: 2.5, H2O: 12.0} 142 | - equation: H + OH + M <=> H2O + M # Reaction 8 143 | type: three-body 144 | rate-constant: {A: 2.24e+22, b: -2.0, Ea: 0.0} 145 | efficiencies: {H2: 2.5, H2O: 6.3} 146 | - equation: H + O2 + M <=> HO2 + M # Reaction 9 147 | type: three-body 148 | rate-constant: {A: 6.17e+19, b: -1.42, Ea: 0.0} 149 | efficiencies: {H2: 2.5, H2O: 12.0} 150 | - equation: HO2 + H <=> H2 + O2 # Reaction 10 151 | rate-constant: {A: 6.63e+13, b: 0.0, Ea: 2130.0} 152 | - equation: HO2 + H <=> OH + OH # Reaction 11 153 | rate-constant: {A: 1.69e+14, b: 0.0, Ea: 874.0} 154 | - equation: HO2 + O <=> O2 + OH # Reaction 12 155 | rate-constant: {A: 1.81e+13, b: 0.0, Ea: -400.0} 156 | - equation: HO2 + OH <=> H2O + O2 # Reaction 13 157 | rate-constant: {A: 1.45e+16, b: -1.0, Ea: 0.0} 158 | - equation: HO2 + HO2 <=> H2O2 + O2 # Reaction 14 159 | rate-constant: {A: 3.02e+12, b: 0.0, Ea: 1390.0} 160 | - equation: H2O2 + M <=> OH + OH + M # Reaction 15 161 | type: three-body 162 | rate-constant: {A: 1.202e+17, b: 0.0, Ea: 4.55e+04} 163 | efficiencies: {H2: 2.5, H2O: 12.0} 164 | - equation: H2O2 + H <=> H2O + OH # Reaction 16 165 | rate-constant: {A: 1.0e+13, b: 0.0, Ea: 3590.0} 166 | - equation: H2O2 + H <=> HO2 + H2 # Reaction 17 167 | rate-constant: {A: 4.82e+13, b: 0.0, Ea: 7950.0} 168 | - equation: H2O2 + O <=> OH + HO2 # Reaction 18 169 | rate-constant: {A: 9.55e+06, b: 2.0, Ea: 3970.0} 170 | - equation: H2O2 + OH <=> HO2 + H2O # Reaction 19 171 | rate-constant: {A: 7.0e+12, b: 0.0, Ea: 1430.0} 172 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmalpica/PyCSP/1817a59bb44250fa15f97273e77e8ab4bb7e950c/logo.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Cantera >= 3.0 2 | matplotlib 3 | numpy 4 | pandas -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import setuptools 4 | 5 | with open("README.md", "r", encoding="utf-8") as fh: 6 | long_description = fh.read() 7 | 8 | setuptools.setup( 9 | name="PyCSP", 10 | version="1.4.0", 11 | author="Riccardo Malpica Galassi", 12 | author_email="riccardo.malpicagalassi@uniroma1.it", 13 | description="A collection of tools for the Computational Singular Perturbation analysis of chemically reacting flows", 14 | url="https://github.com/rmalpica/PyCSP", 15 | packages=['PyCSP'], 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | python_requires='>=3', 22 | install_requires=[ 23 | "Cantera>=3.0","numpy","matplotlib","pandas", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /tests/hydrogen.yaml: -------------------------------------------------------------------------------- 1 | description: |- 2 | "" 3 | 4 | generator: cti2yaml 5 | cantera-version: 3.0.0 6 | date: Mon, 11 Sep 2023 09:52:53 +0200 7 | input-files: [hydrogen.cti] 8 | 9 | units: {length: cm, quantity: mol, activation-energy: cal/mol} 10 | 11 | phases: 12 | - name: gas 13 | thermo: ideal-gas 14 | elements: [H, O, N] 15 | species: [H2, O2, O, OH, H2O, H, HO2, H2O2, N2] 16 | kinetics: gas 17 | reactions: all 18 | state: 19 | T: 300.0 20 | P: 1.01325e+05 21 | 22 | species: 23 | - name: H2 24 | composition: {H: 2} 25 | thermo: 26 | model: NASA7 27 | temperature-ranges: [300.0, 1000.0, 5000.0] 28 | data: 29 | - [3.298124, 8.249441e-04, -8.143015e-07, -9.475434e-11, 4.134872e-13, 30 | -1012.5209, -3.294094] 31 | - [2.991423, 7.000644e-04, -5.633828e-08, -9.231578e-12, 1.5827519e-15, 32 | -835.034, -1.3551101] 33 | note: '121286' 34 | - name: O2 35 | composition: {O: 2} 36 | thermo: 37 | model: NASA7 38 | temperature-ranges: [300.0, 1000.0, 5000.0] 39 | data: 40 | - [3.212936, 1.1274864e-03, -5.75615e-07, 1.3138773e-09, -8.768554e-13, 41 | -1005.249, 6.034737] 42 | - [3.697578, 6.135197e-04, -1.258842e-07, 1.775281e-11, -1.1364354e-15, 43 | -1233.9301, 3.189165] 44 | note: '121386' 45 | - name: O 46 | composition: {O: 1} 47 | thermo: 48 | model: NASA7 49 | temperature-ranges: [300.0, 1000.0, 5000.0] 50 | data: 51 | - [2.946428, -1.6381665e-03, 2.421031e-06, -1.6028431e-09, 3.890696e-13, 52 | 2.914764e+04, 2.963995] 53 | - [2.542059, -2.755061e-05, -3.102803e-09, 4.551067e-12, -4.368051e-16, 54 | 2.92308e+04, 4.920308] 55 | note: '120186' 56 | - name: OH 57 | composition: {H: 1, O: 1} 58 | thermo: 59 | model: NASA7 60 | temperature-ranges: [300.0, 1000.0, 5000.0] 61 | data: 62 | - [3.637266, 1.85091e-04, -1.6761646e-06, 2.387202e-09, -8.431442e-13, 63 | 3606.781, 1.3588605] 64 | - [2.88273, 1.0139743e-03, -2.276877e-07, 2.174683e-11, -5.126305e-16, 65 | 3886.888, 5.595712] 66 | note: '121286' 67 | - name: H2O 68 | composition: {H: 2, O: 1} 69 | thermo: 70 | model: NASA7 71 | temperature-ranges: [300.0, 1000.0, 5000.0] 72 | data: 73 | - [3.386842, 3.474982e-03, -6.354696e-06, 6.968581e-09, -2.506588e-12, 74 | -3.020811e+04, 2.590232] 75 | - [2.672145, 3.056293e-03, -8.73026e-07, 1.2009964e-10, -6.391618e-15, 76 | -2.989921e+04, 6.862817] 77 | note: '20387' 78 | - name: H 79 | composition: {H: 1} 80 | thermo: 81 | model: NASA7 82 | temperature-ranges: [300.0, 1000.0, 5000.0] 83 | data: 84 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 85 | - [2.5, 0.0, 0.0, 0.0, 0.0, 2.547162e+04, -0.4601176] 86 | note: '120186' 87 | - name: HO2 88 | composition: {H: 1, O: 2} 89 | thermo: 90 | model: NASA7 91 | temperature-ranges: [300.0, 1000.0, 5000.0] 92 | data: 93 | - [2.979963, 4.996697e-03, -3.790997e-06, 2.354192e-09, -8.089024e-13, 94 | 176.2273, 9.222724] 95 | - [4.072191, 2.131296e-03, -5.308145e-07, 6.112269e-11, -2.841164e-15, 96 | -157.9727, 3.476029] 97 | note: '20387' 98 | - name: H2O2 99 | composition: {H: 2, O: 2} 100 | thermo: 101 | model: NASA7 102 | temperature-ranges: [300.0, 1000.0, 5000.0] 103 | data: 104 | - [3.388753, 6.569226e-03, -1.4850125e-07, -4.625805e-09, 2.471514e-12, 105 | -1.766314e+04, 6.785363] 106 | - [4.573167, 4.336136e-03, -1.4746888e-06, 2.348903e-10, -1.4316536e-14, 107 | -1.800696e+04, 0.5011369] 108 | note: '120186' 109 | - name: N2 110 | composition: {N: 2} 111 | thermo: 112 | model: NASA7 113 | temperature-ranges: [300.0, 1000.0, 5000.0] 114 | data: 115 | - [3.298677, 1.4082404e-03, -3.963222e-06, 5.641515e-09, -2.444854e-12, 116 | -1020.8999, 3.950372] 117 | - [2.92664, 1.4879768e-03, -5.68476e-07, 1.0097038e-10, -6.753351e-15, 118 | -922.7977, 5.980528] 119 | note: '121286' 120 | 121 | reactions: 122 | - equation: H + O2 <=> O + OH # Reaction 1 123 | rate-constant: {A: 1.915e+14, b: 0.0, Ea: 1.644e+04} 124 | - equation: O + H2 <=> H + OH # Reaction 2 125 | rate-constant: {A: 5.08e+04, b: 2.67, Ea: 6290.0} 126 | - equation: H2 + OH <=> H2O + H # Reaction 3 127 | rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} 128 | - equation: OH + OH <=> O + H2O # Reaction 4 129 | rate-constant: {A: 1.23e+04, b: 2.62, Ea: -1880.0} 130 | - equation: H2 + M <=> H + H + M # Reaction 5 131 | type: three-body 132 | rate-constant: {A: 4.577e+19, b: -1.4, Ea: 1.044e+05} 133 | efficiencies: {H2: 2.5, H2O: 12.0} 134 | - equation: O + O + M <=> O2 + M # Reaction 6 135 | type: three-body 136 | rate-constant: {A: 6.165e+15, b: -0.5, Ea: 0.0} 137 | efficiencies: {H2: 2.5, H2O: 12.0} 138 | - equation: O + H + M <=> OH + M # Reaction 7 139 | type: three-body 140 | rate-constant: {A: 4.714e+18, b: -1.0, Ea: 0.0} 141 | efficiencies: {H2: 2.5, H2O: 12.0} 142 | - equation: H + OH + M <=> H2O + M # Reaction 8 143 | type: three-body 144 | rate-constant: {A: 2.24e+22, b: -2.0, Ea: 0.0} 145 | efficiencies: {H2: 2.5, H2O: 6.3} 146 | - equation: H + O2 + M <=> HO2 + M # Reaction 9 147 | type: three-body 148 | rate-constant: {A: 6.17e+19, b: -1.42, Ea: 0.0} 149 | efficiencies: {H2: 2.5, H2O: 12.0} 150 | - equation: HO2 + H <=> H2 + O2 # Reaction 10 151 | rate-constant: {A: 6.63e+13, b: 0.0, Ea: 2130.0} 152 | - equation: HO2 + H <=> OH + OH # Reaction 11 153 | rate-constant: {A: 1.69e+14, b: 0.0, Ea: 874.0} 154 | - equation: HO2 + O <=> O2 + OH # Reaction 12 155 | rate-constant: {A: 1.81e+13, b: 0.0, Ea: -400.0} 156 | - equation: HO2 + OH <=> H2O + O2 # Reaction 13 157 | rate-constant: {A: 1.45e+16, b: -1.0, Ea: 0.0} 158 | - equation: HO2 + HO2 <=> H2O2 + O2 # Reaction 14 159 | rate-constant: {A: 3.02e+12, b: 0.0, Ea: 1390.0} 160 | - equation: H2O2 + M <=> OH + OH + M # Reaction 15 161 | type: three-body 162 | rate-constant: {A: 1.202e+17, b: 0.0, Ea: 4.55e+04} 163 | efficiencies: {H2: 2.5, H2O: 12.0} 164 | - equation: H2O2 + H <=> H2O + OH # Reaction 16 165 | rate-constant: {A: 1.0e+13, b: 0.0, Ea: 3590.0} 166 | - equation: H2O2 + H <=> HO2 + H2 # Reaction 17 167 | rate-constant: {A: 4.82e+13, b: 0.0, Ea: 7950.0} 168 | - equation: H2O2 + O <=> OH + HO2 # Reaction 18 169 | rate-constant: {A: 9.55e+06, b: 2.0, Ea: 3970.0} 170 | - equation: H2O2 + OH <=> HO2 + H2O # Reaction 19 171 | rate-constant: {A: 7.0e+12, b: 0.0, Ea: 1430.0} 172 | -------------------------------------------------------------------------------- /tests/test_kernel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import sys 9 | import PyCSP.Functions as csp 10 | 11 | #create gas from original mechanism file hydrogen.cti 12 | gas = csp.CanteraCSP('hydrogen.yaml') 13 | 14 | 15 | #set the gas state 16 | T = 1000 17 | P = ct.one_atm 18 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 19 | gas.TP = T, P 20 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 21 | gas.constP = P 22 | gas.jacobiantype = 'full' 23 | 24 | #integrate ODE 25 | r = ct.IdealGasConstPressureReactor(gas) 26 | sim = ct.ReactorNet([r]) 27 | time = 0.0 28 | states = ct.SolutionArray(gas, extra=['t']) 29 | 30 | evals = [] 31 | Revec = [] 32 | Levec = [] 33 | fvec = [] 34 | sim.set_initial_time(0.0) 35 | while sim.time < 1000: 36 | sim.step() 37 | states.append(r.thermo.state, t=sim.time) 38 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 39 | lam,R,L,f = gas.get_kernel() 40 | evals.append(lam) 41 | Revec.append(R) 42 | Levec.append(L) 43 | fvec.append(f) 44 | 45 | evals = np.array(evals) 46 | Revec = np.array(Revec) 47 | Levec = np.array(Levec) 48 | fvec = np.array(fvec) 49 | 50 | 51 | #plot solution 52 | print('plotting ODE solution...') 53 | plt.clf() 54 | plt.subplot(2, 2, 1) 55 | plt.plot(states.t, states.T) 56 | plt.xlabel('Time (s)') 57 | plt.ylabel('Temperature (K)') 58 | plt.xlim(0., 0.002) 59 | plt.subplot(2, 2, 2) 60 | plt.plot(states.t, states.X[:,gas.species_index('OH')]) 61 | plt.xlabel('Time (s)') 62 | plt.ylabel('OH Mole Fraction') 63 | plt.xlim(0., 0.002) 64 | plt.subplot(2, 2, 3) 65 | plt.plot(states.t, states.X[:,gas.species_index('H')]) 66 | plt.xlabel('Time (s)') 67 | plt.ylabel('H Mole Fraction') 68 | plt.xlim(0., 0.002) 69 | plt.subplot(2, 2, 4) 70 | plt.plot(states.t, states.X[:,gas.species_index('H2')]) 71 | plt.xlabel('Time (s)') 72 | plt.ylabel('H2 Mole Fraction') 73 | plt.xlim(0., 0.002) 74 | plt.tight_layout() 75 | plt.show() 76 | 77 | #plot eigenvalues 78 | logevals = np.clip(np.log10(1.0+np.abs(evals.real)),0,100)*np.sign(evals.real) 79 | print('plotting eigenvalues...') 80 | fig, ax = plt.subplots(figsize=(6,4)) 81 | for idx in range(evals.shape[1]): 82 | #color = next(ax._get_lines.prop_cycler)['color'] 83 | ax.plot(states.t, logevals[:,idx], color='black', marker='.', markersize = 2,linestyle = 'None') 84 | ax.set_xlabel('time (s)') 85 | ax.set_ylabel('evals') 86 | ax.set_ylim([-9, 6]) 87 | ax.set_xlim([0., 0.001]) 88 | ax.grid(False) 89 | plt.show() 90 | #plt.savefig('figures/prediction_combined.png', dpi=500, transparent=False) 91 | -------------------------------------------------------------------------------- /tests/test_rhs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import sys 9 | import PyCSP.Functions as csp 10 | 11 | #create gas from original mechanism file hydrogen.cti 12 | gas = csp.CanteraCSP('hydrogen.yaml') 13 | 14 | #set the gas state 15 | T = 1000 16 | P = ct.one_atm 17 | gas.TP = T, P 18 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 19 | #push pressure 20 | gas.constP = P 21 | 22 | 23 | #integrate ODE 24 | r = ct.IdealGasConstPressureReactor(gas) 25 | sim = ct.ReactorNet([r]) 26 | time = 0.0 27 | states = ct.SolutionArray(gas, extra=['t']) 28 | 29 | RHS = [] 30 | splitRHS = [] 31 | varnames = np.array(gas.species_names+['Temperature']) 32 | sim.set_initial_time(0.0) 33 | while sim.time < 1.5e-3: 34 | sim.step() 35 | states.append(r.thermo.state, t=sim.time) 36 | print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.u)) 37 | rhs = gas.source 38 | Smat = gas.generalized_Stoich_matrix 39 | rvec = gas.R_vector 40 | splitrhs = np.dot(Smat,rvec) 41 | checksplitrhs = np.isclose(rhs, splitrhs, rtol=1e-6, atol=0, equal_nan=False) 42 | if(np.any(checksplitrhs == False)): 43 | idx = np.array([*range(len(rhs))]) 44 | print('Mismatch between numerical RHS and S.r') 45 | print(varnames[~checksplitrhs],rhs[~checksplitrhs],splitrhs[~checksplitrhs]) 46 | RHS.append(rhs) 47 | splitRHS.append(splitrhs) 48 | 49 | 50 | RHS = np.array(RHS) 51 | splitRHS = np.array(splitRHS) 52 | 53 | #plot solution 54 | print('plotting ODE solution...') 55 | plt.clf() 56 | plt.subplot(2, 2, 1) 57 | plt.plot(states.t, states.T) 58 | plt.xlabel('Time (s)') 59 | plt.ylabel('Temperature (K)') 60 | plt.xlim(0., 0.002) 61 | plt.subplot(2, 2, 2) 62 | plt.plot(states.t, states.X[:,gas.species_index('OH')]) 63 | plt.xlabel('Time (s)') 64 | plt.ylabel('OH Mole Fraction') 65 | plt.xlim(0., 0.002) 66 | plt.subplot(2, 2, 3) 67 | plt.plot(states.t, states.X[:,gas.species_index('H')]) 68 | plt.xlabel('Time (s)') 69 | plt.ylabel('H Mole Fraction') 70 | plt.xlim(0., 0.002) 71 | plt.subplot(2, 2, 4) 72 | plt.plot(states.t, states.X[:,gas.species_index('H2')]) 73 | plt.xlabel('Time (s)') 74 | plt.ylabel('H2 Mole Fraction') 75 | plt.xlim(0., 0.002) 76 | plt.tight_layout() 77 | plt.show() 78 | 79 | #plot RHS(T) 80 | 81 | print('plotting RHS...') 82 | fig, ax = plt.subplots(figsize=(6,4)) 83 | ax.plot(states.t, RHS[:,-1], color='black', label='rhs') 84 | ax.plot(states.t, splitRHS[:,-1], color='red', linestyle='--',label='S.r') 85 | ax.set_xlabel('time (s)') 86 | ax.set_ylabel('rhs[T]') 87 | ax.set_xlim([0., 0.001]) 88 | ax.grid(False) 89 | ax.legend() 90 | plt.show() 91 | #plt.savefig('figures/prediction_combined.png', dpi=500, transparent=False) 92 | -------------------------------------------------------------------------------- /tests/test_rhs_constV.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Riccardo Malpica Galassi, Sapienza University, Roma, Italy 4 | """ 5 | import cantera as ct 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import PyCSP.Functions as csp 9 | 10 | #create gas from original mechanism file hydrogen.cti 11 | gas = csp.CanteraCSP('hydrogen.yaml') 12 | 13 | #set the gas state 14 | T = 1000 15 | P = ct.one_atm 16 | gas.TP = T, P 17 | #gas.TPX = T, P, "H2:2.0, O2:1, N2:3.76" 18 | gas.set_equivalence_ratio(1.0, 'H2', 'O2:1, N2:3.76') 19 | 20 | #push density 21 | rho = gas.density 22 | gas.constRho = rho 23 | 24 | 25 | #set jacobiantype 26 | gas.jacobiantype = 'full' 27 | 28 | #integrate ODE 29 | r = ct.IdealGasReactor(gas) 30 | sim = ct.ReactorNet([r]) 31 | time = 0.0 32 | states = ct.SolutionArray(gas, extra=['t']) 33 | 34 | RHS = [] 35 | splitRHS = [] 36 | varnames = np.array(gas.species_names+['Temperature']) 37 | sim.set_initial_time(0.0) 38 | while sim.time < 1.e-3: 39 | sim.step() 40 | states.append(r.thermo.state, t=sim.time) 41 | print('%10.3e %10.3f %10.3f %10.3f %14.6e' % (sim.time, r.T, r.thermo.P, r.thermo.density, r.thermo.u)) 42 | rhs = gas.source 43 | Smat = gas.generalized_Stoich_matrix 44 | rvec = gas.R_vector 45 | splitrhs = np.dot(Smat,rvec) 46 | checksplitrhs = np.isclose(rhs, splitrhs, rtol=1e-6, atol=1e-6, equal_nan=False) 47 | if(np.any(checksplitrhs == False)): 48 | idx = np.array([*range(len(rhs))]) 49 | print('Mismatch between numerical RHS and S.r') 50 | print(varnames[~checksplitrhs],rhs[~checksplitrhs],splitrhs[~checksplitrhs]) 51 | RHS.append(rhs) 52 | splitRHS.append(splitrhs) 53 | 54 | 55 | RHS = np.array(RHS) 56 | splitRHS = np.array(splitRHS) 57 | 58 | #plot solution 59 | print('plotting ODE solution...') 60 | plt.clf() 61 | plt.subplot(2, 2, 1) 62 | plt.plot(states.t, states.T) 63 | plt.xlabel('Time (s)') 64 | plt.ylabel('Temperature (K)') 65 | plt.xlim(0., 0.001) 66 | plt.subplot(2, 2, 2) 67 | plt.plot(states.t, states.X[:,gas.species_index('OH')]) 68 | plt.xlabel('Time (s)') 69 | plt.ylabel('OH Mole Fraction') 70 | plt.xlim(0., 0.001) 71 | plt.subplot(2, 2, 3) 72 | plt.plot(states.t, states.X[:,gas.species_index('H')]) 73 | plt.xlabel('Time (s)') 74 | plt.ylabel('H Mole Fraction') 75 | plt.xlim(0., 0.001) 76 | plt.subplot(2, 2, 4) 77 | plt.plot(states.t, states.X[:,gas.species_index('H2')]) 78 | plt.xlabel('Time (s)') 79 | plt.ylabel('H2 Mole Fraction') 80 | plt.xlim(0., 0.001) 81 | plt.tight_layout() 82 | plt.show() 83 | 84 | #plot RHS(T) 85 | 86 | print('plotting RHS...') 87 | fig, ax = plt.subplots(figsize=(6,4)) 88 | ax.plot(states.t, RHS[:,-1], color='black', label='rhs') 89 | ax.plot(states.t, splitRHS[:,-1], color='red', linestyle='--',label='S.r') 90 | ax.set_xlabel('time (s)') 91 | ax.set_ylabel('rhs[T]') 92 | ax.set_xlim([0., 0.001]) 93 | ax.grid(False) 94 | ax.legend() 95 | plt.show() 96 | --------------------------------------------------------------------------------