eos_database.F90 coverage: 100.00 %func 75.82 %block
1) module EOSDatabase_module
2)
3) use PFLOTRAN_Constants_module
4) use Lookup_Table_module
5)
6) implicit none
7)
8) private
9)
10) #include "petsc/finclude/petscsys.h"
11)
12) PetscInt, parameter, public :: EOS_DENSITY = 1
13) PetscInt, parameter, public :: EOS_ENTHALPY = 2
14) PetscInt, parameter, public :: EOS_VISCOSITY = 3
15) !add here other properties and derivatives and increase MAX_PROP_NUM
16) PetscInt, parameter, public :: MAX_PROP_NUM = 3
17)
18) type, public :: eos_database_type
19) character(len=MAXWORDLENGTH) :: dbase_name
20) character(len=MAXWORDLENGTH) :: file_name
21) PetscInt :: num_dp ! number of pressure intervals
22) PetscInt :: num_dt ! number of temperature intervals
23) PetscInt :: num_prop ! number of properties in the database
24) PetscReal :: dp ! uniform pressure interval
25) PetscReal :: dt ! uniform temperature interval
26) PetscInt :: data_to_prop_map(MAX_PROP_NUM) !map the data idx to the prop.
27) PetscInt :: prop_to_data_map(MAX_PROP_NUM) !map the prop to the dat idx
28) PetscReal, pointer :: data(:,:)
29) class(lookup_table_uniform_type), pointer :: lookup_table
30) contains
31) procedure, public :: Read => EOSDatabaseRead
32) procedure, public :: EOSProp => EOSPropLinearInterp
33) procedure, public :: EOSPropPresent
34) end type
35)
36) public :: EOSDatabaseCreate, &
37) EOSDatabaseDestroy
38)
39) contains
40)
41) ! ************************************************************************** !
42)
43) function EOSDatabaseCreate(filename,dbase_name)
44) !
45) ! Author: Paolo Orsini
46) ! Date: 12/11/15
47) !
48)
49) implicit none
50)
51) class(eos_database_type), pointer :: EOSDatabaseCreate
52) character(len=MAXWORDLENGTH) :: filename
53) character(len=*) :: dbase_name
54)
55) allocate(EOSDatabaseCreate)
56) EOSDatabaseCreate%dbase_name = dbase_name
57) EOSDatabaseCreate%file_name = filename
58) EOSDatabaseCreate%num_dp = UNINITIALIZED_INTEGER
59) EOSDatabaseCreate%num_dt = UNINITIALIZED_INTEGER
60) EOSDatabaseCreate%dp = UNINITIALIZED_DOUBLE
61) EOSDatabaseCreate%dt = UNINITIALIZED_DOUBLE
62) EOSDatabaseCreate%data_to_prop_map(1:MAX_PROP_NUM) = UNINITIALIZED_INTEGER
63) EOSDatabaseCreate%prop_to_data_map(1:MAX_PROP_NUM) = UNINITIALIZED_INTEGER
64)
65) nullify(EOSDatabaseCreate%lookup_table)
66) nullify(EOSDatabaseCreate%data)
67)
68) end function EOSDatabaseCreate
69)
70) ! ************************************************************************** !
71)
72) subroutine EOSDatabaseRead(this,option)
73) !
74) ! Author: Paolo Orsini
75) ! Date: 12/11/15
76) !
77) ! Reads the the an EOS database from a text file for one or more
78) ! phase properties.
79) !
80) ! Database format (for look up table)
81) ! Header: NUM_DP, NUM_DT and DATA_LIST_ORDER
82) ! DATA
83) ! column 1 = pressure
84) ! column 2 = temperature
85) ! column 2+1, column 2+n: properties in the order given in DATA_LIST_ORDER
86) !
87) ! The dataset must be followed by NUM_DP * NUM_DT lines, not interrupted
88) ! by a commented line.
89) ! Each line must include P,T, and all properties listed in DATA_LIST_ORDER
90) !
91) ! each property listed depends on P and T, prop(P,T).
92) !
93) ! temperature values must be equispaced (same dt in the entire table)
94) ! pressure values must be equispaced (same dp in the entire table)
95) ! The data must be ordered so for growing P and T, with T looping faster.
96) !
97) ! This format repeates unnecessary P and T values in favours o readibility
98) ! and creation of small dataset by hand. Inefficient for for large datasets.
99) !
100)
101) use Option_module
102) use Input_Aux_module
103) use String_module
104)
105) implicit none
106)
107) class(eos_database_type) :: this
108) type(option_type) :: option
109)
110) character(len=MAXWORDLENGTH) :: keyword, word
111) character(len=MAXSTRINGLENGTH) :: error_string = 'EOS_DATABASE'
112) character(len=MAXSTRINGLENGTH) :: string
113) PetscInt :: prop_idx, prop_count, i_idx, j_idx
114) PetscInt :: data_size
115) PetscReal :: tempreal
116) PetscBool :: pres_present, temp_present
117)
118) type(input_type), pointer :: input_table
119)
120)
121) if (len_trim(this%file_name) < 1) then
122) option%io_buffer = 'FILENAME must be specified for EOS_DATABASE.'
123) call printErrMsg(option)
124) endif
125)
126) input_table => InputCreate(IUNIT_TEMP,this%file_name,option)
127) input_table%ierr = 0
128)
129) !if ( option%myrank == 0 ) then
130) option%io_buffer = 'Reading database = ' // this%file_name
131) call printMsg(option)
132) !end if
133)
134) !reading the database file header
135) do
136)
137) call InputReadPflotranString(input_table,option)
138) !if (InputCheckExit(input_table,option)) exit
139) if (InputError(input_table)) exit
140)
141) call InputReadWord(input_table,option,keyword,PETSC_TRUE)
142) call InputErrorMsg(input_table,option,'keyword',error_string)
143) call StringToUpper(keyword)
144) select case(keyword)
145) case('NUM_DP')
146) call InputReadInt(input_table,option,this%num_dp)
147) call InputErrorMsg(input_table,option,'number of dp',error_string)
148) case('NUM_DT')
149) call InputReadInt(input_table,option,this%num_dt)
150) call InputErrorMsg(input_table,option,'number of dt',error_string)
151) case('DATA_LIST_ORDER')
152) pres_present = PETSC_FALSE; temp_present = PETSC_FALSE;
153) prop_idx = 0
154) do
155) call InputReadPflotranString(input_table,option)
156) if (InputCheckExit(input_table,option)) exit
157) call InputReadWord(input_table,option,word,PETSC_TRUE)
158) select case(word)
159) case('PRESSURE')
160) pres_present = PETSC_TRUE
161) case('TEMPERATURE')
162) temp_present = PETSC_TRUE
163) case('DENSITY')
164) prop_idx = prop_idx + 1
165) this%data_to_prop_map(prop_idx) = EOS_DENSITY
166) this%prop_to_data_map(EOS_DENSITY) = prop_idx
167) case('ENTHALPY')
168) prop_idx = prop_idx + 1
169) this%data_to_prop_map(prop_idx) = EOS_ENTHALPY
170) this%prop_to_data_map(EOS_ENTHALPY) = prop_idx
171) case('VISCOSITY')
172) prop_idx = prop_idx + 1
173) this%data_to_prop_map(prop_idx) = EOS_VISCOSITY
174) this%prop_to_data_map(EOS_VISCOSITY) = prop_idx
175) case default
176) error_string = trim(error_string) // ': ' // this%file_name // &
177) ': DATA_LIST_ORDER'
178) call InputKeywordUnrecognized(keyword,error_string,option)
179) end select
180) end do
181) this%num_prop = prop_idx
182) if (.not.pres_present) then
183) option%io_buffer = 'PRESSURE must be present in any EOS_DATABASE.'
184) call printErrMsg(option)
185) end if
186) if (.not.temp_present) then
187) option%io_buffer = 'TEMPERATURE must be present in any EOS_DATABASE.'
188) call printErrMsg(option)
189) end if
190) case('DATA')
191) exit
192) case default
193) error_string = trim(error_string) // ': ' // this%file_name
194) call InputKeywordUnrecognized(keyword,error_string,option)
195) end select
196)
197) end do
198)
199) data_size = this%num_dp * this%num_dt
200)
201) this%num_prop = prop_idx
202) allocate(this%data(prop_idx,data_size))
203)
204) !create lookup table
205) this%lookup_table => LookupTableCreateUniform(TWO_INTEGER)
206) this%lookup_table%dims(1) = this%num_dt
207) this%lookup_table%dims(2) = this%num_dp
208) allocate(this%lookup_table%axis1%values(this%num_dt))
209) this%lookup_table%axis1%values(1:this%num_dt) = UNINITIALIZED_DOUBLE
210) allocate(this%lookup_table%axis2%values(this%num_dp))
211) this%lookup_table%axis2%values(1:this%num_dp) = UNINITIALIZED_DOUBLE
212)
213) !TODO
214) ! start loading data - at the moment using Input facility, however this file
215) ! can be large. TODO. Implement more efficient solutions:
216) ! - using read(,) without InputReadDouble filter
217) ! - adding the option of reading a .h5 file where the database is defined
218)
219) ! go to data - first time to load axis1 and 2 values
220) string = "DATA"
221) call InputFindStringInFile(input_table,option,string)
222)
223) do j_idx = 1,this%num_dp
224)
225) do i_idx = 1, this%num_dt
226) call InputReadPflotranString(input_table,option)
227)
228) call InputReadDouble(input_table,option,tempreal)
229) call InputErrorMsg(input_table,option, &
230) 'VALUE', 'EOS_DATABASE PRESS_VALUE')
231) ! convert MPa in Pa
232) this%lookup_table%axis2%values(j_idx) = tempreal * 1.0d6
233)
234) ! this is repeated this%num_dp times - not efficient
235) call InputReadDouble(input_table,option, &
236) this%lookup_table%axis1%values(i_idx))
237) call InputErrorMsg(input_table,option, &
238) 'VALUE', 'EOS_DATABASE TEMP_VALUE')
239)
240) prop_count = i_idx + (j_idx-1) * this%num_dt
241) do prop_idx = 1,this%num_prop
242) call InputReadDouble(input_table,option,this%data(prop_idx,prop_count))
243) call InputErrorMsg(input_table,option,&
244) 'VALUE','EOS_DATABASE PROP_VALUE')
245) end do
246)
247) end do
248)
249) end do
250)
251) call InputDestroy(input_table)
252)
253) end subroutine EOSDatabaseRead
254)
255) ! ************************************************************************** !
256) function EOSPropPresent(this,prop_iname)
257) !
258) ! Author: Paolo Orsini
259) ! Date: 12/18/15
260) !
261) ! Checks if a property is defined in the database
262)
263) implicit none
264)
265) class(eos_database_type) :: this
266) PetscInt, intent(in) :: prop_iname
267) PetscBool :: EOSPropPresent
268)
269) EOSPropPresent = Initialized(this%prop_to_data_map(prop_iname))
270)
271) end function EOSPropPresent
272)
273) ! ************************************************************************** !
274)
275) subroutine EOSPropLinearInterp(this,T,P,prop_iname,prop_value,ierr)
276) !
277) ! Author: Paolo Orsini
278) ! Date: 12/12/15
279) !
280) ! interpolates a single EOS property from the EOS database
281) ! Note: when more properties must be extracted from the same EOS database,
282) ! i.e. properties listed for the same values and range of P and T,
283) ! all propoertis should be extracted at the same time to perform
284) ! only once the look up operations, which are:
285) ! (a) LookupTableIndexUniform, i.e. axis look up
286) ! (b) P,T location checks within a single 2D domain, (p,p+dp; t,t+dt)
287) ! currently done within LookupTableInterpolate2DUniform
288) !
289) ! TODO: add a method lookup_table to extract mulitdimensional data
290) ! at the same time (data plus function)
291)
292) implicit none
293)
294) class(eos_database_type) :: this
295) PetscReal, intent(in) :: T ! temperature [C]
296) PetscReal, intent(in) :: P ! pressure [Pa]
297) PetscInt, intent(in) :: prop_iname
298) PetscReal, intent(out) :: prop_value ! database units (SI)
299) PetscErrorCode, intent(out) :: ierr
300)
301) !PetscReal :: EOSEOSProp !property given in units specified in the database
302)
303) ierr = 0
304) !insert check P and/or T out of range
305) ! T smaller then minimum T in the database
306) ! errors here a passed back to the EOSxxxPhase module
307) ! and to ModeXXXComputeAuxVar where cells ids are available to check where
308) ! P and T are out of range.
309) ! TODO: (i) define error id identifiers for each EOSPhase, or for all EOSs.
310) ! (ii) define a function that handles errors ids and print error message.
311) ! (iii) can then remove the print statment below
312) ! note: need to minimise the number if-checks for efficiency.
313) if ( T < this%lookup_table%axis1%values(1) ) then
314) ierr = 101
315) print*, "EOSEOSProp - T smaller than min val in EOSdatabase"
316) print*, "Temp val [°C] = ", T
317) stop
318) end if
319) if ( T > this%lookup_table%axis1%values(this%num_dt) ) then
320) ierr = 102
321) print*, "EOSEOSProp - T larger than max val in EOSdatabase"
322) print*, "Temp val [°C] = ", T
323) stop
324) end if
325) if ( P < this%lookup_table%axis2%values(1) ) then
326) ierr = 103
327) print*, "EOSEOSProp - P smaller than min val in EOSdatabase"
328) print*, "Press val [Mpa] = ", P*1.d-6
329) stop
330) end if
331) if ( P > this%lookup_table%axis2%values(this%num_dp) ) then
332) ierr = 104
333) print*, "EOSEOSProp - P larger than max val in EOSdatabase"
334) print*, "Press val [Mpa] = ", P*1.d-6
335) stop
336) end if
337)
338) this%lookup_table%data => this%data(this%prop_to_data_map(prop_iname),:)
339)
340) !T P optional
341) !this%lookup_table%Sample(lookup1,lookup2,lookup3)
342) prop_value = this%lookup_table%Sample(T,P)
343)
344) nullify(this%lookup_table%data)
345)
346) end subroutine EOSPropLinearInterp
347)
348) ! ************************************************************************** !
349)
350) subroutine EOSDatabaseDestroy(eos_database)
351) !
352) ! Author: Paolo Orsini
353) ! Date: 12/14/15
354) !
355) ! destroys EOS database
356)
357) use Utility_module
358)
359) implicit none
360)
361) class(eos_database_type), pointer :: eos_database
362)
363) if (.not.associated(eos_database)) return
364)
365) !deallocate(eos_database%data)
366) !nullify(eos_database%data)
367) call DeallocateArray(eos_database%data)
368)
369) call LookupTableDestroy(eos_database%lookup_table)
370)
371) deallocate(eos_database)
372) nullify(eos_database)
373)
374) end subroutine EOSDatabaseDestroy
375)
376) ! ************************************************************************** !
377)
378) end module EOSDatabase_module