simulator.rockphysics.standardrp
Descriptive description.
1"""Descriptive description.""" 2 3__author__ = 'TM' 4 5# standardrp.py 6import numpy as np 7import sys 8import multiprocessing as mp 9# internal load 10from misc.system_tools.environ_var import OpenBlasSingleThread # Single threaded OpenBLAS runs 11 12 13class elasticproperties: 14 """ 15 Calculate elastic properties from standard 16 rock-physics models, specifically following Batzle 17 and Wang, Geophysics, 1992, for fluid properties, and 18 Report 1 in Abul Fahimuddin's thesis at Universty of 19 Bergen (2010) for other properties. 20 21 Example 22 ------- 23 >>> porosity = 0.2 24 ... pressure = 5 25 ... phases = ["Oil","Water"] 26 ... saturations = [0.3, 0.5] 27 ... 28 ... satrock = Elasticproperties() 29 ... satrock.calc_props(phases, saturations, pressure, porosity) 30 """ 31 32 def __init__(self, input_dict): 33 self.dens = None 34 self.bulkmod = None 35 self.shearmod = None 36 self.bulkvel = None 37 self.shearvel = None 38 self.bulkimp = None 39 self.shearimp = None 40 # The overburden for each grid cell must be 41 # specified as values on an .npz-file whose 42 # name is given in input_dict. 43 self.input_dict = input_dict 44 self._extInfoInputDict() 45 46 def _extInfoInputDict(self): 47 # The key word for the file name in the 48 # dictionary must read "overburden" 49 if 'overburden' in self.input_dict: 50 obfile = self.input_dict['overburden'] 51 npzfile = np.load(obfile) 52 # The values of overburden must have been 53 # stored on file using: 54 # np.savez(<file name>, 55 # obvalues=<overburden values>) 56 self.overburden = npzfile['obvalues'] 57 npzfile.close() 58 if 'baseline' in self.input_dict: 59 self.baseline = self.input_dict['baseline'] # 4D baseline 60 if 'parallel' in self.input_dict: 61 self.parallel = self.input_dict['parallel'] 62 63 def _filter(self): 64 bulkmod = self.bulkimp 65 self.bulkimp = bulkmod.flatten() 66 67 def setup_fwd_run(self, state): 68 """ 69 Setup the input parameters to be used in the PEM simulator. Parameters can be a an ensemble or a single array. 70 State is set as an attribute of the simulator, and the correct value is determined in self.pem.calc_props() 71 72 Parameters 73 ---------- 74 state : dict 75 Dictionary of input parameters or states. 76 77 Changelog 78 --------- 79 - KF 11/12-2018 80 """ 81 # self.inv_state = {} 82 # list_pem_param =[el for el in [foo for foo in self.pem['garn'].keys()] + [foo for foo in self.filter.keys()] + 83 # [foo for foo in self.__dict__.keys()]] 84 85 # list_tot_param = state.keys() 86 # for param in list_tot_param: 87 # if param in list_pem_param or (param.split('_')[-1] in ['garn', 'rest']): 88 # self.inv_state[param] = state[param] 89 90 pass 91 92 def calc_props(self, phases, saturations, pressure, 93 porosity, wait_for_proc=None, ntg=None, Rs=None, press_init=None, ensembleMember=None): 94 ### 95 # This doesn't initialize for models with no uncertainty 96 ### 97 # # if some PEM properties have uncertainty, set the correct value 98 # if ensembleMember is not None: 99 # for pem_state in self.__dict__.keys(): # loop over all possible pem vaules 100 # if pem_state is not 'inv_state': # do not alter the ensemble 101 # if type(eval('self.{}'.format(pem_state))) is dict: 102 # for el in eval('self.{}'.format(pem_state)).keys(): 103 # if type(eval('self.{}'.format(pem_state))[el]) is dict: 104 # for param in eval('self.{}'.format(pem_state))[el].keys(): 105 # if param in self.inv_state: 106 # eval('self.{}'.format(pem_state))[el][param]=\ 107 # self.inv_state[param][:, ensembleMember] 108 # elif param + '_' + el in self.inv_state: 109 # eval('self.{}'.format(pem_state))[el][param] = \ 110 # self.inv_state[param+'_' + el][:, ensembleMember] 111 # else: 112 # if el in self.inv_state: 113 # eval('self.{}'.format(pem_state))[el] = self.inv_state[el][:,ensembleMember] 114 # else: 115 # if pem_state in self.inv_state: 116 # setattr(self,pem_state, self.inv_state[pem_state][:,ensembleMember]) 117 118 # Check if the inputs are given as a list (more 119 # than one phase) or a single input (single 120 # phase). If single phase input, make the input a 121 # list with a single entry (s.t. it can be used 122 # directly with the methods below) 123 # 124 if not isinstance(phases, list): 125 phases = [phases] 126 if not isinstance(saturations, list): 127 saturations = [saturations] 128 if not isinstance(pressure, list) and \ 129 type(pressure).__module__ != 'numpy': 130 pressure = [pressure] 131 if not isinstance(porosity, list) and \ 132 type(porosity).__module__ != 'numpy': 133 porosity = [porosity] 134 # 135 # Load "overburden" into local variable to 136 # comply with remaining code parts 137 overburden = self.overburden 138 if press_init is None: 139 p_init = self.p_init 140 else: 141 p_init = press_init 142 # 143 # Check that no. of phases is equal to no. of 144 # entries in saturations list 145 # 146 assert (len(saturations) == len(phases)) 147 # 148 # Make saturation a Numpy array (so that we 149 # can easily access the values for each 150 # phase at one grid cell) 151 # 152 # Transpose makes it a no. grid cells x phases 153 # array 154 saturations = np.array(saturations).T 155 # 156 # Check if we actually inputted saturation values 157 # for a single grid cell. If yes, we redefine 158 # saturations to get it on the correct form (no. 159 # grid cells x phases array). 160 # 161 if saturations.ndim == 1: 162 saturations = \ 163 np.array([[x] for x in saturations]).T 164 # 165 # Loop over all grid cells and calculate the 166 # various saturated properties 167 # 168 self.phases = phases 169 170 self.dens = np.zeros(len(saturations[:, 0])) 171 self.bulkmod = np.zeros(len(saturations[:, 0])) 172 self.shearmod = np.zeros(len(saturations[:, 0])) 173 self.bulkvel = np.zeros(len(saturations[:, 0])) 174 self.shearvel = np.zeros(len(saturations[:, 0])) 175 self.bulkimp = np.zeros(len(saturations[:, 0])) 176 self.shearimp = np.zeros(len(saturations[:, 0])) 177 178 if ntg is None: 179 ntg = [None for _ in range(len(saturations[:, 0]))] 180 if Rs is None: 181 Rs = [None for _ in range(len(saturations[:, 0]))] 182 if p_init is None: 183 p_init = [None for _ in range(len(saturations[:, 0]))] 184 185 for i in range(len(saturations[:, 0])): 186 # 187 # Calculate fluid properties 188 # 189 # set Rs if needed 190 densf, bulkf = \ 191 self._fluidprops(self.phases, 192 saturations[i, :], pressure[i], Rs[i]) 193 # 194 denss, bulks, shears = \ 195 self._solidprops(porosity[i], ntg[i], i) 196 # 197 # Calculate dry rock moduli 198 # 199 200 bulkd, sheard = \ 201 self._dryrockmoduli(porosity[i], 202 overburden[i], 203 pressure[i], bulks, 204 shears, i, ntg[i], p_init[i], denss, Rs[i], self.phases) 205 # ------------------------------- 206 # Calculate saturated properties 207 # ------------------------------- 208 # 209 # Density 210 # 211 self.dens[i] = (porosity[i]*densf + 212 (1-porosity[i])*denss)*0.001 213 # 214 # Moduli 215 # 216 self.bulkmod[i] = \ 217 bulkd + (1 - bulkd/bulks)**2 / \ 218 (porosity[i]/bulkf + 219 (1-porosity[i])/bulks - 220 bulkd/(bulks**2)) 221 self.shearmod[i] = sheard 222 223 # Velocities (due to bulk/shear modulus being 224 # in MPa, we multiply by 1000 to get m/s 225 # instead of km/s) 226 # 227 self.bulkvel[i] = \ 228 100*np.sqrt((abs(self.bulkmod[i]) + 229 4*self.shearmod[i]/3)/(self.dens[i])) 230 self.shearvel[i] = \ 231 100*np.sqrt(self.shearmod[i] / 232 (self.dens[i])) 233 # 234 # Impedances (m/s)*(Kg/m3) 235 # 236 self.bulkimp[i] = self.dens[i] * \ 237 self.bulkvel[i] 238 self.shearimp[i] = self.dens[i] * \ 239 self.shearvel[i] 240 241 def getMatchProp(self, petElProp): 242 if petElProp.lower() == 'density': 243 self.match_prop = self.getDens() 244 elif petElProp.lower() == 'bulk_modulus': 245 self.match_prop = self.getBulkMod() 246 elif petElProp.lower() == 'shear_modulus': 247 self.match_prop = self.getShearMod() 248 elif petElProp.lower() == 'bulk_velocity': 249 self.match_prop = self.getBulkVel() 250 elif petElProp.lower() == 'shear_velocity': 251 self.match_prop = self.getShearVel() 252 elif petElProp.lower() == "bulk_impedance": 253 self.match_prop = self.getBulkImp() 254 elif petElProp.lower() == 'shear_impedance': 255 self.match_prop = self.getShearImp() 256 else: 257 print("\nError in getMatchProp method") 258 print("No model output type selected for " 259 "data match.") 260 print("Legal model output types are " 261 "(case insensitive):") 262 print("Density, bulk modulus, shear " 263 "modulus, bulk velocity,") 264 print("shear velocity, bulk impedance, " 265 "shear impedance") 266 sys.exit(1) 267 return self.match_prop 268 269 def getDens(self): 270 return self.dens 271 272 def getBulkMod(self): 273 return self.bulkmod 274 275 def getShearMod(self): 276 return self.shearmod 277 278 def getBulkVel(self): 279 return self.bulkvel 280 281 def getShearVel(self): 282 return self.shearvel 283 284 def getBulkImp(self): 285 return self.bulkimp 286 287 def getShearImp(self): 288 return self.shearimp 289 290 # 291 # =================================================== 292 # Fluid properties start 293 # =================================================== 294 # 295 def _fluidprops(self, fphases, fsats, fpress, Rs=None): 296 # 297 # Calculate fluid density and bulk modulus 298 # 299 # 300 # Input 301 # fphases - fluid phases present; Oil 302 # and/or Water and/or Gas 303 # fsats - fluid saturation values for 304 # fluid phases in "fphases" 305 # fpress - fluid pressure value (MPa) 306 # Rs - Gas oil ratio. Default value None 307 308 # 309 # Output 310 # fdens - density of fluid mixture for 311 # pressure value "fpress" inherited 312 # from phaseprops) 313 # fbulk - bulk modulus of fluid mixture for 314 # pressure value "fpress" (unit 315 # inherited from phaseprops) 316 # 317 # ----------------------------------------------- 318 # 319 fdens = 0.0 320 fbinv = 0.0 321 322 for i in range(len(fphases)): 323 # 324 # Calculate mixture properties by summing 325 # over individual phase properties 326 # 327 pdens, pbulk = self._phaseprops(fphases[i], 328 fpress, Rs) 329 fdens = fdens + fsats[i]*abs(pdens) 330 fbinv = fbinv + fsats[i]/abs(pbulk) 331 fbulk = 1.0/fbinv 332 # 333 return fdens, fbulk 334 # 335 # --------------------------------------------------- 336 # 337 338 def _phaseprops(self, fphase, press, Rs=None): 339 # 340 # Calculate properties for a single fluid phase 341 # 342 # 343 # Input 344 # fphase - fluid phase; Oil, Water or Gas 345 # press - fluid pressure value (MPa) 346 # 347 # Output 348 # pdens - phase density of fluid phase 349 # "fphase" for pressure value 350 # "press" (kg/m³) 351 # pbulk - bulk modulus of fluid phase 352 # "fphase" for pressure value 353 # "press" (MPa) 354 # 355 # ----------------------------------------------- 356 # 357 if fphase.lower() == "oil": 358 coeffsrho = np.array([0.8, 829.9]) 359 coeffsbulk = np.array([10.42, 995.79]) 360 elif fphase.lower() == "wat": 361 coeffsrho = np.array([0.3, 1067.3]) 362 coeffsbulk = np.array([9.0, 2807.6]) 363 elif fphase.lower() == "gas": 364 coeffsrho = np.array([4.7, 13.4]) 365 coeffsbulk = np.array([2.75, 0.0]) 366 else: 367 print("\nError in phaseprops method") 368 print("Illegal fluid phase name.") 369 print("Legal fluid phase names are (case " 370 "insensitive): Oil, Wat, and Gas.") 371 sys.exit(1) 372 # 373 # Assume simple linear pressure dependencies. 374 # Coefficients are inferred from 375 # plots in Batzle and Wang, Geophysics, 1992, 376 # (where I set the temperature to be 100 degrees 377 # Celsius, Note also that they give densities in 378 # g/cc). The resulting straight lines do not fit 379 # the data extremely well, but they should 380 # be sufficiently accurate for the purpose of 381 # this project. 382 # 383 pdens = coeffsrho[0]*press + coeffsrho[1] 384 pbulk = coeffsbulk[0]*press + coeffsbulk[1] 385 # 386 return pdens, pbulk 387 388 # 389 # ======================= 390 # Fluid properties end 391 # ======================= 392 # 393 394 # 395 # ========================= 396 # Solid properties start 397 # ========================= 398 # 399 def _solidprops(self, poro, ntg=None, ind=None): 400 # 401 # Calculate bulk and shear solid rock (mineral) 402 # moduli by averaging Hashin-Shtrikman bounds 403 # 404 # 405 # Input 406 # poro -porosity 407 # 408 # Output 409 # denss - solid rock density (kg/m³) 410 # bulks - solid rock bulk modulus (unit 411 # inherited from hashinshtr) 412 # shears - solid rock shear modulus (unit 413 # inherited from hashinshtr) 414 # 415 # ----------------------------------------------- 416 # 417 # From PetroWiki (kg/m³) 418 # 419 densc = 2540 420 densq = 2650 421 # 422 # From "Step 1" of "recipe" in Report 1 in Abul 423 # Fahimuddin's thesis. 424 # 425 vclay = 0.7 - 1.58*poro 426 # 427 # Calculate solid rock (mineral) density. (Note 428 # that this is often termed \rho_dry, and not 429 # \rho_s) 430 # 431 denss = densq + vclay*(densc - densq) 432 # 433 # Calculate lower and upper bulk and shear 434 # Hashin-Shtrikman bounds 435 # 436 bulkl, bulku, shearl, shearu = \ 437 self._hashinshtr(vclay) 438 # 439 # Calculate bulk and shear solid rock (mineral) 440 # moduli as arithmetic means of the respective 441 # bounds 442 # 443 bulkb = np.array([bulkl, bulku]) 444 shearb = np.array([shearl, shearu]) 445 bulks = np.mean(bulkb) 446 shears = np.mean(shearb) 447 # 448 return denss, bulks, shears 449 # 450 # --------------------------------------------------- 451 # 452 453 def _hashinshtr(self, vclay): 454 # 455 # Calculate lower and upper, bulk and shear, 456 # Hashin-Shtrikman bounds, utilizing that they 457 # all have the common mathematical form, 458 # 459 # f = a + b/((1/c) + d*(1/e)). 460 # 461 # 462 # Input 463 # vclay - "volume of clay" 464 # 465 # Output 466 # bulkl - lower bulk Hashin-Shtrikman 467 # bound (MPa) 468 # bulku - upper bulk Hashin-Shtrikman 469 # bound (MPa) 470 # shearl - lower shear Hashin-Shtrikman 471 # bound (MPa) 472 # shearu - upper shear Hashin-Shtrikman 473 # bound (MPa) 474 # 475 # ----------------------------------------------- 476 # 477 # From table 1 in Report 1 in Abul Fahimuddin's 478 # thesis (he used GPa, I use MPa), ("c" for clay 479 # and "q" for quartz.): 480 # 481 bulkc = 14900 482 bulkq = 37000 483 shearc = 1950 484 shearq = 44000 485 # 486 # Calculate quantities common for both bulk and 487 # shear formulas 488 # 489 lb = 1 - vclay 490 ub = vclay 491 le = bulkc + 4*shearc/3 492 ue = bulkq + 4*shearq/3 493 # 494 # Calculate quantities common only for bulk 495 # formulas 496 # 497 bld = vclay 498 bud = 1 - vclay 499 blc = bulkq - bulkc 500 buc = bulkc - bulkq 501 # 502 # Calculate quantities common only for shear 503 # formulas 504 # 505 sld = 2*vclay*(bulkc + 2*shearc)/(5*shearc) 506 sud = 2*(1 - vclay)*(bulkq + 2*shearq)/(5*shearq) 507 slc = shearq - shearc 508 suc = shearc - shearq 509 # 510 # Calculate bounds utilizing generic formula; 511 # f = a + b/((1/c) + d*(1/e)). 512 # 513 # Lower bulk 514 # 515 bulkl = self._genhashinshtr(bulkc, lb, blc, bld, 516 le) 517 # 518 # Upper bulk 519 # 520 bulku = self._genhashinshtr(bulkq, ub, buc, bud, 521 ue) 522 # 523 # Lower shear 524 # 525 shearl = self._genhashinshtr(shearc, lb, slc, 526 sld, le) 527 # 528 # Upper shear 529 # 530 shearu = self._genhashinshtr(shearq, ub, suc, 531 sud, ue) 532 # 533 return bulkl, bulku, shearl, shearu 534 535 # 536 # --------------------------------------------------- 537 # 538 def _genhashinshtr(self, a, b, c, d, e): 539 # 540 # Calculate arbitrary Hashin-Shtrikman bound, 541 # which has the generic form 542 # 543 # f = a + b/((1/c) + d*(1/e)) 544 # 545 # both for lower and upper bulk and shear bounds 546 # 547 # 548 # Input 549 # a - see above formula 550 # b - see above formula 551 # c - see above formula 552 # d - see above formula 553 # e - see above formula 554 # 555 # Output 556 # f - Bulk or shear Hashin-Shtrikman bound 557 # value 558 # 559 # ----------------------------------------------- 560 # 561 cinv = 1/c 562 einv = 1/e 563 f = a + b/(cinv + d*einv) 564 # 565 return f 566 # 567 # ======================= 568 # Solid properties end 569 # ======================= 570 # 571 572 # 573 # ============================ 574 # Dry rock properties start 575 # ============================ 576 # 577 def _dryrockmoduli(self, poro, poverb, pfluid, bulks, shears, ind=None, ntg=None, p_init=None, denss=None, Rs=None, phases=None): 578 # 579 # 580 # Calculate bulk and shear dry rock moduli, 581 # utilizing that they have the common 582 # mathematical form, 583 # 584 # -- -- ^(-1) 585 # |(poro/poroc) 1 - (poro/poroc)| 586 # f = |------------ + ----------------| - z. 587 # | a + z b + z | 588 # --. -- 589 # 590 # 591 # Input 592 # poro - porosity 593 # poverb - overburden pressure (MPa) 594 # pfluid - fluid pressure (MPa) 595 # bulks - bulk solid (mineral) rock bulk 596 # modulus (MPa) 597 # shears - solid rock (mineral) shear 598 # modulus (MPa) 599 # 600 # Output 601 # bulkd - dry rock bulk modulus (unit 602 # inherited from hertzmindlin and 603 # variable; bulks) 604 # sheard - dry rock shear modulus (unit 605 # inherited from hertzmindlin and 606 # variable; shears) 607 # 608 # ----------------------------------------------- 609 # 610 # Calculate Hertz-Mindlin moduli 611 # 612 bulkhm, shearhm = self._hertzmindlin(poverb, 613 pfluid) 614 # 615 # From table 1 in Report 1 in Abul Fahimuddin's 616 # thesis (I assume \phi_max in that 617 # table corresponds to \phi_c in his formulas): 618 # 619 poroc = 0.4 620 # 621 # Calculate input common to both bulk and 622 # shear formulas 623 # 624 poratio = poro/poroc 625 # 626 # Calculate input to bulk formula 627 # 628 ba = bulkhm 629 bb = bulks 630 bz = 4*shearhm/3 631 # 632 # Calculate input to shear formula 633 # 634 sa = shearhm 635 sb = shears 636 sz = (shearhm/6)*((9*bulkhm + 8*shearhm) / 637 (bulkhm + 2*shearhm)) 638 # 639 # Calculate moduli 640 # 641 bulkd = self._gendryrock(poratio, ba, bb, bz) 642 sheard = self._gendryrock(poratio, sa, sb, sz) 643 # 644 return bulkd, sheard 645 # 646 # --------------------------------------------------- 647 # 648 649 def _hertzmindlin(self, poverb, pfluid): 650 # 651 # Calculate bulk and shear Hertz-Mindlin moduli 652 # utilizing that they have the common 653 # mathematical form, 654 # 655 # f = <a>max*(peff/pref)^kappa. 656 # 657 # 658 # Input 659 # poverb - overburden pressure 660 # pfluid - fluid pressure 661 # 662 # Output 663 # bulkhm - Hertz-Mindlin bulk modulus 664 # (MPa) 665 # shearhm - Hertz-Mindlin shear modulus 666 # (MPa) 667 # 668 # ----------------------------------------------- 669 # 670 # From table 1 in Report 1 in Abul Fahimuddin's 671 # thesis (he used GPa for the moduli, I use MPa 672 # also for them): 673 # 674 bulkmax = 3310 675 shearmax = 2840 676 pref = 8.8 677 kappa = 0.233 678 # 679 # Calculate moduli 680 # 681 peff = poverb - pfluid 682 if peff < 0: 683 print("\nError in _hertzmindlin method") 684 print("Negative effective pressure (" + str(peff) + 685 "). Setting effective pressure to 0.01") 686 peff = 0.01 687 # sys.exit(1) 688 common = (peff/pref)**kappa 689 bulkhm = bulkmax*common 690 shearhm = shearmax*common 691 # 692 return bulkhm, shearhm 693 # 694 # --------------------------------------------------- 695 # 696 697 def _gendryrock(self, q, a, b, z): 698 # 699 # Calculate arbitrary dry rock moduli, which has 700 # the generic form 701 # 702 # -- -- ^(-1) 703 # | q 1 - q | 704 # f = |------------ + ----------------| - z, 705 # | a + z b + z | 706 # --. -- 707 # 708 # both for bulk and shear moduli 709 # 710 # 711 # Input 712 # q - see above formula 713 # a - see above formula 714 # b - see above formula 715 # z - see above formula 716 # 717 # Output 718 # f - Bulk or shear dry rock modulus value 719 # 720 # ----------------------------------------------- 721 # 722 afrac = q/(a + z) 723 bfrac = (1 - q)/(b + z) 724 f = 1/(afrac + bfrac) - z 725 # 726 return f 727 # =========================== 728 # Dry rock properties end 729 # =========================== 730 731 732if __name__ == '__main__': 733 # 734 # Example input with two phases and three grid cells 735 # 736 porosity = [0.34999999, 0.34999999, 0.34999999] 737# pressure = [ 29.29150963, 29.14003944, 28.88845444] 738 pressure = [29.3558, 29.2625, 29.3558] 739# pressure = [ 25.0, 25.0, 25.0] 740 phases = ["Oil", "Wat"] 741# saturations = [[0.72783828, 0.66568458, 0.58033288], 742# [0.27216172, 0.33431542, 0.41966712]] 743 saturations = [[0.6358, 0.5755, 0.6358], 744 [0.3641, 0.4245, 0.3641]] 745# saturations = [[0.4, 0.5, 0.6], 746# [0.6, 0.5, 0.4]] 747 petElProp = "bulk velocity" 748 input_dict = {} 749 input_dict['overburden'] = 'overb.npz' 750 751 print("\nInput:") 752 print("porosity, pressure:", porosity, pressure) 753 print("phases, saturations:", phases, saturations) 754 print("petElProp:", petElProp) 755 print("input_dict:", input_dict) 756 757 satrock = elasticproperties(input_dict) 758 759 print("overburden:", satrock.overburden) 760 761 satrock.calc_props(phases, saturations, pressure, 762 porosity) 763 764 print("\nOutput from calc_props:") 765 print("Density:", satrock.getDens()) 766 print("Bulk modulus:", satrock.getBulkMod()) 767 print("Shear modulus:", satrock.getShearMod()) 768 print("Bulk velocity:", satrock.getBulkVel()) 769 print("Shear velocity:", satrock.getShearVel()) 770 print("Bulk impedance:", satrock.getBulkImp()) 771 print("Shear impedance:", satrock.getShearImp()) 772 773 satrock.getMatchProp(petElProp) 774 775 print("\nOutput from getMatchProp:") 776 print("Model output selected for data match:", 777 satrock.match_prop)
class
elasticproperties:
14class elasticproperties: 15 """ 16 Calculate elastic properties from standard 17 rock-physics models, specifically following Batzle 18 and Wang, Geophysics, 1992, for fluid properties, and 19 Report 1 in Abul Fahimuddin's thesis at Universty of 20 Bergen (2010) for other properties. 21 22 Example 23 ------- 24 >>> porosity = 0.2 25 ... pressure = 5 26 ... phases = ["Oil","Water"] 27 ... saturations = [0.3, 0.5] 28 ... 29 ... satrock = Elasticproperties() 30 ... satrock.calc_props(phases, saturations, pressure, porosity) 31 """ 32 33 def __init__(self, input_dict): 34 self.dens = None 35 self.bulkmod = None 36 self.shearmod = None 37 self.bulkvel = None 38 self.shearvel = None 39 self.bulkimp = None 40 self.shearimp = None 41 # The overburden for each grid cell must be 42 # specified as values on an .npz-file whose 43 # name is given in input_dict. 44 self.input_dict = input_dict 45 self._extInfoInputDict() 46 47 def _extInfoInputDict(self): 48 # The key word for the file name in the 49 # dictionary must read "overburden" 50 if 'overburden' in self.input_dict: 51 obfile = self.input_dict['overburden'] 52 npzfile = np.load(obfile) 53 # The values of overburden must have been 54 # stored on file using: 55 # np.savez(<file name>, 56 # obvalues=<overburden values>) 57 self.overburden = npzfile['obvalues'] 58 npzfile.close() 59 if 'baseline' in self.input_dict: 60 self.baseline = self.input_dict['baseline'] # 4D baseline 61 if 'parallel' in self.input_dict: 62 self.parallel = self.input_dict['parallel'] 63 64 def _filter(self): 65 bulkmod = self.bulkimp 66 self.bulkimp = bulkmod.flatten() 67 68 def setup_fwd_run(self, state): 69 """ 70 Setup the input parameters to be used in the PEM simulator. Parameters can be a an ensemble or a single array. 71 State is set as an attribute of the simulator, and the correct value is determined in self.pem.calc_props() 72 73 Parameters 74 ---------- 75 state : dict 76 Dictionary of input parameters or states. 77 78 Changelog 79 --------- 80 - KF 11/12-2018 81 """ 82 # self.inv_state = {} 83 # list_pem_param =[el for el in [foo for foo in self.pem['garn'].keys()] + [foo for foo in self.filter.keys()] + 84 # [foo for foo in self.__dict__.keys()]] 85 86 # list_tot_param = state.keys() 87 # for param in list_tot_param: 88 # if param in list_pem_param or (param.split('_')[-1] in ['garn', 'rest']): 89 # self.inv_state[param] = state[param] 90 91 pass 92 93 def calc_props(self, phases, saturations, pressure, 94 porosity, wait_for_proc=None, ntg=None, Rs=None, press_init=None, ensembleMember=None): 95 ### 96 # This doesn't initialize for models with no uncertainty 97 ### 98 # # if some PEM properties have uncertainty, set the correct value 99 # if ensembleMember is not None: 100 # for pem_state in self.__dict__.keys(): # loop over all possible pem vaules 101 # if pem_state is not 'inv_state': # do not alter the ensemble 102 # if type(eval('self.{}'.format(pem_state))) is dict: 103 # for el in eval('self.{}'.format(pem_state)).keys(): 104 # if type(eval('self.{}'.format(pem_state))[el]) is dict: 105 # for param in eval('self.{}'.format(pem_state))[el].keys(): 106 # if param in self.inv_state: 107 # eval('self.{}'.format(pem_state))[el][param]=\ 108 # self.inv_state[param][:, ensembleMember] 109 # elif param + '_' + el in self.inv_state: 110 # eval('self.{}'.format(pem_state))[el][param] = \ 111 # self.inv_state[param+'_' + el][:, ensembleMember] 112 # else: 113 # if el in self.inv_state: 114 # eval('self.{}'.format(pem_state))[el] = self.inv_state[el][:,ensembleMember] 115 # else: 116 # if pem_state in self.inv_state: 117 # setattr(self,pem_state, self.inv_state[pem_state][:,ensembleMember]) 118 119 # Check if the inputs are given as a list (more 120 # than one phase) or a single input (single 121 # phase). If single phase input, make the input a 122 # list with a single entry (s.t. it can be used 123 # directly with the methods below) 124 # 125 if not isinstance(phases, list): 126 phases = [phases] 127 if not isinstance(saturations, list): 128 saturations = [saturations] 129 if not isinstance(pressure, list) and \ 130 type(pressure).__module__ != 'numpy': 131 pressure = [pressure] 132 if not isinstance(porosity, list) and \ 133 type(porosity).__module__ != 'numpy': 134 porosity = [porosity] 135 # 136 # Load "overburden" into local variable to 137 # comply with remaining code parts 138 overburden = self.overburden 139 if press_init is None: 140 p_init = self.p_init 141 else: 142 p_init = press_init 143 # 144 # Check that no. of phases is equal to no. of 145 # entries in saturations list 146 # 147 assert (len(saturations) == len(phases)) 148 # 149 # Make saturation a Numpy array (so that we 150 # can easily access the values for each 151 # phase at one grid cell) 152 # 153 # Transpose makes it a no. grid cells x phases 154 # array 155 saturations = np.array(saturations).T 156 # 157 # Check if we actually inputted saturation values 158 # for a single grid cell. If yes, we redefine 159 # saturations to get it on the correct form (no. 160 # grid cells x phases array). 161 # 162 if saturations.ndim == 1: 163 saturations = \ 164 np.array([[x] for x in saturations]).T 165 # 166 # Loop over all grid cells and calculate the 167 # various saturated properties 168 # 169 self.phases = phases 170 171 self.dens = np.zeros(len(saturations[:, 0])) 172 self.bulkmod = np.zeros(len(saturations[:, 0])) 173 self.shearmod = np.zeros(len(saturations[:, 0])) 174 self.bulkvel = np.zeros(len(saturations[:, 0])) 175 self.shearvel = np.zeros(len(saturations[:, 0])) 176 self.bulkimp = np.zeros(len(saturations[:, 0])) 177 self.shearimp = np.zeros(len(saturations[:, 0])) 178 179 if ntg is None: 180 ntg = [None for _ in range(len(saturations[:, 0]))] 181 if Rs is None: 182 Rs = [None for _ in range(len(saturations[:, 0]))] 183 if p_init is None: 184 p_init = [None for _ in range(len(saturations[:, 0]))] 185 186 for i in range(len(saturations[:, 0])): 187 # 188 # Calculate fluid properties 189 # 190 # set Rs if needed 191 densf, bulkf = \ 192 self._fluidprops(self.phases, 193 saturations[i, :], pressure[i], Rs[i]) 194 # 195 denss, bulks, shears = \ 196 self._solidprops(porosity[i], ntg[i], i) 197 # 198 # Calculate dry rock moduli 199 # 200 201 bulkd, sheard = \ 202 self._dryrockmoduli(porosity[i], 203 overburden[i], 204 pressure[i], bulks, 205 shears, i, ntg[i], p_init[i], denss, Rs[i], self.phases) 206 # ------------------------------- 207 # Calculate saturated properties 208 # ------------------------------- 209 # 210 # Density 211 # 212 self.dens[i] = (porosity[i]*densf + 213 (1-porosity[i])*denss)*0.001 214 # 215 # Moduli 216 # 217 self.bulkmod[i] = \ 218 bulkd + (1 - bulkd/bulks)**2 / \ 219 (porosity[i]/bulkf + 220 (1-porosity[i])/bulks - 221 bulkd/(bulks**2)) 222 self.shearmod[i] = sheard 223 224 # Velocities (due to bulk/shear modulus being 225 # in MPa, we multiply by 1000 to get m/s 226 # instead of km/s) 227 # 228 self.bulkvel[i] = \ 229 100*np.sqrt((abs(self.bulkmod[i]) + 230 4*self.shearmod[i]/3)/(self.dens[i])) 231 self.shearvel[i] = \ 232 100*np.sqrt(self.shearmod[i] / 233 (self.dens[i])) 234 # 235 # Impedances (m/s)*(Kg/m3) 236 # 237 self.bulkimp[i] = self.dens[i] * \ 238 self.bulkvel[i] 239 self.shearimp[i] = self.dens[i] * \ 240 self.shearvel[i] 241 242 def getMatchProp(self, petElProp): 243 if petElProp.lower() == 'density': 244 self.match_prop = self.getDens() 245 elif petElProp.lower() == 'bulk_modulus': 246 self.match_prop = self.getBulkMod() 247 elif petElProp.lower() == 'shear_modulus': 248 self.match_prop = self.getShearMod() 249 elif petElProp.lower() == 'bulk_velocity': 250 self.match_prop = self.getBulkVel() 251 elif petElProp.lower() == 'shear_velocity': 252 self.match_prop = self.getShearVel() 253 elif petElProp.lower() == "bulk_impedance": 254 self.match_prop = self.getBulkImp() 255 elif petElProp.lower() == 'shear_impedance': 256 self.match_prop = self.getShearImp() 257 else: 258 print("\nError in getMatchProp method") 259 print("No model output type selected for " 260 "data match.") 261 print("Legal model output types are " 262 "(case insensitive):") 263 print("Density, bulk modulus, shear " 264 "modulus, bulk velocity,") 265 print("shear velocity, bulk impedance, " 266 "shear impedance") 267 sys.exit(1) 268 return self.match_prop 269 270 def getDens(self): 271 return self.dens 272 273 def getBulkMod(self): 274 return self.bulkmod 275 276 def getShearMod(self): 277 return self.shearmod 278 279 def getBulkVel(self): 280 return self.bulkvel 281 282 def getShearVel(self): 283 return self.shearvel 284 285 def getBulkImp(self): 286 return self.bulkimp 287 288 def getShearImp(self): 289 return self.shearimp 290 291 # 292 # =================================================== 293 # Fluid properties start 294 # =================================================== 295 # 296 def _fluidprops(self, fphases, fsats, fpress, Rs=None): 297 # 298 # Calculate fluid density and bulk modulus 299 # 300 # 301 # Input 302 # fphases - fluid phases present; Oil 303 # and/or Water and/or Gas 304 # fsats - fluid saturation values for 305 # fluid phases in "fphases" 306 # fpress - fluid pressure value (MPa) 307 # Rs - Gas oil ratio. Default value None 308 309 # 310 # Output 311 # fdens - density of fluid mixture for 312 # pressure value "fpress" inherited 313 # from phaseprops) 314 # fbulk - bulk modulus of fluid mixture for 315 # pressure value "fpress" (unit 316 # inherited from phaseprops) 317 # 318 # ----------------------------------------------- 319 # 320 fdens = 0.0 321 fbinv = 0.0 322 323 for i in range(len(fphases)): 324 # 325 # Calculate mixture properties by summing 326 # over individual phase properties 327 # 328 pdens, pbulk = self._phaseprops(fphases[i], 329 fpress, Rs) 330 fdens = fdens + fsats[i]*abs(pdens) 331 fbinv = fbinv + fsats[i]/abs(pbulk) 332 fbulk = 1.0/fbinv 333 # 334 return fdens, fbulk 335 # 336 # --------------------------------------------------- 337 # 338 339 def _phaseprops(self, fphase, press, Rs=None): 340 # 341 # Calculate properties for a single fluid phase 342 # 343 # 344 # Input 345 # fphase - fluid phase; Oil, Water or Gas 346 # press - fluid pressure value (MPa) 347 # 348 # Output 349 # pdens - phase density of fluid phase 350 # "fphase" for pressure value 351 # "press" (kg/m³) 352 # pbulk - bulk modulus of fluid phase 353 # "fphase" for pressure value 354 # "press" (MPa) 355 # 356 # ----------------------------------------------- 357 # 358 if fphase.lower() == "oil": 359 coeffsrho = np.array([0.8, 829.9]) 360 coeffsbulk = np.array([10.42, 995.79]) 361 elif fphase.lower() == "wat": 362 coeffsrho = np.array([0.3, 1067.3]) 363 coeffsbulk = np.array([9.0, 2807.6]) 364 elif fphase.lower() == "gas": 365 coeffsrho = np.array([4.7, 13.4]) 366 coeffsbulk = np.array([2.75, 0.0]) 367 else: 368 print("\nError in phaseprops method") 369 print("Illegal fluid phase name.") 370 print("Legal fluid phase names are (case " 371 "insensitive): Oil, Wat, and Gas.") 372 sys.exit(1) 373 # 374 # Assume simple linear pressure dependencies. 375 # Coefficients are inferred from 376 # plots in Batzle and Wang, Geophysics, 1992, 377 # (where I set the temperature to be 100 degrees 378 # Celsius, Note also that they give densities in 379 # g/cc). The resulting straight lines do not fit 380 # the data extremely well, but they should 381 # be sufficiently accurate for the purpose of 382 # this project. 383 # 384 pdens = coeffsrho[0]*press + coeffsrho[1] 385 pbulk = coeffsbulk[0]*press + coeffsbulk[1] 386 # 387 return pdens, pbulk 388 389 # 390 # ======================= 391 # Fluid properties end 392 # ======================= 393 # 394 395 # 396 # ========================= 397 # Solid properties start 398 # ========================= 399 # 400 def _solidprops(self, poro, ntg=None, ind=None): 401 # 402 # Calculate bulk and shear solid rock (mineral) 403 # moduli by averaging Hashin-Shtrikman bounds 404 # 405 # 406 # Input 407 # poro -porosity 408 # 409 # Output 410 # denss - solid rock density (kg/m³) 411 # bulks - solid rock bulk modulus (unit 412 # inherited from hashinshtr) 413 # shears - solid rock shear modulus (unit 414 # inherited from hashinshtr) 415 # 416 # ----------------------------------------------- 417 # 418 # From PetroWiki (kg/m³) 419 # 420 densc = 2540 421 densq = 2650 422 # 423 # From "Step 1" of "recipe" in Report 1 in Abul 424 # Fahimuddin's thesis. 425 # 426 vclay = 0.7 - 1.58*poro 427 # 428 # Calculate solid rock (mineral) density. (Note 429 # that this is often termed \rho_dry, and not 430 # \rho_s) 431 # 432 denss = densq + vclay*(densc - densq) 433 # 434 # Calculate lower and upper bulk and shear 435 # Hashin-Shtrikman bounds 436 # 437 bulkl, bulku, shearl, shearu = \ 438 self._hashinshtr(vclay) 439 # 440 # Calculate bulk and shear solid rock (mineral) 441 # moduli as arithmetic means of the respective 442 # bounds 443 # 444 bulkb = np.array([bulkl, bulku]) 445 shearb = np.array([shearl, shearu]) 446 bulks = np.mean(bulkb) 447 shears = np.mean(shearb) 448 # 449 return denss, bulks, shears 450 # 451 # --------------------------------------------------- 452 # 453 454 def _hashinshtr(self, vclay): 455 # 456 # Calculate lower and upper, bulk and shear, 457 # Hashin-Shtrikman bounds, utilizing that they 458 # all have the common mathematical form, 459 # 460 # f = a + b/((1/c) + d*(1/e)). 461 # 462 # 463 # Input 464 # vclay - "volume of clay" 465 # 466 # Output 467 # bulkl - lower bulk Hashin-Shtrikman 468 # bound (MPa) 469 # bulku - upper bulk Hashin-Shtrikman 470 # bound (MPa) 471 # shearl - lower shear Hashin-Shtrikman 472 # bound (MPa) 473 # shearu - upper shear Hashin-Shtrikman 474 # bound (MPa) 475 # 476 # ----------------------------------------------- 477 # 478 # From table 1 in Report 1 in Abul Fahimuddin's 479 # thesis (he used GPa, I use MPa), ("c" for clay 480 # and "q" for quartz.): 481 # 482 bulkc = 14900 483 bulkq = 37000 484 shearc = 1950 485 shearq = 44000 486 # 487 # Calculate quantities common for both bulk and 488 # shear formulas 489 # 490 lb = 1 - vclay 491 ub = vclay 492 le = bulkc + 4*shearc/3 493 ue = bulkq + 4*shearq/3 494 # 495 # Calculate quantities common only for bulk 496 # formulas 497 # 498 bld = vclay 499 bud = 1 - vclay 500 blc = bulkq - bulkc 501 buc = bulkc - bulkq 502 # 503 # Calculate quantities common only for shear 504 # formulas 505 # 506 sld = 2*vclay*(bulkc + 2*shearc)/(5*shearc) 507 sud = 2*(1 - vclay)*(bulkq + 2*shearq)/(5*shearq) 508 slc = shearq - shearc 509 suc = shearc - shearq 510 # 511 # Calculate bounds utilizing generic formula; 512 # f = a + b/((1/c) + d*(1/e)). 513 # 514 # Lower bulk 515 # 516 bulkl = self._genhashinshtr(bulkc, lb, blc, bld, 517 le) 518 # 519 # Upper bulk 520 # 521 bulku = self._genhashinshtr(bulkq, ub, buc, bud, 522 ue) 523 # 524 # Lower shear 525 # 526 shearl = self._genhashinshtr(shearc, lb, slc, 527 sld, le) 528 # 529 # Upper shear 530 # 531 shearu = self._genhashinshtr(shearq, ub, suc, 532 sud, ue) 533 # 534 return bulkl, bulku, shearl, shearu 535 536 # 537 # --------------------------------------------------- 538 # 539 def _genhashinshtr(self, a, b, c, d, e): 540 # 541 # Calculate arbitrary Hashin-Shtrikman bound, 542 # which has the generic form 543 # 544 # f = a + b/((1/c) + d*(1/e)) 545 # 546 # both for lower and upper bulk and shear bounds 547 # 548 # 549 # Input 550 # a - see above formula 551 # b - see above formula 552 # c - see above formula 553 # d - see above formula 554 # e - see above formula 555 # 556 # Output 557 # f - Bulk or shear Hashin-Shtrikman bound 558 # value 559 # 560 # ----------------------------------------------- 561 # 562 cinv = 1/c 563 einv = 1/e 564 f = a + b/(cinv + d*einv) 565 # 566 return f 567 # 568 # ======================= 569 # Solid properties end 570 # ======================= 571 # 572 573 # 574 # ============================ 575 # Dry rock properties start 576 # ============================ 577 # 578 def _dryrockmoduli(self, poro, poverb, pfluid, bulks, shears, ind=None, ntg=None, p_init=None, denss=None, Rs=None, phases=None): 579 # 580 # 581 # Calculate bulk and shear dry rock moduli, 582 # utilizing that they have the common 583 # mathematical form, 584 # 585 # -- -- ^(-1) 586 # |(poro/poroc) 1 - (poro/poroc)| 587 # f = |------------ + ----------------| - z. 588 # | a + z b + z | 589 # --. -- 590 # 591 # 592 # Input 593 # poro - porosity 594 # poverb - overburden pressure (MPa) 595 # pfluid - fluid pressure (MPa) 596 # bulks - bulk solid (mineral) rock bulk 597 # modulus (MPa) 598 # shears - solid rock (mineral) shear 599 # modulus (MPa) 600 # 601 # Output 602 # bulkd - dry rock bulk modulus (unit 603 # inherited from hertzmindlin and 604 # variable; bulks) 605 # sheard - dry rock shear modulus (unit 606 # inherited from hertzmindlin and 607 # variable; shears) 608 # 609 # ----------------------------------------------- 610 # 611 # Calculate Hertz-Mindlin moduli 612 # 613 bulkhm, shearhm = self._hertzmindlin(poverb, 614 pfluid) 615 # 616 # From table 1 in Report 1 in Abul Fahimuddin's 617 # thesis (I assume \phi_max in that 618 # table corresponds to \phi_c in his formulas): 619 # 620 poroc = 0.4 621 # 622 # Calculate input common to both bulk and 623 # shear formulas 624 # 625 poratio = poro/poroc 626 # 627 # Calculate input to bulk formula 628 # 629 ba = bulkhm 630 bb = bulks 631 bz = 4*shearhm/3 632 # 633 # Calculate input to shear formula 634 # 635 sa = shearhm 636 sb = shears 637 sz = (shearhm/6)*((9*bulkhm + 8*shearhm) / 638 (bulkhm + 2*shearhm)) 639 # 640 # Calculate moduli 641 # 642 bulkd = self._gendryrock(poratio, ba, bb, bz) 643 sheard = self._gendryrock(poratio, sa, sb, sz) 644 # 645 return bulkd, sheard 646 # 647 # --------------------------------------------------- 648 # 649 650 def _hertzmindlin(self, poverb, pfluid): 651 # 652 # Calculate bulk and shear Hertz-Mindlin moduli 653 # utilizing that they have the common 654 # mathematical form, 655 # 656 # f = <a>max*(peff/pref)^kappa. 657 # 658 # 659 # Input 660 # poverb - overburden pressure 661 # pfluid - fluid pressure 662 # 663 # Output 664 # bulkhm - Hertz-Mindlin bulk modulus 665 # (MPa) 666 # shearhm - Hertz-Mindlin shear modulus 667 # (MPa) 668 # 669 # ----------------------------------------------- 670 # 671 # From table 1 in Report 1 in Abul Fahimuddin's 672 # thesis (he used GPa for the moduli, I use MPa 673 # also for them): 674 # 675 bulkmax = 3310 676 shearmax = 2840 677 pref = 8.8 678 kappa = 0.233 679 # 680 # Calculate moduli 681 # 682 peff = poverb - pfluid 683 if peff < 0: 684 print("\nError in _hertzmindlin method") 685 print("Negative effective pressure (" + str(peff) + 686 "). Setting effective pressure to 0.01") 687 peff = 0.01 688 # sys.exit(1) 689 common = (peff/pref)**kappa 690 bulkhm = bulkmax*common 691 shearhm = shearmax*common 692 # 693 return bulkhm, shearhm 694 # 695 # --------------------------------------------------- 696 # 697 698 def _gendryrock(self, q, a, b, z): 699 # 700 # Calculate arbitrary dry rock moduli, which has 701 # the generic form 702 # 703 # -- -- ^(-1) 704 # | q 1 - q | 705 # f = |------------ + ----------------| - z, 706 # | a + z b + z | 707 # --. -- 708 # 709 # both for bulk and shear moduli 710 # 711 # 712 # Input 713 # q - see above formula 714 # a - see above formula 715 # b - see above formula 716 # z - see above formula 717 # 718 # Output 719 # f - Bulk or shear dry rock modulus value 720 # 721 # ----------------------------------------------- 722 # 723 afrac = q/(a + z) 724 bfrac = (1 - q)/(b + z) 725 f = 1/(afrac + bfrac) - z 726 # 727 return f 728 # =========================== 729 # Dry rock properties end 730 # ===========================
Calculate elastic properties from standard rock-physics models, specifically following Batzle and Wang, Geophysics, 1992, for fluid properties, and Report 1 in Abul Fahimuddin's thesis at Universty of Bergen (2010) for other properties.
Example
>>> porosity = 0.2
... pressure = 5
... phases = ["Oil","Water"]
... saturations = [0.3, 0.5]
...
... satrock = Elasticproperties()
... satrock.calc_props(phases, saturations, pressure, porosity)
elasticproperties(input_dict)
33 def __init__(self, input_dict): 34 self.dens = None 35 self.bulkmod = None 36 self.shearmod = None 37 self.bulkvel = None 38 self.shearvel = None 39 self.bulkimp = None 40 self.shearimp = None 41 # The overburden for each grid cell must be 42 # specified as values on an .npz-file whose 43 # name is given in input_dict. 44 self.input_dict = input_dict 45 self._extInfoInputDict()
def
setup_fwd_run(self, state):
68 def setup_fwd_run(self, state): 69 """ 70 Setup the input parameters to be used in the PEM simulator. Parameters can be a an ensemble or a single array. 71 State is set as an attribute of the simulator, and the correct value is determined in self.pem.calc_props() 72 73 Parameters 74 ---------- 75 state : dict 76 Dictionary of input parameters or states. 77 78 Changelog 79 --------- 80 - KF 11/12-2018 81 """ 82 # self.inv_state = {} 83 # list_pem_param =[el for el in [foo for foo in self.pem['garn'].keys()] + [foo for foo in self.filter.keys()] + 84 # [foo for foo in self.__dict__.keys()]] 85 86 # list_tot_param = state.keys() 87 # for param in list_tot_param: 88 # if param in list_pem_param or (param.split('_')[-1] in ['garn', 'rest']): 89 # self.inv_state[param] = state[param] 90 91 pass
Setup the input parameters to be used in the PEM simulator. Parameters can be a an ensemble or a single array. State is set as an attribute of the simulator, and the correct value is determined in self.pem.calc_props()
Parameters
- state (dict): Dictionary of input parameters or states.
Changelog
- KF 11/12-2018
def
calc_props( self, phases, saturations, pressure, porosity, wait_for_proc=None, ntg=None, Rs=None, press_init=None, ensembleMember=None):
93 def calc_props(self, phases, saturations, pressure, 94 porosity, wait_for_proc=None, ntg=None, Rs=None, press_init=None, ensembleMember=None): 95 ### 96 # This doesn't initialize for models with no uncertainty 97 ### 98 # # if some PEM properties have uncertainty, set the correct value 99 # if ensembleMember is not None: 100 # for pem_state in self.__dict__.keys(): # loop over all possible pem vaules 101 # if pem_state is not 'inv_state': # do not alter the ensemble 102 # if type(eval('self.{}'.format(pem_state))) is dict: 103 # for el in eval('self.{}'.format(pem_state)).keys(): 104 # if type(eval('self.{}'.format(pem_state))[el]) is dict: 105 # for param in eval('self.{}'.format(pem_state))[el].keys(): 106 # if param in self.inv_state: 107 # eval('self.{}'.format(pem_state))[el][param]=\ 108 # self.inv_state[param][:, ensembleMember] 109 # elif param + '_' + el in self.inv_state: 110 # eval('self.{}'.format(pem_state))[el][param] = \ 111 # self.inv_state[param+'_' + el][:, ensembleMember] 112 # else: 113 # if el in self.inv_state: 114 # eval('self.{}'.format(pem_state))[el] = self.inv_state[el][:,ensembleMember] 115 # else: 116 # if pem_state in self.inv_state: 117 # setattr(self,pem_state, self.inv_state[pem_state][:,ensembleMember]) 118 119 # Check if the inputs are given as a list (more 120 # than one phase) or a single input (single 121 # phase). If single phase input, make the input a 122 # list with a single entry (s.t. it can be used 123 # directly with the methods below) 124 # 125 if not isinstance(phases, list): 126 phases = [phases] 127 if not isinstance(saturations, list): 128 saturations = [saturations] 129 if not isinstance(pressure, list) and \ 130 type(pressure).__module__ != 'numpy': 131 pressure = [pressure] 132 if not isinstance(porosity, list) and \ 133 type(porosity).__module__ != 'numpy': 134 porosity = [porosity] 135 # 136 # Load "overburden" into local variable to 137 # comply with remaining code parts 138 overburden = self.overburden 139 if press_init is None: 140 p_init = self.p_init 141 else: 142 p_init = press_init 143 # 144 # Check that no. of phases is equal to no. of 145 # entries in saturations list 146 # 147 assert (len(saturations) == len(phases)) 148 # 149 # Make saturation a Numpy array (so that we 150 # can easily access the values for each 151 # phase at one grid cell) 152 # 153 # Transpose makes it a no. grid cells x phases 154 # array 155 saturations = np.array(saturations).T 156 # 157 # Check if we actually inputted saturation values 158 # for a single grid cell. If yes, we redefine 159 # saturations to get it on the correct form (no. 160 # grid cells x phases array). 161 # 162 if saturations.ndim == 1: 163 saturations = \ 164 np.array([[x] for x in saturations]).T 165 # 166 # Loop over all grid cells and calculate the 167 # various saturated properties 168 # 169 self.phases = phases 170 171 self.dens = np.zeros(len(saturations[:, 0])) 172 self.bulkmod = np.zeros(len(saturations[:, 0])) 173 self.shearmod = np.zeros(len(saturations[:, 0])) 174 self.bulkvel = np.zeros(len(saturations[:, 0])) 175 self.shearvel = np.zeros(len(saturations[:, 0])) 176 self.bulkimp = np.zeros(len(saturations[:, 0])) 177 self.shearimp = np.zeros(len(saturations[:, 0])) 178 179 if ntg is None: 180 ntg = [None for _ in range(len(saturations[:, 0]))] 181 if Rs is None: 182 Rs = [None for _ in range(len(saturations[:, 0]))] 183 if p_init is None: 184 p_init = [None for _ in range(len(saturations[:, 0]))] 185 186 for i in range(len(saturations[:, 0])): 187 # 188 # Calculate fluid properties 189 # 190 # set Rs if needed 191 densf, bulkf = \ 192 self._fluidprops(self.phases, 193 saturations[i, :], pressure[i], Rs[i]) 194 # 195 denss, bulks, shears = \ 196 self._solidprops(porosity[i], ntg[i], i) 197 # 198 # Calculate dry rock moduli 199 # 200 201 bulkd, sheard = \ 202 self._dryrockmoduli(porosity[i], 203 overburden[i], 204 pressure[i], bulks, 205 shears, i, ntg[i], p_init[i], denss, Rs[i], self.phases) 206 # ------------------------------- 207 # Calculate saturated properties 208 # ------------------------------- 209 # 210 # Density 211 # 212 self.dens[i] = (porosity[i]*densf + 213 (1-porosity[i])*denss)*0.001 214 # 215 # Moduli 216 # 217 self.bulkmod[i] = \ 218 bulkd + (1 - bulkd/bulks)**2 / \ 219 (porosity[i]/bulkf + 220 (1-porosity[i])/bulks - 221 bulkd/(bulks**2)) 222 self.shearmod[i] = sheard 223 224 # Velocities (due to bulk/shear modulus being 225 # in MPa, we multiply by 1000 to get m/s 226 # instead of km/s) 227 # 228 self.bulkvel[i] = \ 229 100*np.sqrt((abs(self.bulkmod[i]) + 230 4*self.shearmod[i]/3)/(self.dens[i])) 231 self.shearvel[i] = \ 232 100*np.sqrt(self.shearmod[i] / 233 (self.dens[i])) 234 # 235 # Impedances (m/s)*(Kg/m3) 236 # 237 self.bulkimp[i] = self.dens[i] * \ 238 self.bulkvel[i] 239 self.shearimp[i] = self.dens[i] * \ 240 self.shearvel[i]
def
getMatchProp(self, petElProp):
242 def getMatchProp(self, petElProp): 243 if petElProp.lower() == 'density': 244 self.match_prop = self.getDens() 245 elif petElProp.lower() == 'bulk_modulus': 246 self.match_prop = self.getBulkMod() 247 elif petElProp.lower() == 'shear_modulus': 248 self.match_prop = self.getShearMod() 249 elif petElProp.lower() == 'bulk_velocity': 250 self.match_prop = self.getBulkVel() 251 elif petElProp.lower() == 'shear_velocity': 252 self.match_prop = self.getShearVel() 253 elif petElProp.lower() == "bulk_impedance": 254 self.match_prop = self.getBulkImp() 255 elif petElProp.lower() == 'shear_impedance': 256 self.match_prop = self.getShearImp() 257 else: 258 print("\nError in getMatchProp method") 259 print("No model output type selected for " 260 "data match.") 261 print("Legal model output types are " 262 "(case insensitive):") 263 print("Density, bulk modulus, shear " 264 "modulus, bulk velocity,") 265 print("shear velocity, bulk impedance, " 266 "shear impedance") 267 sys.exit(1) 268 return self.match_prop