Based on the original Rocket Workbench on SourceForge in CVS at: https://sourceforge.net/projects/rocketworkbench
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

563 lines
14 KiB

  1. /* thermo.c - Compute thermodynamic properties of individual
  2. species and composition of species */
  3. /* $Id: thermo.c,v 1.2 2001/02/22 19:48:44 antoine Exp $ */
  4. /* Copyright (C) 2000 */
  5. /* Antoine Lefebvre <antoine.lefebvre@polymtl.ca> */
  6. /* Mark Pinese <pinese@cyberwizards.com.au> */
  7. /* */
  8. /* Licensed under the GPLv2 */
  9. #include <math.h>
  10. #include <string.h>
  11. #include <stdio.h>
  12. #include <ctype.h>
  13. #include <stdlib.h>
  14. #include "thermo.h"
  15. #include "compat.h"
  16. #include "conversion.h"
  17. /**************************************************************
  18. These variables hold the number of records for propellant and thermo data
  19. ***************************************************************/
  20. unsigned long num_propellant, num_thermo;
  21. /* global variable containing the information about chemical species */
  22. propellant_t *propellant_list;
  23. thermo_t *thermo_list;
  24. /****************************************************************
  25. VARIABLE: Contain the molar mass of element by atomic number
  26. molar_mass[0] contain hydrogen and so on.
  27. Data come from Sargent-Welch 1996
  28. *****************************************************************/
  29. const float molar_mass[N_SYMB] = {
  30. 1.00794, 4.002602, 6.941, 9.012182, 10.811, 12.0107,
  31. 14.00674, 15.9994, 18.9984032, 20.11797, 22.989770, 24.305,
  32. 26.981538, 28.0855, 30.973761, 32.066, 35.4527, 39.948,
  33. 39.0983, 40.078, 44.95591, 47.88, 50.9415, 51.996,
  34. 54.938, 55.847, 58.9332, 58.6934, 63.546, 65.39,
  35. 69.723, 72.61, 74.9216, 78.96, 79.904, 83.80,
  36. 85.4678, 87.62, 88.9059, 91.224, 92.9064, 95.94,
  37. 98.0, 101.07, 102.9055, 106.42, 107.868, 112.41,
  38. 114.82, 118.71, 121.757, 127.60, 126.9045, 131.29,
  39. 132.9054, 137.33, 138.9055, 140.12, 140.9077, 144.24,
  40. 145., 150.36, 151.965, 157.25, 158.9253, 162.50,
  41. 164.9303, 167.26, 168.9342, 173.04, 174.967, 178.49,
  42. 180.9479, 183.85, 186.207, 190.2, 192.22, 195.08,
  43. 196.9665, 200.59, 204.383, 207.2, 208.9804, 209.,
  44. 210., 222., 223., 226.0254, 227., 232.0381,
  45. 231.0359, 238.029, 237.0482, 244., 12.011, 9.01218,
  46. 10.811, 24.305, 26.98154, 257.0, 0, 2};
  47. /****************************************************************
  48. VARIABLE: Contain the symbol of the element in the same way as
  49. for the molar mass.
  50. COMMENTS: It is use in the loading of the data file to recognize
  51. the chemical formula.
  52. *****************************************************************/
  53. const char symb[N_SYMB][3] = {
  54. "H ","HE","LI","BE","B ","C ","N ","O ",
  55. "F ","NE","NA","MG","AL","SI","P ","S ","CL","AR","K ","CA",
  56. "SC","TI","V ","CR","MN","FE","CO","NI","CU","ZN","GA","GE",
  57. "AS","SE","BR","KR","RB","SR","Y ","ZR","NB","MO","TC","RU",
  58. "RH","PD","AG","CD","IN","SN","SB","TE","I ","XE","CS","BA",
  59. "LA","CE","PR","ND","PM","SM","EU","GD","TB","DY","HO","ER",
  60. "TM","YB","LU","HF","TA","W ","RE","OS","IR","PT","AU","HG","TL",
  61. "PB","BI","PO","AT","RN","FR","RA","AC","TH","PA","U ","NP",
  62. "U6","U5","U1","U2","U3","U4","FM",
  63. "E ", "D " }; /* the E stand for electron and D for deuterium*/
  64. /* Enthalpy in the standard state (Dimensionless) */
  65. double enthalpy_0(int sp, float T)
  66. {
  67. thermo_t *s = (thermo_list + sp);
  68. double val;
  69. int pos = 0, i;
  70. if (T < s->range[0][0]) /* Temperature below the lower range */
  71. {
  72. pos = 0;
  73. } /*Temperature above the higher range */
  74. else if (T >= s->range[s->nint-1][1])
  75. {
  76. pos = s->nint - 1;
  77. }
  78. else
  79. {
  80. for (i = 0; i < s->nint; i++) /* Find the range */
  81. {
  82. if ((T >= s->range[i][0]) && (T < s->range[i][1]))
  83. pos = i;
  84. }
  85. }
  86. /* parametric equation for dimentionless enthalpy */
  87. val = -s->param[pos][0]*pow(T, -2) + s->param[pos][1]*pow(T, -1)*log(T)
  88. + s->param[pos][2] + s->param[pos][3]*T/2 + s->param[pos][4]*pow(T, 2)/3
  89. + s->param[pos][5]*pow(T, 3)/4 + s->param[pos][6]*pow(T, 4)/5
  90. + s->param[pos][7]/T;
  91. return val; /* dimensionless enthalpy */
  92. }
  93. /* Entropy in the standard state (Dimensionless)*/
  94. double entropy_0(int sp, float T)
  95. {
  96. thermo_t *s = (thermo_list + sp);
  97. double val;
  98. int pos = 0, i;
  99. if (T < s->range[0][0])
  100. {
  101. pos = 0;
  102. }
  103. else if (T >= s->range[s->nint-1][1])
  104. {
  105. pos = s->nint - 1;
  106. }
  107. else
  108. {
  109. for (i = 0; i < s->nint; i++)
  110. {
  111. if ((T >= s->range[i][0]) && (T < s->range[i][1]))
  112. pos = i;
  113. }
  114. }
  115. /* parametric equation for dimentionless entropy */
  116. val = -s->param[pos][0]*pow(T, -2)/2 - s->param[pos][1]*pow(T, -1)
  117. + s->param[pos][2]*log(T) + s->param[pos][3]*T
  118. + s->param[pos][4]*pow(T, 2)/2
  119. + s->param[pos][5]*pow(T, 3)/3 + s->param[pos][6]*pow(T, 4)/4
  120. + s->param[pos][8];
  121. return val;
  122. }
  123. /* Specific heat in the standard state (Dimensionless) */
  124. double specific_heat_0(int sp, float T)
  125. {
  126. thermo_t *s = (thermo_list + sp);
  127. double val;
  128. int pos = 0, i;
  129. if (T < s->range[0][0])
  130. {
  131. pos = 0;
  132. }
  133. else if (T >= s->range[s->nint-1][1])
  134. {
  135. pos = s->nint - 1;
  136. }
  137. else
  138. {
  139. for (i = 0; i < s->nint; i++)
  140. {
  141. if ((T >= s->range[i][0]) && (T < s->range[i][1]))
  142. pos = i;
  143. }
  144. }
  145. /* parametric equation for dimentionless specific_heat */
  146. val = s->param[pos][0]*pow(T, -2) + s->param[pos][1]*pow(T, -1)
  147. + s->param[pos][2] + s->param[pos][3]*T + s->param[pos][4]*pow(T, 2)
  148. + s->param[pos][5]*pow(T, 3) + s->param[pos][6]*pow(T, 4);
  149. return val;
  150. }
  151. /* Dimensionless Gibbs free energy in the standard state */
  152. double gibbs_0(int sp, float T)
  153. {
  154. return enthalpy_0(sp, T) - entropy_0(sp, T); /* dimensionless */
  155. }
  156. /* Check if the species is in its range of definition
  157. 0 if out of range, 1 if ok */
  158. int temperature_check(int sp, float T)
  159. {
  160. thermo_t *s = (thermo_list + sp);
  161. if ((T > s->range[s->nint-1][1]) || (T < s->range[0][0]))
  162. return 0;
  163. return 1;
  164. }
  165. /* This function return the transition temperature of the species
  166. considered which is nearest of the temperature T */
  167. double transition_temperature(int sp, float T)
  168. {
  169. thermo_t *s = (thermo_list + sp);
  170. /* first assume that the lowest temperature is the good one */
  171. double transition_T = s->range[0][0];
  172. /* verify if we did the good bet */
  173. if (fabs(transition_T - T) > fabs(s->range[s->nint-1][1] - T))
  174. {
  175. transition_T = s->range[s->nint-1][1];
  176. }
  177. return transition_T;
  178. }
  179. double entropy(int sp, state_t st, double ln_nj_n, float T, float P)
  180. {
  181. double s;
  182. switch (st)
  183. {
  184. case GAS:
  185. /* The thermodynamic data are based on a standard state pressure
  186. of 1 bar (10^5 Pa) */
  187. s = entropy_0(sp, T) - ln_nj_n - log(P * ATM_TO_BAR);
  188. break;
  189. case CONDENSED:
  190. s = entropy_0(sp, T);
  191. break;
  192. default:
  193. s = 0;
  194. }
  195. return s;
  196. }
  197. /* J/mol T is in K, P is in atm */
  198. double gibbs(int sp, state_t st, double ln_nj_n, float T, float P)
  199. {
  200. double g;
  201. switch (st)
  202. {
  203. case GAS:
  204. g = gibbs_0(sp, T) + ln_nj_n + log(P * ATM_TO_BAR);
  205. break;
  206. case CONDENSED:
  207. g = gibbs_0(sp, T);
  208. break;
  209. default:
  210. g = 0;
  211. }
  212. return g;
  213. }
  214. double propellant_molar_mass(int molecule)
  215. {
  216. int i = 0, coef;
  217. double ans = 0;
  218. while ((coef = (propellant_list + molecule)->coef[i]))
  219. {
  220. ans += coef * molar_mass[(propellant_list + molecule)->elem[i]];
  221. i++;
  222. }
  223. return ans;
  224. }
  225. /* J/mol */
  226. double heat_of_formation(int molecule)
  227. {
  228. double hf = (propellant_list + molecule)->heat *
  229. propellant_molar_mass(molecule);
  230. return hf;
  231. }
  232. /* should not be in thermo.c */
  233. double propellant_enthalpy(equilibrium_t *e)
  234. {
  235. int i;
  236. double h = 0.0;
  237. for (i = 0; i < e->propellant.ncomp; i++)
  238. {
  239. h += e->propellant.coef[i] * heat_of_formation (e->propellant.molecule[i])
  240. / propellant_mass (e);
  241. }
  242. return h;
  243. }
  244. /* should not be in thermo.c */
  245. double product_enthalpy(equilibrium_t *e)
  246. {
  247. int i;
  248. double h = 0.0;
  249. for (i = 0; i < e->product.n[GAS]; i++)
  250. {
  251. h += e->product.coef[GAS][i] * enthalpy_0(e->product.species[GAS][i], e->properties.T);
  252. }
  253. for (i = 0; i < e->product.n[CONDENSED]; i++)
  254. {
  255. h += e->product.coef[CONDENSED][i] * enthalpy_0(e->product.species[CONDENSED][i], e->properties.T);
  256. }
  257. return h;
  258. }
  259. /* should not be in thermo.c */
  260. double product_entropy(equilibrium_t *e)
  261. {
  262. int i;
  263. double ent = 0.0;
  264. for (i = 0; i < e->product.n[GAS]; i++)
  265. {
  266. ent += e->product.coef[GAS][i]*entropy(e->product.species[GAS][i], GAS,
  267. e->itn.ln_nj[i] - e->itn.ln_n,
  268. e->properties.T, e->properties.P);
  269. }
  270. for (i = 0; i < e->product.n[CONDENSED]; i++)
  271. {
  272. ent += e->product.coef[CONDENSED][i]*entropy(e->product.species[CONDENSED][i],
  273. CONDENSED, 0, e->properties.T, e->properties.P);
  274. }
  275. return ent;
  276. }
  277. /* should not be in thermo.c */
  278. /* The specific heat of the mixture for frozen performance */
  279. double mixture_specific_heat_0(equilibrium_t *e, double temp)
  280. {
  281. int i;
  282. double cp = 0.0;
  283. /* for gases */
  284. for (i = 0; i < e->product.n[GAS]; i++)
  285. {
  286. cp += e->product.coef[GAS][i]*specific_heat_0(e->product.species[GAS][i], temp);
  287. }
  288. /* for condensed */
  289. for (i = 0; i < e->product.n[CONDENSED]; i++)
  290. {
  291. cp += e->product.coef[CONDENSED][i]*
  292. specific_heat_0(e->product.species[CONDENSED][i], temp);
  293. }
  294. return cp;
  295. }
  296. int thermo_search(char *str)
  297. {
  298. int i;
  299. int last = -1;
  300. for (i = 0; i < num_thermo; i++)
  301. {
  302. if (!(STRNCASECMP(str, (thermo_list + i)->name, strlen(str))))
  303. {
  304. last = i;
  305. printf("%-5d %s\n", i, (thermo_list + i)->name);
  306. }
  307. }
  308. return last;
  309. }
  310. int propellant_search(char *str)
  311. {
  312. int i;
  313. int last = -1;
  314. for (i = 0; i < num_propellant; i++)
  315. {
  316. if (!(STRNCASECMP(str, (propellant_list + i)->name, strlen(str))))
  317. {
  318. last = i;
  319. printf("%-5d %s\n", i, (propellant_list + i)->name);
  320. }
  321. }
  322. return last;
  323. }
  324. int atomic_number(char *symbole)
  325. {
  326. int i;
  327. int element = -1;
  328. /* find the atomic number of the element */
  329. for (i = 0; i < N_SYMB; i++)
  330. {
  331. if (!STRCASECMP(symbole, symb[i]))
  332. {
  333. element = i;
  334. break;
  335. }
  336. }
  337. return element;
  338. }
  339. int compute_density(composition_t *c)
  340. {
  341. short i;
  342. double mass = 0;
  343. c->density = 0.0;
  344. for (i = 0; i < c->ncomp; i++)
  345. {
  346. mass += c->coef[i] * propellant_molar_mass(c->molecule[i]);
  347. }
  348. for (i = 0; i < c->ncomp; i++)
  349. {
  350. if ((propellant_list + c->molecule[i])->density != 0.0)
  351. {
  352. c->density += c->coef[i] * propellant_molar_mass(c->molecule[i])
  353. / (mass * (propellant_list + c->molecule[i])->density);
  354. }
  355. }
  356. if (c->density != 0.0)
  357. {
  358. c->density = 1/c->density;
  359. }
  360. return 0;
  361. }
  362. /* This fonction return the offset of the molecule in the propellant_list
  363. the argument is the chemical formula of the molecule */
  364. int propellant_search_by_formula(char *str)
  365. {
  366. int i = 0, j ;
  367. char tmp[5];
  368. char *ptr;
  369. int elem[6] = {0, 0, 0, 0, 0, 1};
  370. int coef[6] = {0, 0, 0, 0, 0, 0};
  371. int molecule = -1;
  372. ptr = str; /* beginning of the string */
  373. while ( (i < 6) && ((ptr - str) < strlen(str)) )
  374. {
  375. if (isupper(*ptr) && islower(*(ptr+1)) && (isupper(*(ptr+2)) ||
  376. iscntrl(*(ptr+2))) )
  377. {
  378. tmp[0] = *ptr;
  379. tmp[1] = toupper(*(ptr+1));
  380. tmp[2] = '\0';
  381. /* find the atomic number of the element */
  382. elem[i] = atomic_number(tmp);
  383. coef[i] = 1;
  384. i++;
  385. ptr += 2;
  386. }
  387. else if (isupper(*ptr) && (isupper(*(ptr+1)) ||
  388. iscntrl(*(ptr+1))) )
  389. {
  390. tmp[0] = *ptr;
  391. tmp[1] = ' ';
  392. tmp[2] = '\0';
  393. elem[i] = atomic_number(tmp);
  394. coef[i] = 1;
  395. i++;
  396. ptr++;
  397. }
  398. else if (isupper(*ptr) && isdigit(*(ptr+1)))
  399. {
  400. tmp[0] = *ptr;
  401. tmp[1] = ' ';
  402. tmp[2] = '\0';
  403. elem[i] = atomic_number(tmp);
  404. j = 0;
  405. do
  406. {
  407. tmp[j] = *(ptr + 1 + j);
  408. j++;
  409. } while (isdigit(*(ptr + 1 + j)));
  410. tmp[j] = '\0';
  411. coef[i] = atoi(tmp);
  412. i++;
  413. ptr = ptr + j + 1;
  414. }
  415. else if (isupper(*ptr) && islower(*(ptr+1)) && isdigit(*(ptr+2)))
  416. {
  417. tmp[0] = *ptr;
  418. tmp[1] = toupper(*(ptr+1));
  419. tmp[2] = '\0';
  420. elem[i] = atomic_number(tmp);
  421. j = 0;
  422. while (isdigit(*(ptr + 2 + j)))
  423. {
  424. tmp[j] = *(ptr + 1 + j);
  425. j++;
  426. }
  427. tmp[j] = '\0';
  428. coef[i] = atoi(tmp);
  429. i++;
  430. ptr = ptr + j + 2;
  431. }
  432. }
  433. /*
  434. for (i = 0; i < 6; i++)
  435. {
  436. if (elem[i] != -1)
  437. printf("%s %d\n", symb[elem[i]], coef[i]);
  438. }
  439. */
  440. for (i = 0; i < num_propellant; i++)
  441. {
  442. for (j = 0; j < 6; j++)
  443. {
  444. /* set to the same value as the previous one if the same */
  445. if (!( ((propellant_list+i)->coef[j] == coef[j]) &&
  446. ((propellant_list+i)->elem[j] == elem[j]) ))
  447. break;
  448. }
  449. /* Now search in propellant list for this molecule */
  450. /*
  451. for (j = 0; j < num_propellant; j++)
  452. {
  453. for (i = 0; i < 6; i++)
  454. {
  455. if ( (coef[i] != propellant_element_coef(elem[i], j)) &&
  456. (propellant_list + i)
  457. break;
  458. }
  459. */
  460. if (j == 5) /* we found the molecule ! */
  461. {
  462. /* check if the inverse is true */
  463. molecule = i;
  464. break;
  465. }
  466. }
  467. return molecule;
  468. }
  469. /* Mass of propellant in gram */
  470. double propellant_mass(equilibrium_t *e)
  471. {
  472. int i;
  473. double mass = 0.0;
  474. for (i = 0; i < e->propellant.ncomp; i++)
  475. {
  476. mass += e->propellant.coef[i] *
  477. propellant_molar_mass(e->propellant.molecule[i]);
  478. }
  479. return mass;
  480. }