1 |
/* ASCEND modelling environment |
2 |
Copyright (C) 2008 Carnegie Mellon University |
3 |
|
4 |
This program is free software; you can redistribute it and/or modify |
5 |
it under the terms of the GNU General Public License as published by |
6 |
the Free Software Foundation; either version 2, or (at your option) |
7 |
any later version. |
8 |
|
9 |
This program is distributed in the hope that it will be useful, |
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
GNU General Public License for more details. |
13 |
|
14 |
You should have received a copy of the GNU General Public License |
15 |
along with this program; if not, write to the Free Software |
16 |
Foundation, Inc., 59 Temple Place - Suite 330, |
17 |
Boston, MA 02111-1307, USA. |
18 |
*//** @file |
19 |
Implementation of the reduced molar Helmholtz free energy equation of state. |
20 |
|
21 |
For nomenclature see Tillner-Roth, Harms-Watzenberg and Baehr, Eine neue |
22 |
Fundamentalgleichung f端r Ammoniak. |
23 |
|
24 |
John Pye, 29 Jul 2008. |
25 |
*/ |
26 |
|
27 |
#include <math.h> |
28 |
|
29 |
#include "helmholtz.h" |
30 |
#include "ideal_impl.h" |
31 |
|
32 |
#ifdef TEST |
33 |
#include <assert.h> |
34 |
#include <stdlib.h> |
35 |
#include <stdio.h> |
36 |
#endif |
37 |
|
38 |
#define SQ(X) ((X)*(X)) |
39 |
|
40 |
/* forward decls */ |
41 |
|
42 |
#include "helmholtz_impl.h" |
43 |
|
44 |
/** |
45 |
Function to calculate pressure from Helmholtz free energy EOS, given temperature |
46 |
and mass density. |
47 |
|
48 |
@param T temperature in K |
49 |
@param rho mass density in kg/m続 |
50 |
@return pressure in Pa??? |
51 |
*/ |
52 |
double helmholtz_p(double T, double rho, const HelmholtzData *data){ |
53 |
|
54 |
double tau = data->T_star / T; |
55 |
double delta = rho / data->rho_star; |
56 |
|
57 |
#ifdef TEST |
58 |
assert(data->rho_star!=0); |
59 |
assert(T!=0); |
60 |
assert(!isnan(tau)); |
61 |
assert(!isnan(delta)); |
62 |
assert(!isnan(data->R)); |
63 |
|
64 |
//fprintf(stderr,"p calc: T = %f\n",T); |
65 |
//fprintf(stderr,"p calc: tau = %f\n",tau); |
66 |
//fprintf(stderr,"p calc: rho = %f\n",rho); |
67 |
//fprintf(stderr,"p calc: delta = %f\n",delta); |
68 |
//fprintf(stderr,"p calc: R*T*rho = %f\n",data->R * T * rho); |
69 |
|
70 |
//fprintf(stderr,"T = %f\n", T); |
71 |
//fprintf(stderr,"rhob = %f, rhob* = %f, delta = %f\n", rho/data->M, data->rho_star/data->M, delta); |
72 |
#endif |
73 |
|
74 |
return data->R * T * rho * (1 + delta * helm_resid_del(tau,delta,data)); |
75 |
} |
76 |
|
77 |
/** |
78 |
Function to calculate internal energy from Helmholtz free energy EOS, given |
79 |
temperature and mass density. |
80 |
|
81 |
@param T temperature in K |
82 |
@param rho mass density in kg/m続 |
83 |
@return internal energy in ??? |
84 |
*/ |
85 |
double helmholtz_u(double T, double rho, const HelmholtzData *data){ |
86 |
|
87 |
double tau = data->T_star / T; |
88 |
double delta = rho / data->rho_star; |
89 |
|
90 |
#ifdef TEST |
91 |
assert(data->rho_star!=0); |
92 |
assert(T!=0); |
93 |
assert(!isnan(tau)); |
94 |
assert(!isnan(delta)); |
95 |
assert(!isnan(data->R)); |
96 |
#endif |
97 |
|
98 |
#if 0 |
99 |
fprintf(stderr,"ideal_tau = %f\n",helm_ideal_tau(tau,delta,data->ideal)); |
100 |
fprintf(stderr,"resid_tau = %f\n",helm_resid_tau(tau,delta,data)); |
101 |
fprintf(stderr,"R T = %f\n",data->R * data->T_star); |
102 |
#endif |
103 |
|
104 |
return data->R * data->T_star * (helm_ideal_tau(tau,delta,data->ideal) + helm_resid_tau(tau,delta,data)); |
105 |
} |
106 |
|
107 |
/** |
108 |
Function to calculate enthalpy from Helmholtz free energy EOS, given |
109 |
temperature and mass density. |
110 |
|
111 |
@param T temperature in K |
112 |
@param rho mass density in kg/m続 |
113 |
@return enthalpy in J/kg |
114 |
*/ |
115 |
double helmholtz_h(double T, double rho, const HelmholtzData *data){ |
116 |
|
117 |
double tau = data->T_star / T; |
118 |
double delta = rho / data->rho_star; |
119 |
|
120 |
#ifdef TEST |
121 |
assert(data->rho_star!=0); |
122 |
assert(T!=0); |
123 |
assert(!isnan(tau)); |
124 |
assert(!isnan(delta)); |
125 |
assert(!isnan(data->R)); |
126 |
#endif |
127 |
|
128 |
return data->R * T * (1 + tau * (helm_ideal_tau(tau,delta,data->ideal) + helm_resid_tau(tau,delta,data)) + delta*helm_resid_del(tau,delta,data)); |
129 |
} |
130 |
|
131 |
/** |
132 |
Function to calculate entropy from Helmholtz free energy EOS, given |
133 |
temperature and mass density. |
134 |
|
135 |
@param T temperature in K |
136 |
@param rho mass density in kg/m続 |
137 |
@return entropy in J/kgK |
138 |
*/ |
139 |
double helmholtz_s(double T, double rho, const HelmholtzData *data){ |
140 |
|
141 |
double tau = data->T_star / T; |
142 |
double delta = rho / data->rho_star; |
143 |
|
144 |
#ifdef ENTROPY_DEBUG |
145 |
assert(data->rho_star!=0); |
146 |
assert(T!=0); |
147 |
assert(!isnan(tau)); |
148 |
assert(!isnan(delta)); |
149 |
assert(!isnan(data->R)); |
150 |
|
151 |
fprintf(stderr,"helm_ideal_tau = %f\n",helm_ideal_tau(tau,delta,data->ideal)); |
152 |
fprintf(stderr,"helm_resid_tau = %f\n",helm_resid_tau(tau,delta,data)); |
153 |
fprintf(stderr,"helm_ideal = %f\n",helm_ideal(tau,delta,data->ideal)); |
154 |
fprintf(stderr,"helm_resid = %f\n",helm_resid(tau,delta,data)); |
155 |
#endif |
156 |
return data->R * ( |
157 |
tau * (helm_ideal_tau(tau,delta,data->ideal) + helm_resid_tau(tau,delta,data)) |
158 |
- (helm_ideal(tau,delta,data->ideal) + helm_resid(tau,delta,data)) |
159 |
); |
160 |
} |
161 |
|
162 |
/** |
163 |
Function to calculate Helmholtz energy from the Helmholtz free energy EOS, |
164 |
given temperature and mass density. |
165 |
|
166 |
@param T temperature in K |
167 |
@param rho mass density in kg/m続 |
168 |
@return Helmholtz energy 'a', in J/kg |
169 |
*/ |
170 |
double helmholtz_a(double T, double rho, const HelmholtzData *data){ |
171 |
|
172 |
double tau = data->T_star / T; |
173 |
double delta = rho / data->rho_star; |
174 |
|
175 |
#ifdef TEST |
176 |
assert(data->rho_star!=0); |
177 |
assert(T!=0); |
178 |
assert(!isnan(tau)); |
179 |
assert(!isnan(delta)); |
180 |
assert(!isnan(data->R)); |
181 |
#endif |
182 |
|
183 |
#ifdef HELMHOLTZ_DEBUG |
184 |
fprintf(stderr,"helmholtz_a: T = %f, rho = %f\n",T,rho); |
185 |
fprintf(stderr,"multiplying by RT = %f\n",data->R*T); |
186 |
#endif |
187 |
|
188 |
return data->R * T * (helm_ideal(tau,delta,data->ideal) + helm_resid(tau,delta,data)); |
189 |
} |
190 |
|
191 |
/** |
192 |
Function to calculate isochoric heat capacity from the Helmholtz free energy |
193 |
EOS given temperature and mass density. |
194 |
|
195 |
@param T temperature in K |
196 |
@param rho mass density in kg/m続 |
197 |
@return Isochoric specific heat capacity in J/kg/K. |
198 |
*/ |
199 |
double helmholtz_cv(double T, double rho, const HelmholtzData *data){ |
200 |
double tau = data->T_star / T; |
201 |
double delta = rho / data->rho_star; |
202 |
|
203 |
return - data->R * tau*tau * (helm_ideal_tautau(tau,data->ideal) + helm_resid_tautau(tau,delta,data)); |
204 |
} |
205 |
|
206 |
/** |
207 |
Function to calculate isobaric heat capacity from the Helmholtz free energy |
208 |
EOS given temperature and mass density. |
209 |
|
210 |
@param T temperature in K |
211 |
@param rho mass density in kg/m続 |
212 |
@return Isobaric specific heat capacity in J/kg/K. |
213 |
*/ |
214 |
double helmholtz_cp(double T, double rho, const HelmholtzData *data){ |
215 |
double tau = data->T_star / T; |
216 |
double delta = rho / data->rho_star; |
217 |
|
218 |
double phir_d = helm_resid_del(tau,delta,data); |
219 |
double phir_dd = helm_resid_deldel(tau,delta,data); |
220 |
double phir_dt = helm_resid_deltau(tau,delta,data); |
221 |
|
222 |
/* note similarities with helmholtz_w */ |
223 |
double temp1 = 1 + 2*delta*phir_d + SQ(delta)*phir_dd; |
224 |
double temp2 = 1 + delta*phir_d - delta*tau*phir_dt; |
225 |
double temp3 = SQ(tau)*(helm_ideal_tautau(tau,data->ideal) + helm_resid_tautau(tau,delta,data)); |
226 |
|
227 |
return data->R * (-temp3 + SQ(temp2)/temp1); |
228 |
} |
229 |
|
230 |
|
231 |
/** |
232 |
Function to calculate the speed of sound in a fluid from the Helmholtz free |
233 |
energy EOS, given temperature and mass density. |
234 |
|
235 |
@param T temperature in K |
236 |
@param rho mass density in kg/m続 |
237 |
@return Speed of sound in m/s. |
238 |
*/ |
239 |
double helmholtz_w(double T, double rho, const HelmholtzData *data){ |
240 |
|
241 |
double tau = data->T_star / T; |
242 |
double delta = rho / data->rho_star; |
243 |
|
244 |
double phir_d = helm_resid_del(tau,delta,data); |
245 |
double phir_dd = helm_resid_deldel(tau,delta,data); |
246 |
double phir_dt = helm_resid_deltau(tau,delta,data); |
247 |
|
248 |
/* note similarities with helmholtz_cp */ |
249 |
double temp1 = 1 + 2*delta*phir_d + SQ(delta)*phir_dd; |
250 |
double temp2 = 1 + delta*phir_d - delta*tau*phir_dt; |
251 |
double temp3 = SQ(tau)*(helm_ideal_tautau(tau,data->ideal) + helm_resid_tautau(tau,delta,data)); |
252 |
|
253 |
return sqrt(data->R * T *(temp1 - SQ(temp2)/temp3)); |
254 |
|
255 |
} |
256 |
|
257 |
/** |
258 |
Calculation zero-pressure specific heat capacity |
259 |
*/ |
260 |
double helmholtz_cp0(double T, const HelmholtzData *data){ |
261 |
double val = helm_cp0(T,data->ideal); |
262 |
#if 0 |
263 |
fprintf(stderr,"val = %f\n",val); |
264 |
#endif |
265 |
return val; |
266 |
} |
267 |
|
268 |
/** |
269 |
Calculate partial derivative of p with respect to T, with rho constant |
270 |
*/ |
271 |
double helmholtz_dpdT_rho(double T, double rho, const HelmholtzData *data){ |
272 |
double tau = data->T_star / T; |
273 |
double delta = rho / data->rho_star; |
274 |
|
275 |
double phir_del = helm_resid_del(tau,delta,data); |
276 |
double phir_deltau = helm_resid_deltau(tau,delta,data); |
277 |
#ifdef TEST |
278 |
assert(!isinf(phir_del)); |
279 |
assert(!isinf(phir_deltau)); |
280 |
assert(!isnan(phir_del)); |
281 |
assert(!isnan(phir_deltau)); |
282 |
assert(!isnan(data->R)); |
283 |
assert(!isnan(rho)); |
284 |
assert(!isnan(tau)); |
285 |
#endif |
286 |
|
287 |
double res = data->R * rho * (1 + delta*phir_del - delta*tau*phir_deltau); |
288 |
|
289 |
#ifdef TEST |
290 |
assert(!isnan(res)); |
291 |
assert(!isinf(res)); |
292 |
#endif |
293 |
return res; |
294 |
} |
295 |
|
296 |
/** |
297 |
Calculate partial derivative of p with respect to rho, with T constant |
298 |
*/ |
299 |
double helmholtz_dpdrho_T(double T, double rho, const HelmholtzData *data){ |
300 |
double tau = data->T_star / T; |
301 |
double delta = rho / data->rho_star; |
302 |
|
303 |
double phir_del = helm_resid_del(tau,delta,data); |
304 |
double phir_deldel = helm_resid_deldel(tau,delta,data); |
305 |
#ifdef TEST |
306 |
assert(!isinf(phir_del)); |
307 |
assert(!isinf(phir_deldel)); |
308 |
#endif |
309 |
return data->R * T * (1 + 2*delta*phir_del + delta*delta* phir_deldel); |
310 |
} |
311 |
|
312 |
/** |
313 |
Calculate partial derivative of h with respect to T, with rho constant |
314 |
*/ |
315 |
double helmholtz_dhdT_rho(double T, double rho, const HelmholtzData *data){ |
316 |
double tau = data->T_star / T; |
317 |
double delta = rho / data->rho_star; |
318 |
|
319 |
double phir_del = helm_resid_del(tau,delta,data); |
320 |
double phir_deltau = helm_resid_deltau(tau,delta,data); |
321 |
double phir_tautau = helm_resid_tautau(tau,delta,data); |
322 |
double phi0_tautau = helm_ideal_tautau(tau,data->ideal); |
323 |
|
324 |
//fprintf(stderr,"phir_del = %f, phir_deltau = %f, phir_tautau = %f, phi0_tautau = %f\n",phir_del,phir_deltau,phir_tautau,phi0_tautau); |
325 |
|
326 |
//return (helmholtz_h(T+0.01,rho,data) - helmholtz_h(T,rho,data)) / 0.01; |
327 |
return data->R * (1. + delta*phir_del - tau*tau*(phi0_tautau + phir_tautau) - delta*tau*phir_deltau); |
328 |
} |
329 |
|
330 |
/** |
331 |
Calculate partial derivative of h with respect to rho, with T constant |
332 |
*/ |
333 |
double helmholtz_dhdrho_T(double T, double rho, const HelmholtzData *data){ |
334 |
double tau = data->T_star / T; |
335 |
double delta = rho / data->rho_star; |
336 |
|
337 |
double phir_del = helm_resid_del(tau,delta,data); |
338 |
double phir_deltau = helm_resid_deltau(tau,delta,data); |
339 |
double phir_deldel = helm_resid_deldel(tau,delta,data); |
340 |
|
341 |
return data->R * T / rho * (tau*delta*(0 + phir_deltau) + delta * phir_del + SQ(delta)*phir_deldel); |
342 |
} |
343 |
|
344 |
|
345 |
/** |
346 |
Calculate partial derivative of u with respect to T, with rho constant |
347 |
*/ |
348 |
double helmholtz_dudT_rho(double T, double rho, const HelmholtzData *data){ |
349 |
double tau = data->T_star / T; |
350 |
double delta = rho / data->rho_star; |
351 |
|
352 |
double phir_tautau = helm_resid_tautau(tau,delta,data); |
353 |
double phi0_tautau = helm_ideal_tautau(tau,data->ideal); |
354 |
|
355 |
return -data->R * SQ(tau) * (phi0_tautau + phir_tautau); |
356 |
} |
357 |
|
358 |
/** |
359 |
Calculate partial derivative of u with respect to rho, with T constant |
360 |
*/ |
361 |
double helmholtz_dudrho_T(double T, double rho, const HelmholtzData *data){ |
362 |
double tau = data->T_star / T; |
363 |
double delta = rho / data->rho_star; |
364 |
|
365 |
double phir_deltau = helm_resid_deltau(tau,delta,data); |
366 |
|
367 |
return data->R * T / rho * (tau * delta * phir_deltau); |
368 |
} |
369 |
|
370 |
/*--------------------------------------------- |
371 |
UTILITY FUNCTION(S) |
372 |
*/ |
373 |
|
374 |
/* ipow: public domain by Mark Stephen with suggestions by Keiichi Nakasato */ |
375 |
static double ipow(double x, int n){ |
376 |
double t = 1.0; |
377 |
|
378 |
if(!n)return 1.0; /* At the top. x^0 = 1 */ |
379 |
|
380 |
if (n < 0){ |
381 |
n = -n; |
382 |
x = 1.0/x; /* error if x == 0. Good */ |
383 |
} /* ZTC/SC returns inf, which is even better */ |
384 |
|
385 |
if (x == 0.0)return 0.0; |
386 |
|
387 |
do{ |
388 |
if(n & 1)t *= x; |
389 |
n /= 2; /* KN prefers if (n/=2) x*=x; This avoids an */ |
390 |
x *= x; /* unnecessary but benign multiplication on */ |
391 |
}while(n); /* the last pass, but the comparison is always |
392 |
true _except_ on the last pass. */ |
393 |
|
394 |
return t; |
395 |
} |
396 |
|
397 |
//#define RESID_DEBUG |
398 |
|
399 |
/** |
400 |
Residual part of helmholtz function. |
401 |
*/ |
402 |
double helm_resid(double tau, double delta, const HelmholtzData *data){ |
403 |
double dell,ldell, term, sum, res = 0; |
404 |
unsigned n, i; |
405 |
const HelmholtzPowTerm *pt; |
406 |
const HelmholtzGausTerm *gt; |
407 |
const HelmholtzCritTerm *ct; |
408 |
|
409 |
n = data->np; |
410 |
pt = &(data->pt[0]); |
411 |
|
412 |
#ifdef RESID_DEBUG |
413 |
fprintf(stderr,"tau=%f, del=%f\n",tau,delta); |
414 |
#endif |
415 |
|
416 |
/* power terms */ |
417 |
sum = 0; |
418 |
dell = ipow(delta,pt->l); |
419 |
ldell = pt->l * dell; |
420 |
unsigned oldl; |
421 |
for(i=0; i<n; ++i){ |
422 |
term = pt->a * pow(tau, pt->t) * ipow(delta, pt->d); |
423 |
sum += term; |
424 |
#ifdef RESID_DEBUG |
425 |
fprintf(stderr,"i = %d, a=%e, t=%f, d=%d, term = %f, sum = %f",i,pt->a,pt->t,pt->d,term,sum); |
426 |
if(pt->l==0){ |
427 |
fprintf(stderr,",row=%e\n",term); |
428 |
}else{ |
429 |
fprintf(stderr,",row=%e\n,",term*exp(-dell)); |
430 |
} |
431 |
#endif |
432 |
oldl = pt->l; |
433 |
++pt; |
434 |
if(i+1==n || oldl != pt->l){ |
435 |
if(oldl == 0){ |
436 |
#ifdef RESID_DEBUG |
437 |
fprintf(stderr,"linear "); |
438 |
#endif |
439 |
res += sum; |
440 |
}else{ |
441 |
#ifdef RESID_DEBUG |
442 |
fprintf(stderr,"exp dell=%f, exp(-dell)=%f sum=%f: ",dell,exp(-dell),sum); |
443 |
#endif |
444 |
res += sum * exp(-dell); |
445 |
} |
446 |
#ifdef RESID_DEBUG |
447 |
fprintf(stderr,"i = %d, res = %f\n",i,res); |
448 |
#endif |
449 |
sum = 0; |
450 |
dell = ipow(delta,pt->l); |
451 |
ldell = pt->l*dell; |
452 |
} |
453 |
} |
454 |
|
455 |
/* gaussian terms */ |
456 |
n = data->ng; |
457 |
//fprintf(stderr,"THERE ARE %d GAUSSIAN TERMS\n",n); |
458 |
gt = &(data->gt[0]); |
459 |
for(i=0; i<n; ++i){ |
460 |
#ifdef RESID_DEBUG |
461 |
fprintf(stderr,"i = %d, GAUSSIAN, n = %e, t = %f, d = %f, alpha = %f, beta = %f, gamma = %f, epsilon = %f\n",i+1, gt->n, gt->t, gt->d, gt->alpha, gt->beta, gt->gamma, gt->epsilon); |
462 |
#endif |
463 |
double d1 = delta - gt->epsilon; |
464 |
double t1 = tau - gt->gamma; |
465 |
double e1 = -gt->alpha*d1*d1 - gt->beta*t1*t1; |
466 |
sum = gt->n * pow(tau,gt->t) * pow(delta,gt->d) * exp(e1); |
467 |
//fprintf(stderr,"sum = %f\n",sum); |
468 |
res += sum; |
469 |
++gt; |
470 |
} |
471 |
|
472 |
/* critical terms */ |
473 |
n = data->nc; |
474 |
ct = &(data->ct[0]); |
475 |
for(i=0; i<n; ++i){ |
476 |
#ifdef RESID_DEBUG |
477 |
fprintf(stderr,"i = %d, CRITICAL, n = %e, a = %f, b = %f, beta = %f, A = %f, B = %f, C = %f, D = %f\n",i+1, ct->n, ct->a, ct->b, ct->beta, ct->A, ct->B, ct->C, ct->D); |
478 |
#endif |
479 |
double d1 = delta - 1.; |
480 |
double t1 = tau - 1.; |
481 |
double theta = (1. - tau) + ct->A * pow(d1*d1, 0.5/ct->beta); |
482 |
double psi = exp(-ct->C*d1*d1 - ct->D*t1*t1); |
483 |
double DELTA = theta*theta + ct->B* pow(d1*d1, ct->a); |
484 |
sum = ct->n * pow(DELTA, ct->b) * delta * psi; |
485 |
res += sum; |
486 |
++ct; |
487 |
} |
488 |
|
489 |
#ifdef RESID_DEBUG |
490 |
fprintf(stderr,"CALCULATED RESULT FOR phir = %f\n",res); |
491 |
#endif |
492 |
return res; |
493 |
} |
494 |
|
495 |
/*=================== FIRST DERIVATIVES =======================*/ |
496 |
|
497 |
/** |
498 |
Derivative of the helmholtz residual function with respect to |
499 |
delta. |
500 |
*/ |
501 |
double helm_resid_del(double tau,double delta, const HelmholtzData *data){ |
502 |
double sum = 0, res = 0; |
503 |
double dell, ldell; |
504 |
unsigned n, i; |
505 |
const HelmholtzPowTerm *pt; |
506 |
const HelmholtzGausTerm *gt; |
507 |
const HelmholtzCritTerm *ct; |
508 |
|
509 |
#ifdef RESID_DEBUG |
510 |
fprintf(stderr,"tau=%f, del=%f\n",tau,delta); |
511 |
#endif |
512 |
|
513 |
/* power terms */ |
514 |
n = data->np; |
515 |
pt = &(data->pt[0]); |
516 |
dell = ipow(delta,pt->l); |
517 |
ldell = pt->l * dell; |
518 |
unsigned oldl; |
519 |
for(i=0; i<n; ++i){ |
520 |
sum += pt->a * pow(tau, pt->t) * ipow(delta, pt->d - 1) * (pt->d - ldell); |
521 |
oldl = pt->l; |
522 |
++pt; |
523 |
if(i+1==n || oldl != pt->l){ |
524 |
if(oldl == 0){ |
525 |
res += sum; |
526 |
}else{ |
527 |
res += sum * exp(-dell); |
528 |
} |
529 |
sum = 0; |
530 |
dell = ipow(delta,pt->l); |
531 |
ldell = pt->l*dell; |
532 |
} |
533 |
} |
534 |
|
535 |
/* gaussian terms */ |
536 |
n = data->ng; |
537 |
gt = &(data->gt[0]); |
538 |
for(i=0; i<n; ++i){ |
539 |
#ifdef RESID_DEBUG |
540 |
fprintf(stderr,"i = %d, GAUSSIAN, n = %e, t = %f, d = %f, alpha = %f, beta = %f, gamma = %f, epsilon = %f\n",i+1, gt->n, gt->t, gt->d, gt->alpha, gt->beta, gt->gamma, gt->epsilon); |
541 |
#endif |
542 |
sum = - gt->n * pow(tau,gt->t) * pow(delta, -1. + gt->d) |
543 |
* (2. * gt->alpha * delta * (delta - gt->epsilon) - gt->d) |
544 |
* exp(-(gt->alpha * SQ(delta-gt->epsilon) + gt->beta*SQ(tau-gt->gamma))); |
545 |
res += sum; |
546 |
#ifdef RESID_DEBUG |
547 |
fprintf(stderr,"sum = %f --> res = %f\n",sum,res); |
548 |
#endif |
549 |
++gt; |
550 |
} |
551 |
|
552 |
/* critical terms */ |
553 |
n = data->nc; |
554 |
ct = &(data->ct[0]); |
555 |
for(i=0; i<n; ++i){ |
556 |
#ifdef RESID_DEBUG |
557 |
fprintf(stderr,"i = %d, CRITICAL, n = %e, a = %f, b = %f, beta = %f, A = %f, B = %f, C = %f, D = %f\n",i+1, ct->n, ct->a, ct->b, ct->beta, ct->A, ct->B, ct->C, ct->D); |
558 |
#endif |
559 |
double d1 = delta - 1.; |
560 |
double t1 = tau - 1.; |
561 |
double theta = (1. - tau) + ct->A * pow(d1*d1, 0.5/ct->beta); |
562 |
double psi = exp(-ct->C*d1*d1 - ct->D*t1*t1); |
563 |
double DELTA = theta*theta + ct->B* pow(d1*d1, ct->a); |
564 |
|
565 |
double dpsiddelta = -2. * ct->C * d1 * psi; |
566 |
|
567 |
double dDELddelta = d1 * (ct->A * theta * 2./ct->beta * pow(d1*d1, 0.5/ct->beta - 1) + 2* ct->B * ct->a * pow(d1*d1, ct->a - 1)); |
568 |
|
569 |
double dDELbddelta = ct->b * pow(DELTA,ct->b - 1.) * dDELddelta; |
570 |
|
571 |
sum = ct->n * (pow(DELTA, ct->b) * (psi + delta * dpsiddelta) + dDELbddelta * delta * psi); |
572 |
res += sum; |
573 |
++ct; |
574 |
} |
575 |
|
576 |
|
577 |
return res; |
578 |
} |
579 |
|
580 |
/** |
581 |
Derivative of the helmholtz residual function with respect to |
582 |
tau. |
583 |
*/ |
584 |
double helm_resid_tau(double tau,double delta,const HelmholtzData *data){ |
585 |
|
586 |
double sum; |
587 |
double res = 0; |
588 |
double delX; |
589 |
unsigned l; |
590 |
unsigned n, i; |
591 |
const HelmholtzPowTerm *pt; |
592 |
const HelmholtzGausTerm *gt; |
593 |
const HelmholtzCritTerm *ct; |
594 |
|
595 |
n = data->np; |
596 |
pt = &(data->pt[0]); |
597 |
|
598 |
delX = 1; |
599 |
|
600 |
l = 0; |
601 |
sum = 0; |
602 |
for(i=0; i<n; ++i){ |
603 |
if(pt->t){ |
604 |
//fprintf(stderr,"i = %d, a = %e, t = %f, d = %d, l = %d\n",i+1, pt->a, pt->t, pt->d, pt->l); |
605 |
sum += pt->a * pow(tau, pt->t - 1) * ipow(delta, pt->d) * pt->t; |
606 |
} |
607 |
++pt; |
608 |
//fprintf(stderr,"l = %d\n",l); |
609 |
if(i+1==n || l != pt->l){ |
610 |
if(l==0){ |
611 |
//fprintf(stderr,"Adding non-exp term\n"); |
612 |
res += sum; |
613 |
}else{ |
614 |
//fprintf(stderr,"Adding exp term with l = %d, delX = %e\n",l,delX); |
615 |
res += sum * exp(-delX); |
616 |
} |
617 |
/* set l to new value */ |
618 |
if(i+1!=n){ |
619 |
l = pt->l; |
620 |
//fprintf(stderr,"New l = %d\n",l); |
621 |
delX = ipow(delta,l); |
622 |
sum = 0; |
623 |
} |
624 |
} |
625 |
} |
626 |
|
627 |
//#define RESID_DEBUG |
628 |
/* gaussian terms */ |
629 |
n = data->ng; |
630 |
gt = &(data->gt[0]); |
631 |
for(i=0; i<n; ++i){ |
632 |
#ifdef RESID_DEBUG |
633 |
fprintf(stderr,"i = %d, GAUSSIAN, n = %e, t = %f, d = %f, alpha = %f, beta = %f, gamma = %f, epsilon = %f\n",i+1, gt->n, gt->t, gt->d, gt->alpha, gt->beta, gt->gamma, gt->epsilon); |
634 |
#endif |
635 |
|
636 |
double val2; |
637 |
val2 = -gt->n * pow(tau,gt->t - 1.) * pow(delta, gt->d) |
638 |
* (2. * gt->beta * tau * (tau - gt->gamma) - gt->t) |
639 |
* exp(-(gt->alpha * SQ(delta-gt->epsilon) + gt->beta*SQ(tau-gt->gamma))); |
640 |
res += val2; |
641 |
#ifdef RESID_DEBUG |
642 |
fprintf(stderr,"res = %f\n",res); |
643 |
#endif |
644 |
|
645 |
++gt; |
646 |
} |
647 |
|
648 |
/* critical terms */ |
649 |
n = data->nc; |
650 |
ct = &(data->ct[0]); |
651 |
for(i=0; i<n; ++i){ |
652 |
#ifdef RESID_DEBUG |
653 |
fprintf(stderr,"i = %d, CRITICAL, n = %e, a = %f, b = %f, beta = %f, A = %f, B = %f, C = %f, D = %f\n",i+1, ct->n, ct->a, ct->b, ct->beta, ct->A, ct->B, ct->C, ct->D); |
654 |
#endif |
655 |
double d1 = delta - 1.; |
656 |
double t1 = tau - 1.; |
657 |
double theta = (1. - tau) + ct->A * pow(d1*d1, 0.5/ct->beta); |
658 |
double psi = exp(-ct->C*d1*d1 - ct->D*t1*t1); |
659 |
double DELTA = theta*theta + ct->B* pow(d1*d1, ct->a); |
660 |
|
661 |
double dDELbdtau = -2. * theta * ct->b * pow(DELTA, ct->b - 1); |
662 |
|
663 |
double dpsidtau = -2. * ct->D * t1 * psi; |
664 |
|
665 |
sum = ct->n * delta * (dDELbdtau * psi + pow(DELTA, ct->b) * dpsidtau); |
666 |
res += sum; |
667 |
++ct; |
668 |
} |
669 |
|
670 |
return res; |
671 |
} |
672 |
|
673 |
|
674 |
/*=================== SECOND DERIVATIVES =======================*/ |
675 |
|
676 |
/** |
677 |
Mixed derivative of the helmholtz residual function with respect to |
678 |
delta and tau. |
679 |
*/ |
680 |
double helm_resid_deltau(double tau,double delta,const HelmholtzData *data){ |
681 |
double dell,ldell, term, sum = 0, res = 0; |
682 |
unsigned n, i; |
683 |
const HelmholtzPowTerm *pt; |
684 |
const HelmholtzGausTerm *gt; |
685 |
|
686 |
/* power terms */ |
687 |
n = data->np; |
688 |
pt = &(data->pt[0]); |
689 |
dell = ipow(delta,pt->l); |
690 |
ldell = pt->l * dell; |
691 |
unsigned oldl; |
692 |
for(i=0; i<n; ++i){ |
693 |
sum += pt->a * pt->t * pow(tau, pt->t - 1) * ipow(delta, pt->d - 1) * (pt->d - ldell); |
694 |
oldl = pt->l; |
695 |
++pt; |
696 |
if(i+1==n || oldl != pt->l){ |
697 |
if(oldl == 0){ |
698 |
res += sum; |
699 |
}else{ |
700 |
res += sum * exp(-dell); |
701 |
} |
702 |
sum = 0; |
703 |
dell = ipow(delta,pt->l); |
704 |
ldell = pt->l*dell; |
705 |
} |
706 |
} |
707 |
|
708 |
#ifdef TEST |
709 |
assert(!isinf(res)); |
710 |
#endif |
711 |
|
712 |
/* gaussian terms */ |
713 |
n = data->ng; |
714 |
gt = &(data->gt[0]); |
715 |
for(i=0; i<n; ++i){ |
716 |
#ifdef RESID_DEBUG |
717 |
fprintf(stderr,"i = %d, GAUSSIAN, n = %e, t = %f, d = %f, alpha = %f, beta = %f, gamma = %f, epsilon = %f\n",i+1, gt->n, gt->t, gt->d, gt->alpha, gt->beta, gt->gamma, gt->epsilon); |
718 |
#endif |
719 |
double d1 = delta - gt->epsilon; |
720 |
double t1 = tau - gt->gamma; |
721 |
double e1 = -gt->alpha*SQ(d1) - gt->beta*SQ(t1); |
722 |
|
723 |
double f1 = gt->t - 2*gt->beta*tau*(tau - gt->gamma); |
724 |
double g1 = gt->d - 2*gt->alpha*delta*(delta - gt->epsilon); |
725 |
|
726 |
sum = gt->n * f1 * pow(tau,gt->t-1) * g1 * pow(delta,gt->d-1) * exp(e1); |
727 |
|
728 |
//fprintf(stderr,"sum = %f\n",sum); |
729 |
res += sum; |
730 |
#ifdef TEST |
731 |
assert(!isinf(res)); |
732 |
#endif |
733 |
++gt; |
734 |
} |
735 |
|
736 |
/* FIXME add critical terms calculation */ |
737 |
|
738 |
#ifdef RESID_DEBUG |
739 |
fprintf(stderr,"phir = %f\n",res); |
740 |
#endif |
741 |
|
742 |
#ifdef TEST |
743 |
assert(!isnan(res)); |
744 |
assert(!isinf(res)); |
745 |
#endif |
746 |
return res; |
747 |
} |
748 |
|
749 |
/** |
750 |
Second derivative of helmholtz residual function with respect to |
751 |
delta (twice). |
752 |
|
753 |
FIXME this function is WRONG. (UPDATE? is this still true? Think not) |
754 |
*/ |
755 |
double helm_resid_deldel(double tau,double delta,const HelmholtzData *data){ |
756 |
double sum = 0, res = 0; |
757 |
double dell, ldell; |
758 |
unsigned n, i; |
759 |
const HelmholtzPowTerm *pt; |
760 |
const HelmholtzGausTerm *gt; |
761 |
|
762 |
|
763 |
#ifdef RESID_DEBUG |
764 |
fprintf(stderr,"tau=%f, del=%f\n",tau,delta); |
765 |
#endif |
766 |
|
767 |
/* power terms */ |
768 |
n = data->np; |
769 |
pt = &(data->pt[0]); |
770 |
dell = ipow(delta,pt->l); |
771 |
ldell = pt->l * dell; |
772 |
unsigned oldl; |
773 |
for(i=0; i<n; ++i){ |
774 |
double lpart = pt->l ? SQ(ldell) + ldell*(1. - 2*pt->d - pt->l) : 0; |
775 |
sum += pt->a * pow(tau, pt->t) * ipow(delta, pt->d - 2) * (pt->d*(pt->d - 1) + lpart); |
776 |
oldl = pt->l; |
777 |
++pt; |
778 |
if(i+1==n || oldl != pt->l){ |
779 |
if(oldl == 0){ |
780 |
res += sum; |
781 |
}else{ |
782 |
res += sum * exp(-dell); |
783 |
} |
784 |
sum = 0; |
785 |
dell = ipow(delta,pt->l); |
786 |
ldell = pt->l*dell; |
787 |
} |
788 |
} |
789 |
|
790 |
/* gaussian terms */ |
791 |
n = data->ng; |
792 |
//fprintf(stderr,"THERE ARE %d GAUSSIAN TERMS\n",n); |
793 |
gt = &(data->gt[0]); |
794 |
for(i=0; i<n; ++i){ |
795 |
double s1 = SQ(delta - gt->epsilon); |
796 |
double f1 = gt->d*(gt->d - 1) |
797 |
+ 2.*gt->alpha*delta * ( |
798 |
delta * (2. * gt->alpha * s1 - 1) |
799 |
- 2. * gt->d * (delta - gt->epsilon) |
800 |
); |
801 |
res += gt->n * pow(tau,gt->t) * pow(delta, gt->d - 2.) |
802 |
* f1 |
803 |
* exp(-(gt->alpha * s1 + gt->beta*SQ(tau-gt->gamma))); |
804 |
++gt; |
805 |
} |
806 |
|
807 |
/* FIXME add critical terms calculation */ |
808 |
|
809 |
return res; |
810 |
} |
811 |
|
812 |
|
813 |
|
814 |
/** |
815 |
Residual part of helmholtz function. |
816 |
*/ |
817 |
double helm_resid_tautau(double tau, double delta, const HelmholtzData *data){ |
818 |
double dell,ldell, term, sum, res = 0; |
819 |
unsigned n, i; |
820 |
const HelmholtzPowTerm *pt; |
821 |
const HelmholtzGausTerm *gt; |
822 |
const HelmholtzCritTerm *ct; |
823 |
|
824 |
n = data->np; |
825 |
pt = &(data->pt[0]); |
826 |
|
827 |
#ifdef RESID_DEBUG |
828 |
fprintf(stderr,"tau=%f, del=%f\n",tau,delta); |
829 |
#endif |
830 |
|
831 |
/* power terms */ |
832 |
sum = 0; |
833 |
dell = ipow(delta,pt->l); |
834 |
ldell = pt->l * dell; |
835 |
unsigned oldl; |
836 |
for(i=0; i<n; ++i){ |
837 |
term = pt->a * pt->t * (pt->t - 1) * pow(tau, pt->t - 2) * ipow(delta, pt->d); |
838 |
sum += term; |
839 |
#ifdef RESID_DEBUG |
840 |
fprintf(stderr,"i = %d, a=%e, t=%f, d=%d, term = %f, sum = %f",i,pt->a,pt->t,pt->d,term,sum); |
841 |
if(pt->l==0){ |
842 |
fprintf(stderr,",row=%e\n",term); |
843 |
}else{ |
844 |
fprintf(stderr,",row=%e\n,",term*exp(-dell)); |
845 |
} |
846 |
#endif |
847 |
oldl = pt->l; |
848 |
++pt; |
849 |
if(i+1==n || oldl != pt->l){ |
850 |
if(oldl == 0){ |
851 |
#ifdef RESID_DEBUG |
852 |
fprintf(stderr,"linear "); |
853 |
#endif |
854 |
res += sum; |
855 |
}else{ |
856 |
#ifdef RESID_DEBUG |
857 |
fprintf(stderr,"exp dell=%f, exp(-dell)=%f sum=%f: ",dell,exp(-dell),sum); |
858 |
#endif |
859 |
res += sum * exp(-dell); |
860 |
} |
861 |
#ifdef RESID_DEBUG |
862 |
fprintf(stderr,"i = %d, res = %f\n",i,res); |
863 |
#endif |
864 |
sum = 0; |
865 |
dell = ipow(delta,pt->l); |
866 |
ldell = pt->l*dell; |
867 |
} |
868 |
} |
869 |
|
870 |
/* gaussian terms */ |
871 |
n = data->ng; |
872 |
//fprintf(stderr,"THERE ARE %d GAUSSIAN TERMS\n",n); |
873 |
gt = &(data->gt[0]); |
874 |
for(i=0; i<n; ++i){ |
875 |
#ifdef RESID_DEBUG |
876 |
fprintf(stderr,"i = %d, GAUSSIAN, n = %e, t = %f, d = %f, alpha = %f, beta = %f, gamma = %f, epsilon = %f\n",i+1, gt->n, gt->t, gt->d, gt->alpha, gt->beta, gt->gamma, gt->epsilon); |
877 |
#endif |
878 |
double d1 = delta - gt->epsilon; |
879 |
double t1 = tau - gt->gamma; |
880 |
double f1 = gt->t*(gt->t - 1) + 4. * gt->beta * tau * (tau * (gt->beta*SQ(t1) - 0.5) - t1*gt->t); |
881 |
double e1 = -gt->alpha*SQ(d1) - gt->beta*SQ(t1); |
882 |
sum = gt->n * f1 * pow(tau,gt->t - 2) * pow(delta,gt->d) * exp(e1); |
883 |
//fprintf(stderr,"sum = %f\n",sum); |
884 |
res += sum; |
885 |
++gt; |
886 |
} |
887 |
|
888 |
/* critical terms */ |
889 |
n = data->nc; |
890 |
ct = &(data->ct[0]); |
891 |
for(i=0; i<n; ++i){ |
892 |
#ifdef RESID_DEBUG |
893 |
fprintf(stderr,"i = %d, CRITICAL, n = %e, a = %f, b = %f, beta = %f, A = %f, B = %f, C = %f, D = %f\n",i+1, ct->n, ct->a, ct->b, ct->beta, ct->A, ct->B, ct->C, ct->D); |
894 |
#endif |
895 |
double d1 = delta - 1.; |
896 |
double t1 = tau - 1.; |
897 |
double theta = (1. - tau) + ct->A * pow(d1*d1, 0.5/ct->beta); |
898 |
double psi = exp(-ct->C*d1*d1 - ct->D*t1*t1); |
899 |
double DELTA = theta*theta + ct->B* pow(d1*d1, ct->a); |
900 |
|
901 |
double d2DELbdtau2 = 2. * ct->b * pow(DELTA, ct->b - 1) + 4. * theta*theta * ct->b * (ct->b - 1) * pow(DELTA, ct->b - 2); |
902 |
double dDELbdtau = -2. * theta * ct->b * pow(DELTA, ct->b - 1); |
903 |
double dpsidtau = -2. * ct->D * t1 * psi; |
904 |
double d2psidtau2 = 2. * ct->D * psi * (2. * ct->D * t1*t1 -1.); |
905 |
|
906 |
sum = ct->n * delta * (d2DELbdtau2 * psi + 2 * dDELbdtau*dpsidtau + pow(DELTA, ct->b) * d2psidtau2); |
907 |
res += sum; |
908 |
++ct; |
909 |
} |
910 |
|
911 |
#ifdef RESID_DEBUG |
912 |
fprintf(stderr,"phir_tautau = %f\n",res); |
913 |
#endif |
914 |
return res; |
915 |
} |
916 |
|