Compare commits
756 Commits
release/20
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
1a2b0c8cba | ||
|
07151426f1 | ||
|
4e62c66dcd | ||
|
f693b757d3 | ||
|
d887618581 | ||
|
8240cca69e | ||
|
aa125c2620 | ||
|
68cd8510cd | ||
|
cb61da132b | ||
|
de2c3817bc | ||
|
f4111ead14 | ||
|
5881cfe5da | ||
|
9603fbd57f | ||
|
6c9365ac17 | ||
|
2977d8659e | ||
|
d700402b15 | ||
|
666591d79c | ||
|
900dbc6096 | ||
|
a5c14af204 | ||
|
cf82a41229 | ||
|
529278d2b9 | ||
|
cc20b89e73 | ||
|
0d96749d4d | ||
|
c2c35b90b8 | ||
|
3b89515a8c | ||
|
8b34a6e795 | ||
|
a77b07ddfd | ||
|
b190a0531d | ||
|
1f4447df98 | ||
|
0f70255b86 | ||
|
761788100d | ||
|
6264404883 | ||
|
8d98d76c17 | ||
|
a1543115aa | ||
|
204ef58bb6 | ||
|
634a7bef42 | ||
|
fbec8c2300 | ||
|
9796066199 | ||
|
ec58bfb224 | ||
|
93a7b2da36 | ||
|
c685e86f91 | ||
|
d97a5c5734 | ||
|
febdc04e80 | ||
|
efc2915b74 | ||
|
0c1ed25eb1 | ||
|
c9c4c81661 | ||
|
55348d6441 | ||
|
54b5910921 | ||
|
0a4cc569aa | ||
|
860e3d5831 | ||
|
97098ac190 | ||
|
bcd83025ec | ||
|
1b13f44e12 | ||
|
1d1347d2b3 | ||
|
19796e2ddc | ||
|
2a88805822 | ||
|
37421c62da | ||
|
729d6cf30e | ||
|
f786d4749e | ||
|
d1a654f414 | ||
|
bce19d87dc | ||
|
833b019413 | ||
|
3486e98ae3 | ||
|
e613f2b8c5 | ||
|
12108d57ab | ||
|
8def11ff93 | ||
|
d049125205 | ||
|
acc631f504 | ||
|
d4bbaa1d3e | ||
|
476a64c370 | ||
|
c3121f0a1a | ||
|
b208db1b74 | ||
|
18e230a18b | ||
|
c2dd80f6c5 | ||
|
c1f16be87b | ||
|
132cfc0ff7 | ||
|
210364ecde | ||
|
10a07ef9a4 | ||
|
1e0135c9bd | ||
|
ac9a20da20 | ||
|
e5a7535a22 | ||
|
e4a8fad29c | ||
|
c8fc2bd336 | ||
|
a46e2c7121 | ||
|
e0fc476fbe | ||
|
75ff5ea7d5 | ||
|
7e40af760f | ||
|
0a6baab957 | ||
|
8f445699e9 | ||
|
8321530c4e | ||
|
75657ff482 | ||
|
417c5b2d46 | ||
|
2ab9b5de45 | ||
|
33305c4f1f | ||
|
f696042f26 | ||
|
6ff0433435 | ||
|
8a3fe0a3f6 | ||
|
660a65aec9 | ||
|
9a278c32b1 | ||
|
b544bfe9cd | ||
|
babfbd5e28 | ||
|
1f8e56311e | ||
|
355d4e7809 | ||
|
1a8ae0a95a | ||
|
059cfa5a0c | ||
|
96c4e8f531 | ||
|
b8838d28b3 | ||
|
d3b7f24abb | ||
|
c98683e31e | ||
|
9a306def92 | ||
|
4a8d74a910 | ||
|
843e8c9a6f | ||
|
86524b27aa | ||
|
41234f1bee | ||
|
d935458fca | ||
|
fc380041f2 | ||
|
a8a46ee5ae | ||
|
4bab353e90 | ||
|
d058d8e380 | ||
|
67fbac5c95 | ||
|
d504715caa | ||
|
327d146caa | ||
|
1231870e56 | ||
|
3f403a8470 | ||
|
02b1e58a77 | ||
|
dbe7930ef7 | ||
|
daecfa7e55 | ||
|
419aedfcbf | ||
|
acc0f81260 | ||
|
f5999e3fcb | ||
|
eeadc43cad | ||
|
a59ce6581f | ||
|
d837401d33 | ||
|
9dc2fc4a82 | ||
|
fdd7b39587 | ||
|
0cb21a8c04 | ||
|
d690549842 | ||
|
a96c1f9ea0 | ||
|
e52b7ef2ad | ||
|
b6c5247ef9 | ||
|
aed63397c5 | ||
|
8ef90deb2a | ||
|
2e9b04fc37 | ||
|
e77f726906 | ||
|
29372e287c | ||
|
44924b9ee9 | ||
|
bd9ff2ad94 | ||
|
b7ea707928 | ||
|
a6f4772ca1 | ||
|
17fb6f9ffe | ||
|
d50d21d8e0 | ||
|
2fcb449196 | ||
|
7701b68814 | ||
|
81fb20160c | ||
|
aaaacce49e | ||
|
be00224a4b | ||
|
524233c60a | ||
|
b368cb0177 | ||
|
3b5e15771b | ||
|
901dbc248e | ||
|
c3dc875005 | ||
|
a8261b62da | ||
|
e2f8375a1a | ||
|
e289c8f35b | ||
|
77b9311f2c | ||
|
0b1d5745a6 | ||
|
b55ce6b53e | ||
|
53eb7e2f1c | ||
|
1caaa5bd35 | ||
|
d3d7c64107 | ||
|
7579f2bdb9 | ||
|
7bf6da1953 | ||
|
70b19780b4 | ||
|
03e005bb76 | ||
|
aecfa0e448 | ||
|
70b6a6e5ef | ||
|
b97c585519 | ||
|
6ca48313ca | ||
|
6d01c29686 | ||
|
8198c256a9 | ||
|
d5a9f27e5e | ||
|
e6cad074c2 | ||
|
119b5b1a3a | ||
|
61e8c0b8d3 | ||
|
1e25b6fcfb | ||
|
8ef2a923ec | ||
|
c5a80e97a2 | ||
|
c5a0ea7524 | ||
|
5aa5ac89c5 | ||
|
78bf488a2a | ||
|
5e67765229 | ||
|
24f709f983 | ||
|
674d97c660 | ||
|
cb84571540 | ||
|
9fe6d80f99 | ||
|
3a06a2dd50 | ||
|
8f658a92f2 | ||
|
713c833b0a | ||
|
7830083e9b | ||
|
ac6965de2e | ||
|
8851f072a3 | ||
|
00dad30432 | ||
|
7ef8971be4 | ||
|
8daa0f440c | ||
|
e2023ed38e | ||
|
898ca1b7e0 | ||
|
be26c072e1 | ||
|
3e3a76d37d | ||
|
d5e2e5d2e5 | ||
|
5ac89ad8a7 | ||
|
cf9f37169d | ||
|
fdd81eacaa | ||
|
d89b48689b | ||
|
2e49273da7 | ||
|
bca5c8e8de | ||
|
ad4033b9dc | ||
|
ec060d513b | ||
|
8cc624fd0d | ||
|
4ea6611ecc | ||
|
2a3a825895 | ||
|
f65739c718 | ||
|
62c56ff716 | ||
|
59cc0a1635 | ||
|
e53b0a58fa | ||
|
f0b4c4f390 | ||
|
28a02f6953 | ||
|
dec5bdf7c4 | ||
|
078de1b039 | ||
|
b5b3507cb0 | ||
|
87650a3819 | ||
|
f72f996008 | ||
|
fd552926d8 | ||
|
ce02a4bb92 | ||
|
ebfcea125d | ||
|
868d008353 | ||
|
8d129d7172 | ||
|
ec85a922ac | ||
|
c7cc24385a | ||
|
602f3252e6 | ||
|
647e005297 | ||
|
1edcdbc055 | ||
|
1dd4500bd2 | ||
|
cadb14c9e8 | ||
|
1e462f4c04 | ||
|
4427c85d77 | ||
|
af874ada54 | ||
|
3c35e767d4 | ||
|
1429046029 | ||
|
1d98c3b8ca | ||
|
fda1016532 | ||
|
054fa92e61 | ||
|
6e4f9e708e | ||
|
df920e4970 | ||
|
c901e2ec7c | ||
|
c3b00dc7fd | ||
|
708bfd169b | ||
|
bf2f9b3f06 | ||
|
55eec0b2ed | ||
|
f9f5bacee4 | ||
|
0cf4021698 | ||
|
91b8c872eb | ||
|
95997e208c | ||
|
085785bf26 | ||
|
b319e1a75d | ||
|
3e8b1bdb82 | ||
|
76a2108ea9 | ||
|
88181f4948 | ||
|
06d380df51 | ||
|
2b289f8964 | ||
|
63e5755fa9 | ||
|
a0e1fcf89d | ||
|
013c907e66 | ||
|
2e135388a6 | ||
|
b8ac674f9b | ||
|
65d61d1c4f | ||
|
352c185edf | ||
|
32e9b26ce8 | ||
|
fec53a1af5 | ||
|
e183ab6ccd | ||
|
7295f26f54 | ||
|
46a9a62741 | ||
|
6942a986da | ||
|
e28715b601 | ||
|
a438680fb0 | ||
|
0d5a86cc71 | ||
|
f628064884 | ||
|
4214cfec83 | ||
|
36bedfcf67 | ||
|
3bdf0eae11 | ||
|
c5958da6c9 | ||
|
a0d3ceff62 | ||
|
f79fe1f7ad | ||
|
48cbbdee19 | ||
|
24acbd1c23 | ||
|
59ef5f2b11 | ||
|
630d5477d8 | ||
|
76bdc081c0 | ||
|
be462f6c3a | ||
|
e348baa6c7 | ||
|
5376fb618d | ||
|
23ef9dce5c | ||
|
5d33f0d900 | ||
|
e0dc670604 | ||
|
d96b7193ee | ||
|
1387c5f834 | ||
|
1ce4c47b09 | ||
|
5963dd0027 | ||
|
9dfd16cffb | ||
|
125d1696d6 | ||
|
ad5d98664c | ||
|
d082d977fd | ||
|
cc72693348 | ||
|
044d47b9fb | ||
|
c884b5c1d5 | ||
|
069b65a635 | ||
|
e38b9ffcb7 | ||
|
8e8b624d4c | ||
|
7d9097490b | ||
|
f4586f5380 | ||
|
e98d6204ad | ||
|
1057e6d3d0 | ||
|
518c9849ff | ||
|
4ae5f16cb3 | ||
|
1fe0ee77c6 | ||
|
33872cfb4a | ||
|
f72832dbd6 | ||
|
99d8ab73f4 | ||
|
35ffd9b647 | ||
|
d25cefa011 | ||
|
7a295747de | ||
|
07707ecc30 | ||
|
f91cb76086 | ||
|
5fdb868526 | ||
|
edb57704f9 | ||
|
29ba3465b4 | ||
|
df24d28671 | ||
|
a44500623a | ||
|
22f7d1ec32 | ||
|
dda280fcf9 | ||
|
227b5851b8 | ||
|
04fe79aa23 | ||
|
f202f2948c | ||
|
a6995fdf3e | ||
|
fe19c589c4 | ||
|
98402c2e8d | ||
|
e8c3389b80 | ||
|
3647834b96 | ||
|
60da3d9213 | ||
|
ac37eef547 | ||
|
08b90e8acd | ||
|
7f1026551a | ||
|
74e9925769 | ||
|
cd75174650 | ||
|
040e3f9872 | ||
|
ff81d48551 | ||
|
f426a03801 | ||
|
f1bdd67438 | ||
|
6e9bb4cf0b | ||
|
ce78dc5560 | ||
|
18a5c0b748 | ||
|
4186a37563 | ||
|
cf16b0affe | ||
|
ff7be8eced | ||
|
a1058d45ea | ||
|
da29e292b5 | ||
|
82822160af | ||
|
21de431eb1 | ||
|
c4f0539534 | ||
|
05add1884f | ||
|
17f93ce3bd | ||
|
474128fe2d | ||
|
19f6d540fc | ||
|
8666c8b687 | ||
|
8efc75367b | ||
|
466e721e0b | ||
|
b7bb01b77e | ||
|
d71aceff0b | ||
|
9615f0e9d7 | ||
|
aef10cc292 | ||
|
8329dc7474 | ||
|
14cd4b0472 | ||
|
89a22c30b0 | ||
|
8ea0b62b33 | ||
|
a6a95eeac1 | ||
|
77a93abcac | ||
|
2a773b735e | ||
|
6f264781b0 | ||
|
d3ff659437 | ||
|
a6f08d13d5 | ||
|
bc0977ec3d | ||
|
bb387e16f9 | ||
|
25c4a8c156 | ||
|
65c7a2c197 | ||
|
a792c29f00 | ||
|
dae94b5041 | ||
|
ff899f3c53 | ||
|
6f8acf49f8 | ||
|
1fc189065c | ||
|
c58a0fce43 | ||
|
30bc54ba4a | ||
|
4b4492e877 | ||
|
a07f628469 | ||
|
c7c38374ec | ||
|
bc1fb8e477 | ||
|
9c6eeae068 | ||
|
50573d1168 | ||
|
2c25bb8262 | ||
|
745666468f | ||
|
c1b4ed8bc8 | ||
|
cd32e65632 | ||
|
7d97ef7e4a | ||
|
f66f699fca | ||
|
2177aa745e | ||
|
fd049a6962 | ||
|
3d5689dc72 | ||
|
f1024bb210 | ||
|
afc9e4453a | ||
|
49d5d4c79a | ||
|
bdebcf48a3 | ||
|
06cd6c85f3 | ||
|
7ddf390bf8 | ||
|
c686ec69d7 | ||
|
08e8297b47 | ||
|
4323b2f4fa | ||
|
db03e88090 | ||
|
bd9c514457 | ||
|
4db70d1902 | ||
|
d9c33cbcb6 | ||
|
9b2acf96bf | ||
|
aee0020a98 | ||
|
f3f6170881 | ||
|
6e2099d293 | ||
|
89cb2680a1 | ||
|
8f4d65a0c2 | ||
|
6fdfcefaa0 | ||
|
15e12c89bf | ||
|
8f2e2c91b1 | ||
|
8b2ebc690b | ||
|
e6da69575a | ||
|
291b13c2d4 | ||
|
34805c1bf7 | ||
|
a824ef9c22 | ||
|
cb82608779 | ||
|
b5dc48af0e | ||
|
fcb99e425d | ||
|
d87afa29ab | ||
|
881ed8fc49 | ||
|
1b6048016b | ||
|
bbd6a5f03d | ||
|
ba255d89d3 | ||
|
192bb54e70 | ||
|
413e3fe381 | ||
|
6ae9c3104f | ||
|
f54c74db06 | ||
|
4e40b784af | ||
|
98707b87a8 | ||
|
6a8ddb76b2 | ||
|
d3aa0926c8 | ||
|
c94cdb0de8 | ||
|
bb2a582aa8 | ||
|
1db1b3d178 | ||
|
f948d9ea44 | ||
|
7f355929ac | ||
|
324e60171c | ||
|
681846686a | ||
|
f5cf2d47e1 | ||
|
20662fffb9 | ||
|
ae9a10942e | ||
|
42e5e41fa2 | ||
|
2ad419bd1a | ||
|
392dc8557d | ||
|
c0b0b0d40a | ||
|
c6fc7f5e30 | ||
|
1bf201f294 | ||
|
be0187d512 | ||
|
39924206ca | ||
|
408ea0845d | ||
|
2d2f86f089 | ||
|
8f3829bb76 | ||
|
7de314bb7e | ||
|
dbf425411f | ||
|
ef2125f39e | ||
|
94ee69505a | ||
|
7eba8b01f5 | ||
|
eab67baa3b | ||
|
97eeee516d | ||
|
341472456a | ||
|
b7f7352b6d | ||
|
537c4dd730 | ||
|
f582de6023 | ||
|
d06b515c47 | ||
|
f55d216561 | ||
|
a44ab90759 | ||
|
2a43e9631f | ||
|
b3db61bf7e | ||
|
b5e78ab1ec | ||
|
3028e819c6 | ||
|
b25a964597 | ||
|
98a413bf5c | ||
|
747ec8fa8c | ||
|
d660dea73b | ||
|
79f04b6c9e | ||
|
cb4b698b51 | ||
|
31133af803 | ||
|
dd510244f8 | ||
|
da70ab4e84 | ||
|
8b43643e25 | ||
|
9f90e6cc3f | ||
|
684614fe77 | ||
|
e7a0b60b1c | ||
|
a5d858f396 | ||
|
022e594850 | ||
|
b248655314 | ||
|
94266d37fc | ||
|
1fbc689f6c | ||
|
131e900cf3 | ||
|
c8d5174113 | ||
|
404929fe34 | ||
|
6db874cbd7 | ||
|
f9b7a5fe66 | ||
|
3fd6a47e1c | ||
|
973438128c | ||
|
668cd14755 | ||
|
b07f4dd494 | ||
|
34ee734737 | ||
|
6d9ae3f050 | ||
|
30956131ec | ||
|
145d61d751 | ||
|
b6e381d627 | ||
|
e76b9dd7f1 | ||
|
165b17564d | ||
|
972c55ea52 | ||
|
b307259ce2 | ||
|
78a927c859 | ||
|
eacbd4f214 | ||
|
acade0bb5f | ||
|
284f3702a2 | ||
|
fee514375d | ||
|
1e0356d392 | ||
|
9dbae15cc9 | ||
|
8a7bf1ef3c | ||
|
277b3a2e58 | ||
|
90e9374a2b | ||
|
ccb34c14ec | ||
|
33bb10b98f | ||
|
b5cbf0ea2d | ||
|
2b9b1f8b05 | ||
|
ccac8fe6ac | ||
|
7bc1ec7693 | ||
|
a20da3a6d2 | ||
|
e1298ff21b | ||
|
5e6e4b2a38 | ||
|
53485a7ae7 | ||
|
0fb81945f6 | ||
|
88d41057c0 | ||
|
09c31b67ca | ||
|
730f1e88b7 | ||
|
b8bad8b26b | ||
|
77a6c4229a | ||
|
8e68a4d815 | ||
|
03d5a5ba25 | ||
|
9febba59a4 | ||
|
ce6cdb4a47 | ||
|
7a5e8865b5 | ||
|
e4929b7263 | ||
|
c360079926 | ||
|
3f63d09f86 | ||
|
a2d64e681e | ||
|
fcd4368da4 | ||
|
05c2fb7c5e | ||
|
4f857b71fc | ||
|
755873f5cc | ||
|
212e3c26a4 | ||
|
81f6c2dd8e | ||
|
0a5be9ba52 | ||
|
d943cbfe2a | ||
|
2ca569d2e4 | ||
|
6bcca2ea23 | ||
|
31aac3481d | ||
|
897aa67b89 | ||
|
26e18bdcc4 | ||
|
aa32349f19 | ||
|
eafa6e2d9a | ||
|
47fefe9e3a | ||
|
b7972eb01c | ||
|
95d65222fa | ||
|
3e8d6b521f | ||
|
c635d84d37 | ||
|
bd91cf6e72 | ||
|
dfda345661 | ||
|
41faabe61a | ||
|
23088f987f | ||
|
0c05fc4dc0 | ||
|
eb0f59f96d | ||
|
72b9bdabf4 | ||
|
9a83cbaddf | ||
|
2a4ca8a34e | ||
|
54ac4bd5e4 | ||
|
10468bafab | ||
|
58f53cae2a | ||
|
a703bfbcf1 | ||
|
e12ca5a825 | ||
|
27a240ae7f | ||
|
85232bb355 | ||
|
5c2257cafa | ||
|
01df431198 | ||
|
8f042081f7 | ||
|
82472a1dfd | ||
|
893e6c851f | ||
|
df6e6a2347 | ||
|
6ca10ea57d | ||
|
6c5fae2f9d | ||
|
a214c10595 | ||
|
089229adf4 | ||
|
377b844862 | ||
|
c709e347c3 | ||
|
be1ea358ff | ||
|
0297d6c657 | ||
|
58f355935f | ||
|
61ebad5bba | ||
|
7909311749 | ||
|
b8edc8b5b4 | ||
|
1661ac8f58 | ||
|
62d7c7bed2 | ||
|
dc918fcff9 | ||
|
8f0d81db2f | ||
|
6515b57632 | ||
|
5172652e73 | ||
|
88aad94f54 | ||
|
ab3ea52384 | ||
|
279799ee80 | ||
|
9b1efd7810 | ||
|
b241fde63c | ||
|
4e46bb3fa4 | ||
|
5ce1d84ac1 | ||
|
df0d11b8de | ||
|
e6c89a595b | ||
|
6365167109 | ||
|
d738138afa | ||
|
697763a4b9 | ||
|
282cac57ea | ||
|
ba1c06d415 | ||
|
10baa52828 | ||
|
56868d8a6e | ||
|
9e9287f20d | ||
|
039f9ce1cc | ||
|
229282c310 | ||
|
009f1b101b | ||
|
1686b45221 | ||
|
c7349b5586 | ||
|
8e397ef7ce | ||
|
ce0ec1e466 | ||
|
dd9b607c99 | ||
|
743f17169d | ||
|
c02c93ce1b | ||
|
2f2bd8a955 | ||
|
682d0a7d1f | ||
|
3bce4e72b9 | ||
|
5e234fcd21 | ||
|
1f809cf3d5 | ||
|
3e143907d3 | ||
|
f38fe6d91b | ||
|
b26eb3827f | ||
|
bbcc39805d | ||
|
5d50776d1b | ||
|
a35520a65d | ||
|
a6deb02d04 | ||
|
1b9be6d4bd | ||
|
ca60041332 | ||
|
a566ac608d | ||
|
65ff9bb75b | ||
|
c003900145 | ||
|
1a8b38dcc9 | ||
|
576d24a070 | ||
|
125c27462d | ||
|
8827ca0794 | ||
|
cc07dc80b7 | ||
|
7689f80a7f | ||
|
a819d314f8 | ||
|
8605675199 | ||
|
43abdf5946 | ||
|
c208d32498 | ||
|
7e4bff4a18 | ||
|
3d7ce4ece2 | ||
|
b8f54e4e59 | ||
|
97525de43a | ||
|
a7c1810029 | ||
|
97466d988b | ||
|
dec23df301 | ||
|
ef203a1d04 | ||
|
8d393ac816 | ||
|
e8ab4341f1 | ||
|
51111aa845 | ||
|
1de19dd47d | ||
|
130f222cb0 | ||
|
4790c2a2eb | ||
|
252af19e4b | ||
|
fc20fe5be9 | ||
|
944e1565fd | ||
|
780292875a | ||
|
91e14bec36 | ||
|
544c7c24f9 | ||
|
5cfc3961db | ||
|
6d06c36f64 | ||
|
d4ca0d8c00 | ||
|
4cded211b0 | ||
|
ec25abb15f | ||
|
05ebcc2067 | ||
|
839d2e0471 | ||
|
e600383589 | ||
|
df6043ace6 | ||
|
fd0dbe1d48 | ||
|
89d688d0f8 | ||
|
9428c829c3 | ||
|
57f4348f6d | ||
|
c128e218da | ||
|
8810a47215 | ||
|
9e7d287212 | ||
|
4d2c656a72 | ||
|
5938bbc74b | ||
|
26b680970f | ||
|
1db1bf6fd6 | ||
|
84b3bcc6ea | ||
|
e7d57bd0e6 | ||
|
556e24f008 | ||
|
dc9f73e94d | ||
|
ec0f28c176 | ||
|
d9dfd852f5 | ||
|
909f62ef4b | ||
|
9f7a1be749 | ||
|
14a4b00695 | ||
|
e2ec1c8547 | ||
|
e5d3b3e10f | ||
|
23c27cc826 | ||
|
6ac19ac961 | ||
|
e49e9bd9b9 | ||
|
f4fcb616be | ||
|
6a9e11a4e1 | ||
|
547b701a57 | ||
|
8d253606fc | ||
|
7542aca827 | ||
|
bdd5104bc5 | ||
|
11cd4d56ad | ||
|
b12708785c | ||
|
bd760d3e61 | ||
|
de66419e38 | ||
|
6dc67229f1 | ||
|
4dbb517d92 | ||
|
5676bd4573 | ||
|
de6cf63e50 | ||
|
8b7ccd9b19 | ||
|
21412edc97 | ||
|
b20c21feb8 | ||
|
d43e447852 | ||
|
7df248d2d6 | ||
|
40220994c7 |
|
@ -13,47 +13,44 @@
|
|||
# cmake/Modules/${project}-prereqs.cmake Dependencies #
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
# Mandatory call to project
|
||||
project(opm-core C CXX)
|
||||
|
||||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
# additional search modules
|
||||
set( OPM_COMMON_ROOT "" CACHE PATH "Root directory containing OPM related cmake modules")
|
||||
option(SIBLING_SEARCH "Search for other modules in sibling directories?" ON)
|
||||
|
||||
if(NOT OPM_COMMON_ROOT)
|
||||
find_package(opm-common QUIET)
|
||||
if(SIBLING_SEARCH AND NOT opm-common_DIR)
|
||||
# guess the sibling dir
|
||||
get_filename_component(_leaf_dir_name ${PROJECT_BINARY_DIR} NAME)
|
||||
get_filename_component(_parent_full_dir ${PROJECT_BINARY_DIR} DIRECTORY)
|
||||
get_filename_component(_parent_dir_name ${_parent_full_dir} NAME)
|
||||
#Try if <module-name>/<build-dir> is used
|
||||
get_filename_component(_modules_dir ${_parent_full_dir} DIRECTORY)
|
||||
if(IS_DIRECTORY ${_modules_dir}/opm-common/${_leaf_dir_name})
|
||||
set(opm-common_DIR ${_modules_dir}/opm-common/${_leaf_dir_name})
|
||||
else()
|
||||
string(REPLACE ${PROJECT_NAME} opm-common _opm_common_leaf ${_leaf_dir_name})
|
||||
if(NOT _leaf_dir_name STREQUAL _opm_common_leaf
|
||||
AND IS_DIRECTORY ${_parent_full_dir}/${_opm_common_leaf})
|
||||
# We are using build directories named <prefix><module-name><postfix>
|
||||
set(opm-common_DIR ${_parent_full_dir}/${_opm_common_leaf})
|
||||
elseif(IS_DIRECTORY ${_parent_full_dir}/opm-common)
|
||||
# All modules are in a common build dir
|
||||
set(opm-common_DIR "${_parent_full_dir}/opm-common}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(opm-common_DIR AND NOT IS_DIRECTORY ${opm-common_DIR})
|
||||
message(WARNING "Value ${opm-common_DIR} passed to variable"
|
||||
" opm-common_DIR is not a directory")
|
||||
endif()
|
||||
|
||||
if (opm-common_FOUND)
|
||||
include(OpmInit)
|
||||
else()
|
||||
unset(opm-common_FOUND)
|
||||
find_package(opm-common REQUIRED)
|
||||
|
||||
if (NOT OPM_COMMON_ROOT AND SIBLING_SEARCH)
|
||||
set(OPM_COMMON_ROOT ${PROJECT_SOURCE_DIR}/../opm-common)
|
||||
endif()
|
||||
if (OPM_COMMON_ROOT)
|
||||
list( APPEND CMAKE_MODULE_PATH "${OPM_COMMON_ROOT}/cmake/Modules")
|
||||
include (OpmInit OPTIONAL RESULT_VARIABLE OPM_INIT)
|
||||
set( OPM_MACROS_ROOT ${OPM_COMMON_ROOT} )
|
||||
endif()
|
||||
|
||||
if (NOT OPM_INIT)
|
||||
message( "" )
|
||||
message( " /---------------------------------------------------------------------------------\\")
|
||||
message( " | Could not locate the opm build macros. The opm build macros |")
|
||||
message( " | are in a separate repository - instructions to proceed: |")
|
||||
message( " | |")
|
||||
message( " | 1. Clone the repository: git clone git@github.com:OPM/opm-common.git |")
|
||||
message( " | |")
|
||||
message( " | 2. Run cmake in the current project with -DOPM_COMMON_ROOT=<path>/opm-common |")
|
||||
message( " | |")
|
||||
message( " \\---------------------------------------------------------------------------------/")
|
||||
message( "" )
|
||||
message( FATAL_ERROR "Could not find OPM Macros")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
include(OpmInit)
|
||||
|
||||
# not the same location as most of the other projects; this hook overrides
|
||||
macro (dir_hook)
|
||||
|
@ -84,24 +81,7 @@ macro (sources_hook)
|
|||
${PROJECT_SOURCE_DIR}/${opm-core_DIR}/core/linalg/LinearSolverIstl.cpp
|
||||
)
|
||||
endif (NOT dune-istl_FOUND)
|
||||
if (NOT SuiteSparse_FOUND)
|
||||
list (REMOVE_ITEM opm-core_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/${opm-core_DIR}/core/linalg/call_umfpack.c
|
||||
${PROJECT_SOURCE_DIR}/${opm-core_DIR}/core/linalg/LinearSolverUmfpack.cpp
|
||||
)
|
||||
list (REMOVE_ITEM examples_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/tutorials/tutorial2.cpp
|
||||
${PROJECT_SOURCE_DIR}/tutorials/tutorial3.cpp
|
||||
${PROJECT_SOURCE_DIR}/tutorials/tutorial4.cpp
|
||||
)
|
||||
endif (NOT SuiteSparse_FOUND)
|
||||
|
||||
if (NOT PETSC_FOUND)
|
||||
list (REMOVE_ITEM opm-core_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/${opm-core_DIR}/core/linalg/call_petsc.c
|
||||
${PROJECT_SOURCE_DIR}/${opm-core_DIR}/core/linalg/LinearSolverPetsc.cpp
|
||||
)
|
||||
endif (NOT PETSC_FOUND)
|
||||
if ((NOT MPI_FOUND) OR (NOT DUNE_ISTL_FOUND))
|
||||
list (REMOVE_ITEM tests_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/tests/test_parallel_linearsolver.cpp
|
||||
|
|
|
@ -29,188 +29,92 @@
|
|||
# originally generated with the command:
|
||||
# find opm -name '*.c*' -printf '\t%p\n' | sort
|
||||
list (APPEND MAIN_SOURCE_FILES
|
||||
opm/core/grid/GridHelpers.cpp
|
||||
opm/core/grid/GridManager.cpp
|
||||
opm/core/grid/GridUtilities.cpp
|
||||
opm/core/grid/grid.c
|
||||
opm/core/grid/cart_grid.c
|
||||
opm/core/grid/cornerpoint_grid.c
|
||||
opm/core/grid/cpgpreprocess/facetopology.c
|
||||
opm/core/grid/cpgpreprocess/geometry.c
|
||||
opm/core/grid/cpgpreprocess/preprocess.c
|
||||
opm/core/grid/cpgpreprocess/uniquepoints.c
|
||||
opm/core/io/eclipse/EclipseGridInspector.cpp
|
||||
opm/core/io/eclipse/EclipseWriter.cpp
|
||||
opm/core/io/eclipse/EclipseReader.cpp
|
||||
opm/core/io/eclipse/EclipseWriteRFTHandler.cpp
|
||||
opm/core/io/eclipse/writeECLData.cpp
|
||||
opm/core/io/OutputWriter.cpp
|
||||
opm/core/io/vag/vag.cpp
|
||||
opm/core/io/vtk/writeVtkData.cpp
|
||||
opm/core/linalg/LinearSolverFactory.cpp
|
||||
opm/core/linalg/LinearSolverInterface.cpp
|
||||
opm/core/linalg/LinearSolverIstl.cpp
|
||||
opm/core/linalg/LinearSolverUmfpack.cpp
|
||||
opm/core/linalg/LinearSolverPetsc.cpp
|
||||
opm/core/linalg/call_umfpack.c
|
||||
opm/core/linalg/sparse_sys.c
|
||||
opm/core/pressure/CompressibleTpfa.cpp
|
||||
opm/core/pressure/FlowBCManager.cpp
|
||||
opm/core/pressure/IncompTpfa.cpp
|
||||
opm/core/pressure/IncompTpfaSinglePhase.cpp
|
||||
opm/core/pressure/cfsh.c
|
||||
opm/core/pressure/flow_bc.c
|
||||
opm/core/pressure/fsh.c
|
||||
opm/core/pressure/fsh_common_impl.c
|
||||
opm/core/pressure/ifsh.c
|
||||
opm/core/pressure/mimetic/hybsys.c
|
||||
opm/core/pressure/mimetic/hybsys_global.c
|
||||
opm/core/pressure/mimetic/mimetic.c
|
||||
opm/core/pressure/msmfem/coarse_conn.c
|
||||
opm/core/pressure/msmfem/coarse_sys.c
|
||||
opm/core/pressure/msmfem/dfs.c
|
||||
opm/core/pressure/msmfem/hash_set.c
|
||||
opm/core/pressure/msmfem/ifsh_ms.c
|
||||
opm/core/pressure/msmfem/partition.c
|
||||
opm/core/pressure/tpfa/cfs_tpfa.c
|
||||
opm/core/pressure/tpfa/cfs_tpfa_residual.c
|
||||
opm/core/pressure/tpfa/compr_bc.c
|
||||
opm/core/pressure/tpfa/compr_quant.c
|
||||
opm/core/pressure/tpfa/compr_quant_general.c
|
||||
opm/core/pressure/tpfa/compr_source.c
|
||||
opm/core/pressure/tpfa/ifs_tpfa.c
|
||||
opm/core/pressure/tpfa/TransTpfa.cpp
|
||||
opm/core/pressure/tpfa/trans_tpfa.c
|
||||
opm/core/pressure/legacy_well.c
|
||||
opm/core/props/BlackoilPropertiesBasic.cpp
|
||||
opm/core/props/BlackoilPropertiesFromDeck.cpp
|
||||
opm/core/props/IncompPropertiesBasic.cpp
|
||||
opm/core/props/IncompPropertiesFromDeck.cpp
|
||||
opm/core/props/IncompPropertiesSinglePhase.cpp
|
||||
opm/core/props/pvt/BlackoilPvtProperties.cpp
|
||||
opm/core/props/pvt/PvtPropertiesBasic.cpp
|
||||
opm/core/props/pvt/PvtPropertiesIncompFromDeck.cpp
|
||||
opm/core/props/pvt/PvtDead.cpp
|
||||
opm/core/props/pvt/PvtDeadSpline.cpp
|
||||
opm/core/props/pvt/PvtInterface.cpp
|
||||
opm/core/props/pvt/PvtLiveGas.cpp
|
||||
opm/core/props/pvt/PvtLiveOil.cpp
|
||||
opm/core/props/rock/RockBasic.cpp
|
||||
opm/core/props/rock/RockCompressibility.cpp
|
||||
opm/core/props/rock/RockFromDeck.cpp
|
||||
opm/core/props/satfunc/SaturationPropsBasic.cpp
|
||||
opm/core/props/satfunc/SaturationPropsFromDeck.cpp
|
||||
opm/core/simulator/AdaptiveSimulatorTimer.cpp
|
||||
opm/core/simulator/BlackoilState.cpp
|
||||
opm/core/simulator/TimeStepControl.cpp
|
||||
opm/core/simulator/SimulatorCompressibleTwophase.cpp
|
||||
opm/core/simulator/SimulatorIncompTwophase.cpp
|
||||
opm/core/simulator/SimulatorOutput.cpp
|
||||
opm/core/simulator/SimulatorReport.cpp
|
||||
opm/core/simulator/SimulatorState.cpp
|
||||
opm/core/simulator/SimulatorTimer.cpp
|
||||
opm/core/flowdiagnostics/AnisotropicEikonal.cpp
|
||||
opm/core/flowdiagnostics/DGBasis.cpp
|
||||
opm/core/flowdiagnostics/FlowDiagnostics.cpp
|
||||
opm/core/flowdiagnostics/TofReorder.cpp
|
||||
opm/core/flowdiagnostics/TofDiscGalReorder.cpp
|
||||
opm/core/transport/TransportSolverTwophaseInterface.cpp
|
||||
opm/core/transport/implicit/TransportSolverTwophaseImplicit.cpp
|
||||
opm/core/transport/implicit/transport_source.c
|
||||
opm/core/transport/minimal/spu_explicit.c
|
||||
opm/core/transport/minimal/spu_implicit.c
|
||||
opm/core/transport/reorder/TransportSolverCompressibleTwophaseReorder.cpp
|
||||
opm/core/transport/reorder/ReorderSolverInterface.cpp
|
||||
opm/core/transport/reorder/TransportSolverTwophaseReorder.cpp
|
||||
opm/core/transport/reorder/reordersequence.cpp
|
||||
opm/core/transport/reorder/tarjan.c
|
||||
opm/core/utility/compressedToCartesian.cpp
|
||||
opm/core/utility/Event.cpp
|
||||
opm/core/utility/MonotCubicInterpolator.cpp
|
||||
opm/core/utility/StopWatch.cpp
|
||||
opm/core/utility/VelocityInterpolation.cpp
|
||||
opm/core/utility/WachspressCoord.cpp
|
||||
opm/core/utility/miscUtilities.cpp
|
||||
opm/core/utility/opm_memcmp_double.c
|
||||
opm/core/utility/miscUtilitiesBlackoil.cpp
|
||||
opm/core/utility/NullStream.cpp
|
||||
opm/core/utility/parameters/Parameter.cpp
|
||||
opm/core/utility/parameters/ParameterGroup.cpp
|
||||
opm/core/utility/parameters/ParameterTools.cpp
|
||||
opm/core/utility/parameters/ParameterXML.cpp
|
||||
opm/core/utility/parameters/tinyxml/tinystr.cpp
|
||||
opm/core/utility/parameters/tinyxml/tinyxml.cpp
|
||||
opm/core/utility/parameters/tinyxml/tinyxmlerror.cpp
|
||||
opm/core/utility/parameters/tinyxml/tinyxmlparser.cpp
|
||||
opm/core/utility/parameters/tinyxml/xmltest.cpp
|
||||
opm/core/version.c
|
||||
opm/core/wells/InjectionSpecification.cpp
|
||||
opm/core/wells/ProductionSpecification.cpp
|
||||
opm/core/wells/WellCollection.cpp
|
||||
opm/core/wells/WellsGroup.cpp
|
||||
opm/core/wells/WellsManager.cpp
|
||||
opm/core/wells/wells.c
|
||||
opm/core/wells/well_controls.c
|
||||
opm/core/flowdiagnostics/AnisotropicEikonal.cpp
|
||||
opm/core/flowdiagnostics/DGBasis.cpp
|
||||
opm/core/flowdiagnostics/FlowDiagnostics.cpp
|
||||
opm/core/flowdiagnostics/TofDiscGalReorder.cpp
|
||||
opm/core/flowdiagnostics/TofReorder.cpp
|
||||
opm/core/linalg/LinearSolverFactory.cpp
|
||||
opm/core/linalg/LinearSolverInterface.cpp
|
||||
opm/core/linalg/LinearSolverIstl.cpp
|
||||
opm/core/linalg/LinearSolverPetsc.cpp
|
||||
opm/core/linalg/LinearSolverUmfpack.cpp
|
||||
opm/core/linalg/call_umfpack.c
|
||||
opm/core/linalg/sparse_sys.c
|
||||
opm/core/pressure/CompressibleTpfa.cpp
|
||||
opm/core/pressure/FlowBCManager.cpp
|
||||
opm/core/pressure/IncompTpfa.cpp
|
||||
opm/core/pressure/IncompTpfaSinglePhase.cpp
|
||||
opm/core/pressure/flow_bc.c
|
||||
opm/core/pressure/mimetic/mimetic.c
|
||||
opm/core/pressure/msmfem/dfs.c
|
||||
opm/core/pressure/msmfem/partition.c
|
||||
opm/core/pressure/tpfa/cfs_tpfa_residual.c
|
||||
opm/core/pressure/tpfa/ifs_tpfa.c
|
||||
opm/core/props/BlackoilPropertiesBasic.cpp
|
||||
opm/core/props/BlackoilPropertiesFromDeck.cpp
|
||||
opm/core/props/IncompPropertiesBasic.cpp
|
||||
opm/core/props/IncompPropertiesFromDeck.cpp
|
||||
opm/core/props/IncompPropertiesSinglePhase.cpp
|
||||
opm/core/props/pvt/PvtPropertiesBasic.cpp
|
||||
opm/core/props/pvt/PvtPropertiesIncompFromDeck.cpp
|
||||
opm/core/props/rock/RockBasic.cpp
|
||||
opm/core/props/rock/RockCompressibility.cpp
|
||||
opm/core/props/rock/RockFromDeck.cpp
|
||||
opm/core/props/satfunc/RelpermDiagnostics.cpp
|
||||
opm/core/props/satfunc/SaturationPropsBasic.cpp
|
||||
opm/core/props/satfunc/SaturationPropsFromDeck.cpp
|
||||
opm/core/simulator/BlackoilState.cpp
|
||||
opm/core/simulator/TwophaseState.cpp
|
||||
opm/core/simulator/SimulatorReport.cpp
|
||||
opm/core/transport/TransportSolverTwophaseInterface.cpp
|
||||
opm/core/transport/reorder/ReorderSolverInterface.cpp
|
||||
opm/core/transport/reorder/TransportSolverCompressibleTwophaseReorder.cpp
|
||||
opm/core/transport/reorder/TransportSolverTwophaseReorder.cpp
|
||||
opm/core/transport/reorder/reordersequence.cpp
|
||||
opm/core/transport/reorder/tarjan.c
|
||||
opm/core/utility/miscUtilities.cpp
|
||||
opm/core/utility/miscUtilitiesBlackoil.cpp
|
||||
opm/core/wells/InjectionSpecification.cpp
|
||||
opm/core/wells/ProductionSpecification.cpp
|
||||
opm/core/wells/WellCollection.cpp
|
||||
opm/core/wells/WellsGroup.cpp
|
||||
opm/core/wells/WellsManager.cpp
|
||||
opm/core/wells/well_controls.c
|
||||
opm/core/wells/wells.c
|
||||
)
|
||||
|
||||
# originally generated with the command:
|
||||
# find tests -name '*.cpp' -a ! -wholename '*/not-unit/*' -printf '\t%p\n' | sort
|
||||
list (APPEND TEST_SOURCE_FILES
|
||||
tests/test_writenumwells.cpp
|
||||
tests/test_readWriteWellStateData.cpp
|
||||
tests/test_EclipseWriter.cpp
|
||||
tests/test_EclipseWriteRFTHandler.cpp
|
||||
tests/test_compressedpropertyaccess.cpp
|
||||
tests/test_dgbasis.cpp
|
||||
tests/test_cartgrid.cpp
|
||||
tests/test_ug.cpp
|
||||
tests/test_cubic.cpp
|
||||
tests/test_event.cpp
|
||||
tests/test_flowdiagnostics.cpp
|
||||
tests/test_nonuniformtablelinear.cpp
|
||||
tests/test_parallelistlinformation.cpp
|
||||
tests/test_sparsevector.cpp
|
||||
tests/test_sparsetable.cpp
|
||||
#tests/test_thresholdpressure.cpp
|
||||
tests/test_velocityinterpolation.cpp
|
||||
tests/test_quadratures.cpp
|
||||
tests/test_uniformtablelinear.cpp
|
||||
tests/test_wells.cpp
|
||||
tests/test_wachspresscoord.cpp
|
||||
tests/test_column_extract.cpp
|
||||
tests/test_geom2d.cpp
|
||||
tests/test_linearsolver.cpp
|
||||
tests/test_parallel_linearsolver.cpp
|
||||
tests/test_param.cpp
|
||||
tests/test_blackoilfluid.cpp
|
||||
tests/test_satfunc.cpp
|
||||
tests/test_shadow.cpp
|
||||
tests/test_equil.cpp
|
||||
tests/test_regionmapping.cpp
|
||||
tests/test_units.cpp
|
||||
tests/test_blackoilstate.cpp
|
||||
tests/test_parser.cpp
|
||||
tests/test_wellsmanager.cpp
|
||||
tests/test_wellcontrols.cpp
|
||||
tests/test_wellsgroup.cpp
|
||||
tests/test_wellcollection.cpp
|
||||
tests/test_timer.cpp
|
||||
tests/test_minpvprocessor.cpp
|
||||
tests/test_pinchprocessor.cpp
|
||||
tests/test_gridutilities.cpp
|
||||
tests/test_anisotropiceikonal.cpp
|
||||
tests/test_stoppedwells.cpp
|
||||
tests/test_relpermdiagnostics.cpp
|
||||
tests/test_norne_pvt.cpp
|
||||
)
|
||||
|
||||
# originally generated with the command:
|
||||
# find tests -name '*.xml' -a ! -wholename '*/not-unit/*' -printf '\t%p\n' | sort
|
||||
# find tests -name '*.param' -a ! -wholename '*/not-unit/*' -printf '\t%p\n' | sort
|
||||
list (APPEND TEST_DATA_FILES
|
||||
tests/extratestdata.xml
|
||||
tests/testdata.xml
|
||||
tests/liveoil.DATA
|
||||
tests/capillary.DATA
|
||||
tests/capillary_overlap.DATA
|
||||
tests/compressed_gridproperty.data
|
||||
tests/capillarySwatinit.DATA
|
||||
tests/deadfluids.DATA
|
||||
tests/equil_livegas.DATA
|
||||
tests/equil_liveoil.DATA
|
||||
|
@ -224,32 +128,23 @@ list (APPEND TEST_DATA_FILES
|
|||
tests/satfuncEPS_D.DATA
|
||||
tests/testBlackoilState1.DATA
|
||||
tests/testBlackoilState2.DATA
|
||||
tests/testBlackoilState3.DATA
|
||||
tests/testPinch1.DATA
|
||||
tests/wells_manager_data.data
|
||||
tests/wells_manager_data_expanded.data
|
||||
tests/wells_manager_data_wellSTOP.data
|
||||
tests/wells_group.data
|
||||
tests/TESTTIMER.DATA
|
||||
tests/CORNERPOINT_ACTNUM.DATA
|
||||
tests/wells_stopped.data
|
||||
)
|
||||
tests/relpermDiagnostics.DATA
|
||||
tests/norne_pvt.data
|
||||
)
|
||||
|
||||
# originally generated with the command:
|
||||
# find tutorials examples -name '*.c*' -printf '\t%p\n' | sort
|
||||
# find examples -name '*.c*' -printf '\t%p\n' | sort
|
||||
list (APPEND EXAMPLE_SOURCE_FILES
|
||||
examples/compute_eikonal_from_files.cpp
|
||||
examples/compute_initial_state.cpp
|
||||
examples/compute_tof.cpp
|
||||
examples/compute_tof_from_files.cpp
|
||||
examples/mirror_grid.cpp
|
||||
examples/sim_2p_comp_reorder.cpp
|
||||
examples/sim_2p_incomp.cpp
|
||||
examples/wells_example.cpp
|
||||
tutorials/tutorial1.cpp
|
||||
tutorials/tutorial2.cpp
|
||||
tutorials/tutorial3.cpp
|
||||
tutorials/tutorial4.cpp
|
||||
examples/diagnose_relperm.cpp
|
||||
)
|
||||
|
||||
# originally generated with the command:
|
||||
|
@ -266,199 +161,90 @@ list (APPEND ATTIC_FILES
|
|||
# programs listed here will not only be compiled, but also marked for
|
||||
# installation
|
||||
list (APPEND PROGRAM_SOURCE_FILES
|
||||
examples/mirror_grid.cpp
|
||||
examples/sim_2p_comp_reorder.cpp
|
||||
examples/sim_2p_incomp.cpp
|
||||
)
|
||||
|
||||
# originally generated with the command:
|
||||
# find opm -name '*.h*' -a ! -name '*-pch.hpp' -printf '\t%p\n' | sort
|
||||
list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/core/doxygen_main.hpp
|
||||
opm/core/grid.h
|
||||
opm/core/grid/CellQuadrature.hpp
|
||||
opm/core/grid/ColumnExtract.hpp
|
||||
opm/core/grid/FaceQuadrature.hpp
|
||||
opm/core/grid/GridHelpers.hpp
|
||||
opm/core/grid/GridManager.hpp
|
||||
opm/core/grid/GridUtilities.hpp
|
||||
opm/core/grid/MinpvProcessor.hpp
|
||||
opm/core/grid/PinchProcessor.hpp
|
||||
opm/core/grid/cart_grid.h
|
||||
opm/core/grid/cornerpoint_grid.h
|
||||
opm/core/grid/cpgpreprocess/facetopology.h
|
||||
opm/core/grid/cpgpreprocess/geometry.h
|
||||
opm/core/grid/cpgpreprocess/preprocess.h
|
||||
opm/core/grid/cpgpreprocess/uniquepoints.h
|
||||
opm/core/io/eclipse/CornerpointChopper.hpp
|
||||
opm/core/io/eclipse/EclipseIOUtil.hpp
|
||||
opm/core/io/eclipse/EclipseGridInspector.hpp
|
||||
opm/core/io/eclipse/EclipseUnits.hpp
|
||||
opm/core/io/eclipse/EclipseWriter.hpp
|
||||
opm/core/io/eclipse/EclipseReader.hpp
|
||||
opm/core/io/eclipse/EclipseWriteRFTHandler.hpp
|
||||
opm/core/io/eclipse/writeECLData.hpp
|
||||
opm/core/io/OutputWriter.hpp
|
||||
opm/core/io/vag/vag.hpp
|
||||
opm/core/io/vtk/writeVtkData.hpp
|
||||
opm/core/linalg/LinearSolverFactory.hpp
|
||||
opm/core/linalg/LinearSolverInterface.hpp
|
||||
opm/core/linalg/LinearSolverIstl.hpp
|
||||
opm/core/linalg/LinearSolverUmfpack.hpp
|
||||
opm/core/linalg/LinearSolverPetsc.hpp
|
||||
opm/core/linalg/ParallelIstlInformation.hpp
|
||||
opm/core/linalg/blas_lapack.h
|
||||
opm/core/linalg/call_umfpack.h
|
||||
opm/core/linalg/sparse_sys.h
|
||||
opm/core/version.h
|
||||
opm/core/wells.h
|
||||
opm/core/well_controls.h
|
||||
opm/core/pressure/CompressibleTpfa.hpp
|
||||
opm/core/pressure/FlowBCManager.hpp
|
||||
opm/core/pressure/IncompTpfa.hpp
|
||||
opm/core/pressure/IncompTpfaSinglePhase.hpp
|
||||
opm/core/pressure/flow_bc.h
|
||||
opm/core/pressure/fsh.h
|
||||
opm/core/pressure/fsh_common_impl.h
|
||||
opm/core/pressure/legacy_well.h
|
||||
opm/core/pressure/mimetic/hybsys.h
|
||||
opm/core/pressure/mimetic/hybsys_global.h
|
||||
opm/core/pressure/mimetic/mimetic.h
|
||||
opm/core/pressure/msmfem/coarse_conn.h
|
||||
opm/core/pressure/msmfem/coarse_sys.h
|
||||
opm/core/pressure/msmfem/dfs.h
|
||||
opm/core/pressure/msmfem/hash_set.h
|
||||
opm/core/pressure/msmfem/ifsh_ms.h
|
||||
opm/core/pressure/msmfem/partition.h
|
||||
opm/core/pressure/tpfa/cfs_tpfa.h
|
||||
opm/core/pressure/tpfa/cfs_tpfa_residual.h
|
||||
opm/core/pressure/tpfa/compr_bc.h
|
||||
opm/core/pressure/tpfa/compr_quant.h
|
||||
opm/core/pressure/tpfa/compr_quant_general.h
|
||||
opm/core/pressure/tpfa/compr_source.h
|
||||
opm/core/pressure/tpfa/ifs_tpfa.h
|
||||
opm/core/pressure/tpfa/TransTpfa.hpp
|
||||
opm/core/pressure/tpfa/TransTpfa_impl.hpp
|
||||
opm/core/pressure/tpfa/trans_tpfa.h
|
||||
opm/core/props/BlackoilPhases.hpp
|
||||
opm/core/props/BlackoilPropertiesBasic.hpp
|
||||
opm/core/props/BlackoilPropertiesFromDeck.hpp
|
||||
opm/core/props/BlackoilPropertiesInterface.hpp
|
||||
opm/core/props/IncompPropertiesBasic.hpp
|
||||
opm/core/props/IncompPropertiesFromDeck.hpp
|
||||
opm/core/props/IncompPropertiesInterface.hpp
|
||||
opm/core/props/IncompPropertiesShadow.hpp
|
||||
opm/core/props/IncompPropertiesShadow_impl.hpp
|
||||
opm/core/props/IncompPropertiesSinglePhase.hpp
|
||||
opm/core/props/phaseUsageFromDeck.hpp
|
||||
opm/core/props/pvt/BlackoilPvtProperties.hpp
|
||||
opm/core/props/pvt/PvtPropertiesBasic.hpp
|
||||
opm/core/props/pvt/PvtPropertiesIncompFromDeck.hpp
|
||||
opm/core/props/pvt/PvtConstCompr.hpp
|
||||
opm/core/props/pvt/PvtDead.hpp
|
||||
opm/core/props/pvt/PvtDeadSpline.hpp
|
||||
opm/core/props/pvt/PvtInterface.hpp
|
||||
opm/core/props/pvt/PvtLiveGas.hpp
|
||||
opm/core/props/pvt/PvtLiveOil.hpp
|
||||
opm/core/props/pvt/ThermalWaterPvtWrapper.hpp
|
||||
opm/core/props/pvt/ThermalOilPvtWrapper.hpp
|
||||
opm/core/props/pvt/ThermalGasPvtWrapper.hpp
|
||||
opm/core/props/rock/RockBasic.hpp
|
||||
opm/core/props/rock/RockCompressibility.hpp
|
||||
opm/core/props/rock/RockFromDeck.hpp
|
||||
opm/core/props/satfunc/SaturationPropsBasic.hpp
|
||||
opm/core/props/satfunc/SaturationPropsFromDeck.hpp
|
||||
opm/core/props/satfunc/SaturationPropsInterface.hpp
|
||||
opm/core/simulator/AdaptiveSimulatorTimer.hpp
|
||||
opm/core/simulator/AdaptiveTimeStepping.hpp
|
||||
opm/core/simulator/AdaptiveTimeStepping_impl.hpp
|
||||
opm/core/simulator/BlackoilState.hpp
|
||||
opm/core/simulator/BlackoilStateToFluidState.hpp
|
||||
opm/core/simulator/EquilibrationHelpers.hpp
|
||||
opm/core/simulator/ExplicitArraysFluidState.hpp
|
||||
opm/core/simulator/ExplicitArraysSatDerivativesFluidState.hpp
|
||||
opm/core/simulator/TimeStepControl.hpp
|
||||
opm/core/simulator/SimulatorCompressibleTwophase.hpp
|
||||
opm/core/simulator/SimulatorIncompTwophase.hpp
|
||||
opm/core/simulator/SimulatorOutput.hpp
|
||||
opm/core/simulator/SimulatorReport.hpp
|
||||
opm/core/simulator/SimulatorState.hpp
|
||||
opm/core/simulator/SimulatorTimerInterface.hpp
|
||||
opm/core/simulator/SimulatorTimer.hpp
|
||||
opm/core/simulator/TimeStepControlInterface.hpp
|
||||
opm/core/simulator/TwophaseState.hpp
|
||||
opm/core/simulator/TwophaseState_impl.hpp
|
||||
opm/core/simulator/WellState.hpp
|
||||
opm/core/simulator/initState.hpp
|
||||
opm/core/simulator/initState_impl.hpp
|
||||
opm/core/simulator/initStateEquil.hpp
|
||||
opm/core/simulator/initStateEquil_impl.hpp
|
||||
opm/core/flowdiagnostics/AnisotropicEikonal.hpp
|
||||
opm/core/flowdiagnostics/DGBasis.hpp
|
||||
opm/core/flowdiagnostics/FlowDiagnostics.hpp
|
||||
opm/core/flowdiagnostics/TofReorder.hpp
|
||||
opm/core/flowdiagnostics/TofDiscGalReorder.hpp
|
||||
opm/core/transport/TransportSolverTwophaseInterface.hpp
|
||||
opm/core/transport/implicit/CSRMatrixBlockAssembler.hpp
|
||||
opm/core/transport/implicit/CSRMatrixUmfpackSolver.hpp
|
||||
opm/core/transport/implicit/ImplicitAssembly.hpp
|
||||
opm/core/transport/implicit/ImplicitTransport.hpp
|
||||
opm/core/transport/implicit/JacobianSystem.hpp
|
||||
opm/core/transport/implicit/NormSupport.hpp
|
||||
opm/core/transport/implicit/SinglePointUpwindTwoPhase.hpp
|
||||
opm/core/transport/implicit/TransportSolverTwophaseImplicit.hpp
|
||||
opm/core/transport/implicit/SimpleFluid2pWrappingProps.hpp
|
||||
opm/core/transport/implicit/SimpleFluid2pWrappingProps_impl.hpp
|
||||
opm/core/transport/implicit/transport_source.h
|
||||
opm/core/transport/minimal/spu_explicit.h
|
||||
opm/core/transport/minimal/spu_implicit.h
|
||||
opm/core/transport/reorder/TransportSolverCompressibleTwophaseReorder.hpp
|
||||
opm/core/transport/reorder/ReorderSolverInterface.hpp
|
||||
opm/core/transport/reorder/TransportSolverTwophaseReorder.hpp
|
||||
opm/core/transport/reorder/reordersequence.h
|
||||
opm/core/transport/reorder/tarjan.h
|
||||
opm/core/utility/Average.hpp
|
||||
opm/core/utility/CompressedPropertyAccess.hpp
|
||||
opm/core/utility/compressedToCartesian.hpp
|
||||
opm/core/utility/DataMap.hpp
|
||||
opm/core/utility/Event.hpp
|
||||
opm/core/utility/Event_impl.hpp
|
||||
opm/core/utility/Factory.hpp
|
||||
opm/core/utility/MonotCubicInterpolator.hpp
|
||||
opm/core/utility/opm_memcmp_double.h
|
||||
opm/core/utility/NonuniformTableLinear.hpp
|
||||
opm/core/utility/NullStream.hpp
|
||||
opm/core/utility/RegionMapping.hpp
|
||||
opm/core/utility/RootFinders.hpp
|
||||
opm/core/utility/SparseTable.hpp
|
||||
opm/core/utility/SparseVector.hpp
|
||||
opm/core/utility/StopWatch.hpp
|
||||
opm/core/utility/UniformTableLinear.hpp
|
||||
opm/core/utility/Units.hpp
|
||||
opm/core/utility/VelocityInterpolation.hpp
|
||||
opm/core/utility/WachspressCoord.hpp
|
||||
opm/core/utility/buildUniformMonotoneTable.hpp
|
||||
opm/core/utility/have_boost_redef.hpp
|
||||
opm/core/utility/linearInterpolation.hpp
|
||||
opm/core/utility/miscUtilities.hpp
|
||||
opm/core/utility/miscUtilities_impl.hpp
|
||||
opm/core/utility/miscUtilitiesBlackoil.hpp
|
||||
opm/core/utility/parameters/Parameter.hpp
|
||||
opm/core/utility/parameters/ParameterGroup.hpp
|
||||
opm/core/utility/parameters/ParameterGroup_impl.hpp
|
||||
opm/core/utility/parameters/ParameterMapItem.hpp
|
||||
opm/core/utility/parameters/ParameterRequirement.hpp
|
||||
opm/core/utility/parameters/ParameterStrings.hpp
|
||||
opm/core/utility/parameters/ParameterTools.hpp
|
||||
opm/core/utility/parameters/ParameterXML.hpp
|
||||
opm/core/utility/parameters/tinyxml/tinystr.h
|
||||
opm/core/utility/parameters/tinyxml/tinyxml.h
|
||||
opm/core/utility/share_obj.hpp
|
||||
opm/core/utility/thresholdPressures.hpp
|
||||
opm/core/wells/InjectionSpecification.hpp
|
||||
opm/core/wells/ProductionSpecification.hpp
|
||||
opm/core/wells/WellCollection.hpp
|
||||
opm/core/wells/WellsGroup.hpp
|
||||
opm/core/wells/WellsManager.hpp
|
||||
opm/core/wells/WellsManager_impl.hpp
|
||||
opm/core/doxygen_main.hpp
|
||||
opm/core/flowdiagnostics/AnisotropicEikonal.hpp
|
||||
opm/core/flowdiagnostics/DGBasis.hpp
|
||||
opm/core/flowdiagnostics/FlowDiagnostics.hpp
|
||||
opm/core/flowdiagnostics/TofDiscGalReorder.hpp
|
||||
opm/core/flowdiagnostics/TofReorder.hpp
|
||||
opm/core/linalg/LinearSolverFactory.hpp
|
||||
opm/core/linalg/LinearSolverInterface.hpp
|
||||
opm/core/linalg/LinearSolverIstl.hpp
|
||||
opm/core/linalg/LinearSolverPetsc.hpp
|
||||
opm/core/linalg/LinearSolverUmfpack.hpp
|
||||
opm/core/linalg/ParallelIstlInformation.hpp
|
||||
opm/core/linalg/call_umfpack.h
|
||||
opm/core/linalg/sparse_sys.h
|
||||
opm/core/pressure/CompressibleTpfa.hpp
|
||||
opm/core/pressure/FlowBCManager.hpp
|
||||
opm/core/pressure/IncompTpfa.hpp
|
||||
opm/core/pressure/flow_bc.h
|
||||
opm/core/pressure/legacy_well.h
|
||||
opm/core/pressure/mimetic/mimetic.h
|
||||
opm/core/pressure/msmfem/dfs.h
|
||||
opm/core/pressure/msmfem/partition.h
|
||||
opm/core/pressure/tpfa/cfs_tpfa_residual.h
|
||||
opm/core/pressure/tpfa/compr_quant_general.h
|
||||
opm/core/pressure/tpfa/compr_source.h
|
||||
opm/core/pressure/tpfa/ifs_tpfa.h
|
||||
opm/core/props/BlackoilPhases.hpp
|
||||
opm/core/props/BlackoilPropertiesBasic.hpp
|
||||
opm/core/props/BlackoilPropertiesFromDeck.hpp
|
||||
opm/core/props/BlackoilPropertiesInterface.hpp
|
||||
opm/core/props/IncompPropertiesBasic.hpp
|
||||
opm/core/props/IncompPropertiesFromDeck.hpp
|
||||
opm/core/props/IncompPropertiesInterface.hpp
|
||||
opm/core/props/IncompPropertiesShadow.hpp
|
||||
opm/core/props/IncompPropertiesShadow_impl.hpp
|
||||
opm/core/props/IncompPropertiesSinglePhase.hpp
|
||||
opm/core/props/phaseUsageFromDeck.hpp
|
||||
opm/core/props/pvt/PvtPropertiesBasic.hpp
|
||||
opm/core/props/pvt/PvtPropertiesIncompFromDeck.hpp
|
||||
opm/core/props/pvt/ThermalGasPvtWrapper.hpp
|
||||
opm/core/props/pvt/ThermalOilPvtWrapper.hpp
|
||||
opm/core/props/pvt/ThermalWaterPvtWrapper.hpp
|
||||
opm/core/props/rock/RockBasic.hpp
|
||||
opm/core/props/rock/RockCompressibility.hpp
|
||||
opm/core/props/rock/RockFromDeck.hpp
|
||||
opm/core/props/satfunc/RelpermDiagnostics.hpp
|
||||
opm/core/props/satfunc/SaturationPropsBasic.hpp
|
||||
opm/core/props/satfunc/SaturationPropsFromDeck.hpp
|
||||
opm/core/props/satfunc/SaturationPropsInterface.hpp
|
||||
opm/core/props/satfunc/RelpermDiagnostics_impl.hpp
|
||||
opm/core/simulator/BlackoilState.hpp
|
||||
opm/core/simulator/BlackoilStateToFluidState.hpp
|
||||
opm/core/simulator/EquilibrationHelpers.hpp
|
||||
opm/core/simulator/ExplicitArraysFluidState.hpp
|
||||
opm/core/simulator/ExplicitArraysSatDerivativesFluidState.hpp
|
||||
opm/core/simulator/SimulatorReport.hpp
|
||||
opm/core/simulator/TwophaseState.hpp
|
||||
opm/core/simulator/WellState.hpp
|
||||
opm/core/simulator/initState.hpp
|
||||
opm/core/simulator/initStateEquil.hpp
|
||||
opm/core/simulator/initStateEquil_impl.hpp
|
||||
opm/core/simulator/initState_impl.hpp
|
||||
opm/core/transport/TransportSolverTwophaseInterface.hpp
|
||||
opm/core/transport/reorder/ReorderSolverInterface.hpp
|
||||
opm/core/transport/reorder/TransportSolverCompressibleTwophaseReorder.hpp
|
||||
opm/core/transport/reorder/TransportSolverTwophaseReorder.hpp
|
||||
opm/core/transport/reorder/reordersequence.h
|
||||
opm/core/transport/reorder/tarjan.h
|
||||
opm/core/utility/initHydroCarbonState.hpp
|
||||
opm/core/utility/miscUtilities.hpp
|
||||
opm/core/utility/miscUtilitiesBlackoil.hpp
|
||||
opm/core/utility/miscUtilities_impl.hpp
|
||||
opm/core/well_controls.h
|
||||
opm/core/wells.h
|
||||
opm/core/wells/InjectionSpecification.hpp
|
||||
opm/core/wells/ProductionSpecification.hpp
|
||||
opm/core/wells/WellCollection.hpp
|
||||
opm/core/wells/WellsGroup.hpp
|
||||
opm/core/wells/WellsManager.hpp
|
||||
opm/core/wells/DynamicListEconLimited.hpp
|
||||
opm/core/wells/WellsManager_impl.hpp
|
||||
)
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
set(CTEST_PROJECT_NAME "${${project}_NAME}")
|
||||
set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
|
||||
set(CTEST_DROP_METHOD "http")
|
||||
set(CTEST_DROP_SITE "opm-project.org")
|
||||
set(CTEST_DROP_LOCATION "/CDash/submit.php?project=${${project}_NAME}")
|
||||
set(CTEST_DROP_SITE "cdash.opm-project.org")
|
||||
set(CTEST_DROP_LOCATION "/submit.php?project=${${project}_NAME}")
|
||||
set(CTEST_DROP_SITE_CDASH TRUE)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
Open Porous Media Core Library
|
||||
==============================
|
||||
# Open Porous Media Core Library
|
||||
|
||||
THIS MODULE IS DEPRECATED.
|
||||
The code is now integrated in the other modules.
|
||||
|
||||
These are release notes for opm-core.
|
||||
|
35
configure
vendored
35
configure
vendored
|
@ -1,35 +0,0 @@
|
|||
#!/bin/sh
|
||||
# this file is supposed to be located in the source directory
|
||||
src_dir=$(dirname $0)
|
||||
|
||||
# scan the arguments and set this if build macros could be specified
|
||||
mod_dir=
|
||||
for OPT in "$@"; do
|
||||
case "$OPT" in
|
||||
--with-opm-common=*)
|
||||
# remove everything before equal sign and assign the rest
|
||||
mod_dir=${OPT#*=}
|
||||
# tilde expansion; note that doing eval may have side effects
|
||||
mod_dir=$(eval echo $mod_dir)
|
||||
# absolute path
|
||||
[ -d "$mod_dir" ] && mod_dir=$(cd $mod_dir ; pwd)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# if it isn't specified, the look around in other known places
|
||||
conf_file=cmake/Scripts/configure
|
||||
if [ -z "$mod_dir" ]; then
|
||||
if [ -r "$src_dir/$conf_file" ]; then
|
||||
mod_dir="$src_dir"
|
||||
fi
|
||||
fi
|
||||
|
||||
# terminate with error message here if the module directory is not found
|
||||
if [ ! -r "$mod_dir/$conf_file" ]; then
|
||||
echo Build macros not located in \"$mod_dir\", use --with-opm-common= to specify! 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# forward to the corresponding script in the cmake/Scripts/ directory
|
||||
exec "$mod_dir/$conf_file" --src-dir="$src_dir" "$@"
|
27
debian/control
vendored
27
debian/control
vendored
|
@ -3,12 +3,14 @@ Priority: extra
|
|||
Maintainer: Arne Morten Kvarving <arne.morten.kvarving@sintef.no>
|
||||
Build-Depends: build-essential, debhelper (>= 9), libboost-filesystem-dev,
|
||||
libboost-system-dev, libboost-date-time-dev, libboost-test-dev,
|
||||
libsuperlu3-dev, gfortran, libsuitesparse-dev, pkg-config,
|
||||
libsuperlu3-dev (>= 3.0) | libsuperlu-dev (>= 4.3), gfortran, libsuitesparse-dev, pkg-config,
|
||||
libdune-common-dev, libdune-istl-dev, cmake, libtinyxml-dev, bc,
|
||||
libert.ecl-dev, git, zlib1g-dev, libtool, doxygen, libopm-parser-dev,
|
||||
libecl-dev, git, zlib1g-dev, libtool, doxygen, libopm-parser-dev,
|
||||
libopm-material-dev, texlive-latex-extra, texlive-latex-recommended,
|
||||
ghostscript, libboost-iostreams-dev, libopm-common-dev,
|
||||
libopm-material-dev
|
||||
libopm-material-dev, libopenmpi-dev, mpi-default-bin,
|
||||
libtrilinos-zoltan-dev, libopm-output-dev,
|
||||
libopm-grid-dev, libdune-geometry-dev, libdune-grid-dev
|
||||
Standards-Version: 3.9.2
|
||||
Section: libs
|
||||
Homepage: http://opm-project.org
|
||||
|
@ -53,25 +55,6 @@ Description: OPM core library
|
|||
* Utilities (input and output processing, unit conversion)
|
||||
* Wells (basic well handling)
|
||||
|
||||
Package: libopm-core1-bin
|
||||
Section: science
|
||||
Pre-Depends: ${misc:Pre-Depends}, multiarch-support
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Provides: libopm-core-bin
|
||||
Description: OPM core library -- applications
|
||||
The OPM core library is the core library within OPM and contains
|
||||
* Eclipse deck input and preprosessing
|
||||
* Fluid properties (basic PVT models and rock properties)
|
||||
* Grid handling (cornerpoint grids, unstructured grid interface)
|
||||
* Linear Algebra (interface to different linear solvers)
|
||||
* Pressure solvers (various discretization schemes, flow models)
|
||||
* Simulators (some basic examples of simulators based on sequential splitting schemes)
|
||||
* Transport solvers (various discretization schemes, flow models)
|
||||
* Utilities (input and output processing, unit conversion)
|
||||
* Wells (basic well handling)
|
||||
|
||||
Package: libopm-core1-doc
|
||||
Section: doc
|
||||
Architecture: all
|
||||
|
|
2
debian/docs
vendored
2
debian/docs
vendored
|
@ -1,2 +1,2 @@
|
|||
README
|
||||
README.md
|
||||
COPYING
|
||||
|
|
1
debian/libopm-core1-bin.install
vendored
1
debian/libopm-core1-bin.install
vendored
|
@ -1 +0,0 @@
|
|||
usr/bin/*
|
1
debian/libopm-core1-dev.install
vendored
1
debian/libopm-core1-dev.install
vendored
|
@ -3,3 +3,4 @@ usr/lib/*/lib*.so
|
|||
usr/lib/dunecontrol/*
|
||||
usr/lib/*/pkgconfig/*
|
||||
usr/share/cmake/*
|
||||
usr/share/opm/cmake/Modules/*
|
||||
|
|
3
debian/rules
vendored
3
debian/rules
vendored
|
@ -8,6 +8,7 @@
|
|||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
export OMPI_MCA_plm_rsh_agent=/bin/false
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
@ -20,7 +21,7 @@ override_dh_auto_build:
|
|||
|
||||
# consider using -DUSE_VERSIONED_DIR=ON if backporting
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSTRIP_DEBUGGING_SYMBOLS=ON -DBUILD_SHARED_LIBS=1 -DCMAKE_INSTALL_DOCDIR=share/doc/libopm-core1 -DWHOLE_PROG_OPTIM=ON -DUSE_RUNPATH=OFF
|
||||
dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_MPI=1 -DSTRIP_DEBUGGING_SYMBOLS=ON -DBUILD_SHARED_LIBS=1 -DCMAKE_INSTALL_DOCDIR=share/doc/libopm-core1 -DWHOLE_PROG_OPTIM=ON -DUSE_RUNPATH=OFF -DWITH_NATIVE=OFF
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_auto_install -- install-html
|
||||
|
|
14
dune.module
14
dune.module
|
@ -1,7 +1,13 @@
|
|||
####################################################################
|
||||
# Dune module information file: This file gets parsed by dunecontrol
|
||||
# and by the CMake build scripts.
|
||||
####################################################################
|
||||
|
||||
Module: opm-core
|
||||
Description: Open Porous Media Initiative Core Library
|
||||
# if you change this, make sure to also change opm/core/version.h
|
||||
Version: 1.1
|
||||
Label: 2013.10
|
||||
Version: 2018.04-pre
|
||||
Label: 2018.04-pre
|
||||
Maintainer: atgeirr@sintef.no
|
||||
Depends: dune-common (>= 2.2) dune-istl (>= 2.2) opm-common opm-parser opm-material
|
||||
MaintainerName: Atgeirr F. Rasmussen
|
||||
Url: http://opm-project.org
|
||||
Depends: dune-common (>= 2.4) dune-istl (>= 2.4) opm-common opm-parser opm-grid opm-output opm-material
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
|||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <iterator>
|
||||
|
@ -41,7 +42,7 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param)
|
||||
void warnIfUnusedParams(const Opm::ParameterGroup& param)
|
||||
{
|
||||
if (param.anyUnused()) {
|
||||
std::cout << "-------------------- Warning: unused parameters: --------------------\n";
|
||||
|
@ -60,7 +61,7 @@ try
|
|||
{
|
||||
using namespace Opm;
|
||||
|
||||
parameter::ParameterGroup param(argc, argv);
|
||||
ParameterGroup param(argc, argv);
|
||||
|
||||
// Read grid.
|
||||
GridManager grid_manager(param.get<std::string>("grid_filename"));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2014 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2017 IRIS
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
|
@ -18,7 +19,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
|
@ -27,18 +28,24 @@
|
|||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initStateEquil.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/utility/compressedToCartesian.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
|
||||
#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param)
|
||||
void warnIfUnusedParams(const Opm::ParameterGroup& param)
|
||||
{
|
||||
if (param.anyUnused()) {
|
||||
std::cout << "-------------------- Unused parameters: --------------------\n";
|
||||
|
@ -68,12 +75,36 @@ namespace
|
|||
std::copy(data.begin(), data.end(), std::ostream_iterator<double>(file, "\n"));
|
||||
}
|
||||
|
||||
|
||||
/// Convert saturations from a vector of individual phase saturation vectors
|
||||
/// to an interleaved format where all values for a given cell come before all
|
||||
/// values for the next cell, all in a single vector.
|
||||
template <class FluidSystem>
|
||||
void convertSats(std::vector<double>& sat_interleaved, const std::vector< std::vector<double> >& sat, const Opm::PhaseUsage& pu)
|
||||
{
|
||||
assert(sat.size() == 3);
|
||||
const auto nc = sat[0].size();
|
||||
const auto np = sat_interleaved.size() / nc;
|
||||
for (size_t c = 0; c < nc; ++c) {
|
||||
if ( FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
|
||||
const int opos = pu.phase_pos[Opm::BlackoilPhases::Liquid];
|
||||
const std::vector<double>& sat_p = sat[ FluidSystem::oilPhaseIdx];
|
||||
sat_interleaved[np*c + opos] = sat_p[c];
|
||||
}
|
||||
if ( FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
|
||||
const int wpos = pu.phase_pos[Opm::BlackoilPhases::Aqua];
|
||||
const std::vector<double>& sat_p = sat[ FluidSystem::waterPhaseIdx];
|
||||
sat_interleaved[np*c + wpos] = sat_p[c];
|
||||
}
|
||||
if ( FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
|
||||
const int gpos = pu.phase_pos[Opm::BlackoilPhases::Vapour];
|
||||
const std::vector<double>& sat_p = sat[ FluidSystem::gasPhaseIdx];
|
||||
sat_interleaved[np*c + gpos] = sat_p[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
|
||||
|
||||
// ----------------- Main program -----------------
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
|
@ -82,22 +113,53 @@ try
|
|||
using namespace Opm;
|
||||
|
||||
// Setup.
|
||||
parameter::ParameterGroup param(argc, argv);
|
||||
ParameterGroup param(argc, argv);
|
||||
std::cout << "--------------- Reading parameters ---------------" << std::endl;
|
||||
const std::string deck_filename = param.get<std::string>("deck_filename");
|
||||
Opm::ParseMode parseMode;
|
||||
Opm::ParserPtr parser(new Opm::Parser() );
|
||||
Opm::DeckConstPtr deck = parser->parseFile(deck_filename , parseMode);
|
||||
Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(deck, parseMode));
|
||||
Opm::ParseContext parseContext;
|
||||
Opm::Parser parser;
|
||||
const Opm::Deck& deck = parser.parseFile(deck_filename , parseContext);
|
||||
const Opm::EclipseState eclipseState(deck, parseContext);
|
||||
const double grav = param.getDefault("gravity", unit::gravity);
|
||||
GridManager gm(deck);
|
||||
GridManager gm(eclipseState.getInputGrid());
|
||||
const UnstructuredGrid& grid = *gm.c_grid();
|
||||
BlackoilPropertiesFromDeck props(deck, eclipseState, grid, param);
|
||||
warnIfUnusedParams(param);
|
||||
|
||||
// Create material law manager.
|
||||
std::vector<int> compressedToCartesianIdx
|
||||
= Opm::compressedToCartesian(grid.number_of_cells, grid.global_cell);
|
||||
|
||||
typedef FluidSystems::BlackOil<double> FluidSystem;
|
||||
|
||||
// Forward declaring the MaterialLawManager template.
|
||||
typedef Opm::ThreePhaseMaterialTraits<double,
|
||||
/*wettingPhaseIdx=*/FluidSystem::waterPhaseIdx,
|
||||
/*nonWettingPhaseIdx=*/FluidSystem::oilPhaseIdx,
|
||||
/*gasPhaseIdx=*/FluidSystem::gasPhaseIdx> MaterialTraits;
|
||||
typedef Opm::EclMaterialLawManager<MaterialTraits> MaterialLawManager;
|
||||
|
||||
MaterialLawManager materialLawManager = MaterialLawManager();
|
||||
materialLawManager.initFromDeck(deck, eclipseState, compressedToCartesianIdx);
|
||||
|
||||
// Initialisation.
|
||||
BlackoilState state;
|
||||
initStateEquil(grid, props, deck, eclipseState, grav, state);
|
||||
//initBlackoilSurfvolUsingRSorRV(UgGridHelpers::numCells(grid), props, state);
|
||||
BlackoilState state( UgGridHelpers::numCells(grid) , UgGridHelpers::numFaces(grid), 3);
|
||||
FluidSystem::initFromDeck(deck, eclipseState);
|
||||
PhaseUsage pu = phaseUsageFromDeck(deck);
|
||||
|
||||
typedef EQUIL::DeckDependent::InitialStateComputer<FluidSystem> ISC;
|
||||
|
||||
ISC isc(materialLawManager, eclipseState, grid, grav);
|
||||
|
||||
const bool oil = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
|
||||
const int oilpos = FluidSystem::oilPhaseIdx;
|
||||
const int waterpos = FluidSystem::waterPhaseIdx;
|
||||
const int ref_phase = oil ? oilpos : waterpos;
|
||||
|
||||
state.pressure() = isc.press()[ref_phase];
|
||||
convertSats<FluidSystem>(state.saturation(), isc.saturation(), pu);
|
||||
state.gasoilratio() = isc.rs();
|
||||
state.rv() = isc.rv();
|
||||
|
||||
// Output.
|
||||
const std::string output_dir = param.getDefault<std::string>("output_dir", "output");
|
||||
|
|
|
@ -1,349 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/utility/SparseTable.hpp>
|
||||
#include <opm/core/utility/StopWatch.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/props/IncompPropertiesSinglePhase.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
|
||||
#include <opm/core/pressure/IncompTpfaSinglePhase.hpp>
|
||||
#include <opm/core/flowdiagnostics/FlowDiagnostics.hpp>
|
||||
#include <opm/core/flowdiagnostics/TofReorder.hpp>
|
||||
#include <opm/core/flowdiagnostics/TofDiscGalReorder.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
const static double alq_invalid = -std::numeric_limits<double>::max();
|
||||
const static int vfp_invalid = -std::numeric_limits<int>::max();
|
||||
|
||||
void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param)
|
||||
{
|
||||
if (param.anyUnused()) {
|
||||
std::cout << "-------------------- Unused parameters: --------------------\n";
|
||||
param.displayUsage();
|
||||
std::cout << "----------------------------------------------------------------" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void buildTracerheadsFromWells(const Wells& wells,
|
||||
const bool trace_injectors,
|
||||
Opm::SparseTable<int>& tracerheads)
|
||||
{
|
||||
tracerheads.clear();
|
||||
const int num_wells = wells.number_of_wells;
|
||||
const WellType wanted_type = trace_injectors ? INJECTOR : PRODUCER;
|
||||
for (int w = 0; w < num_wells; ++w) {
|
||||
if (wells.type[w] != wanted_type) {
|
||||
continue;
|
||||
}
|
||||
tracerheads.appendRow(wells.well_cells + wells.well_connpos[w],
|
||||
wells.well_cells + wells.well_connpos[w + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void setBhpWells(Wells& wells)
|
||||
{
|
||||
const int num_wells = wells.number_of_wells;
|
||||
for (int w = 0; w < num_wells; ++w) {
|
||||
WellControls* ctrl = wells.ctrls[w];
|
||||
const double target = (wells.type[w] == INJECTOR) ? 200*Opm::unit::barsa : 100*Opm::unit::barsa;
|
||||
const double distr[3] = { 1.0, 0.0, 0.0 }; // Large enough irrespective of #phases.
|
||||
well_controls_add_new(BHP, target,
|
||||
alq_invalid, vfp_invalid,
|
||||
distr, ctrl);
|
||||
well_controls_set_current(ctrl, well_controls_get_num(ctrl) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void computeTransportSourceSinglePhase(const UnstructuredGrid& grid,
|
||||
const std::vector<double>& src,
|
||||
const std::vector<double>& faceflux,
|
||||
const double inflow_frac,
|
||||
const Wells* wells,
|
||||
const std::vector<double>& well_perfrates,
|
||||
std::vector<double>& transport_src)
|
||||
{
|
||||
using namespace Opm;
|
||||
int nc = grid.number_of_cells;
|
||||
transport_src.resize(nc);
|
||||
// Source term and boundary contributions.
|
||||
for (int c = 0; c < nc; ++c) {
|
||||
transport_src[c] = 0.0;
|
||||
transport_src[c] += src[c] > 0.0 ? inflow_frac*src[c] : src[c];
|
||||
for (int hf = grid.cell_facepos[c]; hf < grid.cell_facepos[c + 1]; ++hf) {
|
||||
int f = grid.cell_faces[hf];
|
||||
const int* f2c = &grid.face_cells[2*f];
|
||||
double bdy_influx = 0.0;
|
||||
if (f2c[0] == c && f2c[1] == -1) {
|
||||
bdy_influx = -faceflux[f];
|
||||
} else if (f2c[0] == -1 && f2c[1] == c) {
|
||||
bdy_influx = faceflux[f];
|
||||
}
|
||||
if (bdy_influx != 0.0) {
|
||||
transport_src[c] += bdy_influx > 0.0 ? inflow_frac*bdy_influx : bdy_influx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Well contributions.
|
||||
if (wells) {
|
||||
const int nw = wells->number_of_wells;
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
for (int perf = wells->well_connpos[w]; perf < wells->well_connpos[w + 1]; ++perf) {
|
||||
const int perf_cell = wells->well_cells[perf];
|
||||
double perf_rate = well_perfrates[perf];
|
||||
if (perf_rate > 0.0) {
|
||||
// perf_rate is a total inflow rate, we want a water rate.
|
||||
if (wells->type[w] != INJECTOR) {
|
||||
std::cout << "**** Warning: crossflow in well "
|
||||
<< w << " perf " << perf - wells->well_connpos[w]
|
||||
<< " ignored. Rate was "
|
||||
<< perf_rate/Opm::unit::day << " m^3/day." << std::endl;
|
||||
perf_rate = 0.0;
|
||||
} else {
|
||||
perf_rate *= inflow_frac;
|
||||
}
|
||||
}
|
||||
transport_src[perf_cell] += perf_rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // anon namespace
|
||||
|
||||
|
||||
|
||||
// ----------------- Main program -----------------
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
using namespace Opm;
|
||||
|
||||
std::cout << "\n================ Test program for incompressible tof computations ===============\n\n";
|
||||
parameter::ParameterGroup param(argc, argv);
|
||||
std::cout << "--------------- Reading parameters ---------------" << std::endl;
|
||||
|
||||
// Read the deck.
|
||||
std::string deck_filename = param.get<std::string>("deck_filename");
|
||||
Parser parser;
|
||||
ParseMode parseMode;
|
||||
DeckConstPtr deck = parser.parseFile(deck_filename , parseMode);
|
||||
EclipseStateConstPtr eclipseState = std::make_shared<EclipseState>(deck , parseMode);
|
||||
|
||||
// Grid init
|
||||
GridManager grid_manager(deck);
|
||||
const UnstructuredGrid& grid = *grid_manager.c_grid();
|
||||
// Rock and fluid init
|
||||
IncompPropertiesSinglePhase props(deck, eclipseState, grid);
|
||||
// Wells init.
|
||||
WellsManager wells_manager(eclipseState , 0, grid, props.permeability());
|
||||
|
||||
std::shared_ptr<Wells> my_wells(clone_wells(wells_manager.c_wells()), destroy_wells);
|
||||
setBhpWells(*my_wells);
|
||||
const Wells& wells = *my_wells;
|
||||
|
||||
// Pore volume.
|
||||
std::vector<double> porevol;
|
||||
computePorevolume(grid, props.porosity(), porevol);
|
||||
int num_cells = grid.number_of_cells;
|
||||
|
||||
// Linear solver.
|
||||
LinearSolverFactory linsolver(param);
|
||||
|
||||
// Pressure solver.
|
||||
Opm::IncompTpfaSinglePhase psolver(grid, props, linsolver, wells);
|
||||
|
||||
// Choice of tof solver.
|
||||
bool use_dg = param.getDefault("use_dg", false);
|
||||
bool use_multidim_upwind = false;
|
||||
// Need to initialize dg solver here, since it uses parameters now.
|
||||
std::unique_ptr<Opm::TofDiscGalReorder> dg_solver;
|
||||
if (use_dg) {
|
||||
dg_solver.reset(new Opm::TofDiscGalReorder(grid, param));
|
||||
} else {
|
||||
use_multidim_upwind = param.getDefault("use_multidim_upwind", false);
|
||||
}
|
||||
bool compute_tracer = param.getDefault("compute_tracer", false);
|
||||
|
||||
// Write parameters used for later reference.
|
||||
bool output = param.getDefault("output", true);
|
||||
std::ofstream epoch_os;
|
||||
std::string output_dir;
|
||||
if (output) {
|
||||
output_dir =
|
||||
param.getDefault("output_dir", std::string("output"));
|
||||
boost::filesystem::path fpath(output_dir);
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
param.writeParam(output_dir + "/simulation.param");
|
||||
}
|
||||
|
||||
// Check if we have misspelled anything
|
||||
warnIfUnusedParams(param);
|
||||
|
||||
// Main solvers.
|
||||
Opm::time::StopWatch pressure_timer;
|
||||
double ptime = 0.0;
|
||||
Opm::time::StopWatch transport_timer;
|
||||
double ttime = 0.0;
|
||||
Opm::time::StopWatch total_timer;
|
||||
total_timer.start();
|
||||
std::cout << "\n\n================ Starting main solvers ===============" << std::endl;
|
||||
|
||||
// Solve pressure.
|
||||
std::vector<double> press;
|
||||
std::vector<double> flux;
|
||||
std::vector<double> bhp;
|
||||
std::vector<double> wellrates;
|
||||
pressure_timer.start();
|
||||
psolver.solve(press, flux, bhp, wellrates);
|
||||
pressure_timer.stop();
|
||||
double pt = pressure_timer.secsSinceStart();
|
||||
std::cout << "Pressure solver took: " << pt << " seconds." << std::endl;
|
||||
ptime += pt;
|
||||
|
||||
// Process transport sources (to include bdy terms and well flows).
|
||||
std::vector<double> src(num_cells, 0.0);
|
||||
std::vector<double> transport_src;
|
||||
computeTransportSourceSinglePhase(grid, src, flux, 1.0,
|
||||
&wells, wellrates, transport_src);
|
||||
|
||||
std::string tof_filenames[2] = { output_dir + "/ftof.txt", output_dir + "/btof.txt" };
|
||||
std::string tracer_filenames[2] = { output_dir + "/ftracer.txt", output_dir + "/btracer.txt" };
|
||||
std::vector<double> tracers[2];
|
||||
|
||||
// We compute tof twice, direction == 0 is from injectors, 1 is from producers.
|
||||
for (int direction = 0; direction < 2; ++direction) {
|
||||
// Turn direction of flux and flip source terms if starting from producers.
|
||||
if (direction == 1) {
|
||||
for (auto it = flux.begin(); it != flux.end(); ++it) {
|
||||
(*it) = -(*it);
|
||||
}
|
||||
for (auto it = transport_src.begin(); it != transport_src.end(); ++it) {
|
||||
(*it) = -(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// Solve time-of-flight.
|
||||
transport_timer.start();
|
||||
std::vector<double> tof;
|
||||
std::vector<double> tracer;
|
||||
Opm::SparseTable<int> tracerheads;
|
||||
if (compute_tracer) {
|
||||
buildTracerheadsFromWells(wells, direction == 0, tracerheads);
|
||||
}
|
||||
if (use_dg) {
|
||||
if (compute_tracer) {
|
||||
dg_solver->solveTofTracer(flux.data(), porevol.data(), transport_src.data(), tracerheads, tof, tracer);
|
||||
} else {
|
||||
dg_solver->solveTof(flux.data(), porevol.data(), transport_src.data(), tof);
|
||||
}
|
||||
} else {
|
||||
Opm::TofReorder tofsolver(grid, use_multidim_upwind);
|
||||
if (compute_tracer) {
|
||||
tofsolver.solveTofTracer(flux.data(), porevol.data(), transport_src.data(), tracerheads, tof, tracer);
|
||||
} else {
|
||||
tofsolver.solveTof(flux.data(), porevol.data(), transport_src.data(), tof);
|
||||
}
|
||||
}
|
||||
transport_timer.stop();
|
||||
double tt = transport_timer.secsSinceStart();
|
||||
if (direction == 0) {
|
||||
std::cout << "Forward ";
|
||||
} else {
|
||||
std::cout << "Backward ";
|
||||
}
|
||||
std::cout << "time-of-flight/tracer solve took: " << tt << " seconds." << std::endl;
|
||||
ttime += tt;
|
||||
|
||||
// Output.
|
||||
if (output) {
|
||||
std::string tof_filename = tof_filenames[direction];
|
||||
std::ofstream tof_stream(tof_filename.c_str());
|
||||
tof_stream.precision(16);
|
||||
std::copy(tof.begin(), tof.end(), std::ostream_iterator<double>(tof_stream, "\n"));
|
||||
if (compute_tracer) {
|
||||
std::string tracer_filename = tracer_filenames[direction];
|
||||
std::ofstream tracer_stream(tracer_filename.c_str());
|
||||
tracer_stream.precision(16);
|
||||
const int nt = tracer.size()/num_cells;
|
||||
for (int i = 0; i < nt*num_cells; ++i) {
|
||||
tracer_stream << tracer[i] << (((i + 1) % nt == 0) ? '\n' : ' ');
|
||||
}
|
||||
tracers[direction] = tracer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have tracers, compute well pairs.
|
||||
if (compute_tracer) {
|
||||
auto wp = Opm::computeWellPairs(wells, porevol, tracers[0], tracers[1]);
|
||||
std::string wellpair_filename = output_dir + "/wellpairs.txt";
|
||||
std::ofstream wellpair_stream(wellpair_filename.c_str());
|
||||
const int nwp = wp.size();
|
||||
for (int ii = 0; ii < nwp; ++ii) {
|
||||
wellpair_stream << std::get<0>(wp[ii]) << ' ' << std::get<1>(wp[ii]) << ' ' << std::get<2>(wp[ii]) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
total_timer.stop();
|
||||
|
||||
std::cout << "\n\n================ End of simulation ===============\n"
|
||||
<< "Total time taken: " << total_timer.secsSinceStart()
|
||||
<< "\n Pressure time: " << ptime
|
||||
<< "\n Tof/tracer time: " << ttime << std::endl;
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
|
@ -18,12 +18,10 @@
|
|||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
|
@ -42,7 +40,6 @@
|
|||
#include <opm/core/simulator/TwophaseState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/pressure/IncompTpfa.hpp>
|
||||
#include <opm/core/flowdiagnostics/TofReorder.hpp>
|
||||
#include <opm/core/flowdiagnostics/TofDiscGalReorder.hpp>
|
||||
|
||||
|
@ -54,11 +51,11 @@
|
|||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <iterator>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param)
|
||||
void warnIfUnusedParams(const Opm::ParameterGroup& param)
|
||||
{
|
||||
if (param.anyUnused()) {
|
||||
std::cout << "-------------------- Warning: unused parameters: --------------------\n";
|
||||
|
@ -77,7 +74,7 @@ try
|
|||
{
|
||||
using namespace Opm;
|
||||
|
||||
parameter::ParameterGroup param(argc, argv);
|
||||
ParameterGroup param(argc, argv);
|
||||
|
||||
// Read grid.
|
||||
GridManager grid_manager(param.get<std::string>("grid_filename"));
|
||||
|
|
105
examples/diagnose_relperm.cpp
Normal file
105
examples/diagnose_relperm.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2015 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/props/satfunc/RelpermDiagnostics.hpp>
|
||||
#include <opm/core/props/satfunc/RelpermDiagnostics_impl.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/utility/StopWatch.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
#include <opm/common/OpmLog/EclipsePRTLog.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <iterator>
|
||||
|
||||
|
||||
void usage() {
|
||||
std::cout << std::endl <<
|
||||
"Usage: diagnose_relperm <eclipseFile>" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
// ----------------- Main program -----------------
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
using namespace Opm;
|
||||
if (argc <= 1) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
const char* eclipseFilename = argv[1];
|
||||
Parser parser;
|
||||
Opm::ParseContext parseContext({{ ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE },
|
||||
{ ParseContext::PARSE_UNKNOWN_KEYWORD, InputError::IGNORE},
|
||||
{ ParseContext::PARSE_RANDOM_TEXT, InputError::IGNORE},
|
||||
{ ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER, InputError::IGNORE},
|
||||
{ ParseContext::UNSUPPORTED_COMPORD_TYPE, InputError::IGNORE},
|
||||
{ ParseContext::UNSUPPORTED_INITIAL_THPRES, InputError::IGNORE},
|
||||
{ ParseContext::INTERNAL_ERROR_UNINITIALIZED_THPRES, InputError::IGNORE}
|
||||
});
|
||||
Opm::Deck deck = parser.parseFile(eclipseFilename, parseContext);
|
||||
Opm::EclipseState eclState( deck, parseContext );
|
||||
|
||||
GridManager gm(eclState.getInputGrid());
|
||||
const UnstructuredGrid& grid = *gm.c_grid();
|
||||
using boost::filesystem::path;
|
||||
path fpath(eclipseFilename);
|
||||
std::string baseName;
|
||||
if (boost::to_upper_copy(path(fpath.extension()).string())== ".DATA") {
|
||||
baseName = path(fpath.stem()).string();
|
||||
} else {
|
||||
baseName = path(fpath.filename()).string();
|
||||
}
|
||||
|
||||
std::string logFile = baseName + ".SATFUNCLOG";
|
||||
std::shared_ptr<EclipsePRTLog> prtLog = std::make_shared<EclipsePRTLog>(logFile, Log::DefaultMessageTypes);
|
||||
OpmLog::addBackend( "ECLIPSEPRTLOG" , prtLog );
|
||||
prtLog->setMessageFormatter(std::make_shared<SimpleMessageFormatter>(true, false));
|
||||
std::shared_ptr<StreamLog> streamLog = std::make_shared<EclipsePRTLog>(std::cout, Log::DefaultMessageTypes);
|
||||
OpmLog::addBackend( "STREAMLOG" , streamLog );
|
||||
streamLog->setMessageLimiter(std::make_shared<MessageLimiter>(10));
|
||||
streamLog->setMessageFormatter(std::make_shared<SimpleMessageFormatter>(true, true));
|
||||
RelpermDiagnostics diagnostic;
|
||||
diagnostic.diagnosis(eclState, deck, grid);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
|
@ -1,402 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 Statoil ASA.
|
||||
Copyright 2014 Andreas Lauser.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
/**
|
||||
* @file mirror_grid.cpp
|
||||
* @brief Mirror grid taken from grdecl file
|
||||
*
|
||||
* The input grid is mirrored in either the x- or y-direction, resulting in a periodic grid.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/// Print init message in new grid filename
|
||||
void printInitMessage(std::ofstream& out, const char* origfilename, std::string direction) {
|
||||
std::ifstream infile;
|
||||
infile.open(origfilename, std::ios::in);
|
||||
if (!infile) {
|
||||
std::cerr << "Can't open input file " << origfilename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
// Print init message and copy comments from original grid file
|
||||
out << "-- This grdecl file is generated from OPM application 'mirror_grid.cpp'." << std::endl
|
||||
<< "-- The grid '" << origfilename << "' is mirrored around itself in the " << direction << " direction." << std::endl
|
||||
<< "-- Thus, the resulting grid should be periodic in the " << direction << "-direction." << std::endl
|
||||
<< "-- Original comments taken from '" << origfilename << "':" << std::endl;
|
||||
std::string nextInLine;
|
||||
while (getline(infile, nextInLine)) {
|
||||
if (nextInLine.substr(0,2) == "--") {
|
||||
out << nextInLine << std::endl;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
/// Write keyword values to file
|
||||
template <class T>
|
||||
void printKeywordValues(std::ofstream& out, std::string keyword, std::vector<T> values, int nCols) {
|
||||
out << keyword << std::endl;
|
||||
int col = 0;
|
||||
typename std::vector<T>::iterator iter;
|
||||
for (iter = values.begin(); iter != values.end(); ++iter) {
|
||||
out << *iter << " ";
|
||||
++col;
|
||||
// Break line for every nCols entry.
|
||||
if (col == nCols) {
|
||||
out << std::endl;
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
if (col != 0)
|
||||
out << std::endl;
|
||||
out << "/" << std::endl << std::endl;
|
||||
}
|
||||
|
||||
// forward declaration
|
||||
std::vector<double> getMapaxesValues(Opm::DeckConstPtr deck);
|
||||
|
||||
/// Mirror keyword MAPAXES in deck
|
||||
void mirror_mapaxes(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) {
|
||||
// Assumes axis aligned with x/y-direction
|
||||
std::cout << "Warning: Keyword MAPAXES not fully understood. Result should be verified manually." << std::endl;
|
||||
if (deck->hasKeyword("MAPAXES")) {
|
||||
std::vector<double> mapaxes = getMapaxesValues(deck);
|
||||
std::vector<double> mapaxes_mirrored = mapaxes;
|
||||
// Double the length of the coordinate axis
|
||||
if (direction == "x") {
|
||||
mapaxes_mirrored[4] = (mapaxes[4]-mapaxes[2])*2 + mapaxes[2];
|
||||
}
|
||||
else if (direction == "y") {
|
||||
mapaxes_mirrored[1] = (mapaxes[1]-mapaxes[3])*2 + mapaxes[3];
|
||||
}
|
||||
printKeywordValues(out, "MAPAXES", mapaxes_mirrored, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mirror keyword SPECGRID in deck
|
||||
void mirror_specgrid(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) {
|
||||
// We only need to multiply the dimension by 2 in the correct direction.
|
||||
Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0);
|
||||
std::vector<int> dimensions(3);
|
||||
dimensions[0] = specgridRecord->getItem("NX")->getInt(0);
|
||||
dimensions[1] = specgridRecord->getItem("NY")->getInt(0);
|
||||
dimensions[2] = specgridRecord->getItem("NZ")->getInt(0);
|
||||
if (direction == "x") {dimensions[0] *= 2;}
|
||||
else if (direction == "y") {dimensions[1] *= 2;}
|
||||
else {std::cerr << "Direction should be either x or y" << std::endl; exit(1);}
|
||||
out << "SPECGRID" << std::endl << dimensions[0] << " " << dimensions[1] << " " << dimensions[2] << " "
|
||||
<< specgridRecord->getItem("NUMRES")->getInt(0) << " "
|
||||
<< specgridRecord->getItem("COORD_TYPE")->getString(0) << " "
|
||||
<< std::endl << "/" << std::endl << std::endl;
|
||||
}
|
||||
|
||||
/// Mirror keyword COORD in deck
|
||||
void mirror_coord(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) {
|
||||
// We assume uniform spacing in x and y directions and parallel top and bottom faces
|
||||
Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0);
|
||||
std::vector<int> dimensions(3);
|
||||
dimensions[0] = specgridRecord->getItem("NX")->getInt(0);
|
||||
dimensions[1] = specgridRecord->getItem("NY")->getInt(0);
|
||||
dimensions[2] = specgridRecord->getItem("NZ")->getInt(0);
|
||||
std::vector<double> coord = deck->getKeyword("COORD")->getRawDoubleData();
|
||||
const int entries_per_pillar = 6;
|
||||
std::vector<double> coord_mirrored;
|
||||
// Handle the two directions differently due to ordering of the pillars.
|
||||
if (direction == "x") {
|
||||
// Total entries in mirrored ZCORN. Number of pillars times 6
|
||||
const int entries = (2*dimensions[0] + 1) * (dimensions[1] + 1) * entries_per_pillar;
|
||||
// Entries per line in x-direction. Number of pillars in x-direction times 6
|
||||
const int entries_per_line = entries_per_pillar*(dimensions[0] + 1);
|
||||
coord_mirrored.assign(entries, 0.0);
|
||||
// Distance between pillars in x-directiion
|
||||
const double spacing = coord[entries_per_pillar]-coord[0];
|
||||
std::vector<double>::iterator it_new = coord_mirrored.begin();
|
||||
std::vector<double>::iterator it_orig;
|
||||
// Loop through each pillar line in the x-direction
|
||||
for (it_orig = coord.begin(); it_orig != coord.end(); it_orig += entries_per_line) {
|
||||
// Copy old pillars
|
||||
copy(it_orig, it_orig + entries_per_line, it_new);
|
||||
// Add new pillars in between
|
||||
it_new += entries_per_line;
|
||||
std::vector<double> next_vec(it_orig + entries_per_line - entries_per_pillar, it_orig + entries_per_line);
|
||||
for (int r=0; r < dimensions[0]; ++r) {
|
||||
next_vec[0] += spacing;
|
||||
next_vec[3] += spacing;
|
||||
copy(next_vec.begin(), next_vec.end(), it_new);
|
||||
it_new += entries_per_pillar;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (direction == "y") {
|
||||
// Total entries in mirrored ZCORN. Number of pillars times 6
|
||||
const int entries = (dimensions[0] + 1) * (2*dimensions[1] + 1) * entries_per_pillar;
|
||||
// Entries per line in y-direction. Number of pillars in y-direction times 6
|
||||
const int entries_per_line = entries_per_pillar*(dimensions[0] + 1);
|
||||
coord_mirrored.assign(entries, 0.0);
|
||||
// Distance between pillars in y-directiion
|
||||
const double spacing = coord[entries_per_line + 1]-coord[1];
|
||||
std::vector<double>::iterator it_new = coord_mirrored.begin();
|
||||
// Copy old pillars
|
||||
copy(coord.begin(), coord.end(), it_new);
|
||||
// Add new pillars at the end
|
||||
it_new += coord.size();
|
||||
std::vector<double> next_vec(coord.end() - entries_per_line, coord.end());
|
||||
for ( ; it_new != coord_mirrored.end(); it_new += entries_per_line) {
|
||||
for (int i = 1; i < entries_per_line; i += 3) {
|
||||
next_vec[i] += spacing;
|
||||
}
|
||||
copy(next_vec.begin(), next_vec.end(), it_new);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "Direction should be either x or y" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
// Write new COORD values to output file
|
||||
printKeywordValues(out, "COORD", coord_mirrored, 6);
|
||||
}
|
||||
|
||||
/// Mirror keyword ZCORN in deck
|
||||
void mirror_zcorn(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) {
|
||||
Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0);
|
||||
std::vector<int> dimensions(3);
|
||||
dimensions[0] = specgridRecord->getItem("NX")->getInt(0);
|
||||
dimensions[1] = specgridRecord->getItem("NY")->getInt(0);
|
||||
dimensions[2] = specgridRecord->getItem("NZ")->getInt(0);
|
||||
std::vector<double> zcorn = deck->getKeyword("ZCORN")->getRawDoubleData();
|
||||
std::vector<double> zcorn_mirrored;
|
||||
// Handle the two directions differently due to ordering of the pillars.
|
||||
if (direction == "x") {
|
||||
// Total entries in mirrored ZCORN. Eight corners per cell.
|
||||
const int entries = dimensions[0]*2*dimensions[1]*dimensions[2]*8;
|
||||
zcorn_mirrored.assign(entries, 0.0);
|
||||
// Entries per line in x-direction. Two for each cell.
|
||||
const int entries_per_line = dimensions[0]*2;
|
||||
std::vector<double>::iterator it_new = zcorn_mirrored.begin();
|
||||
std::vector<double>::iterator it_orig = zcorn.begin();
|
||||
// Loop through each line and copy old corner-points and add new (which are the old reversed)
|
||||
for ( ; it_orig != zcorn.end(); it_orig += entries_per_line) {
|
||||
std::vector<double> next_vec(it_orig, it_orig + entries_per_line);
|
||||
std::vector<double> next_reversed = next_vec;
|
||||
reverse(next_reversed.begin(), next_reversed.end());
|
||||
// Copy old corner-points
|
||||
copy(it_orig, it_orig + entries_per_line, it_new);
|
||||
it_new += entries_per_line;
|
||||
// Add new corner-points
|
||||
copy(next_reversed.begin(), next_reversed.end(), it_new);
|
||||
it_new += entries_per_line;
|
||||
}
|
||||
}
|
||||
else if (direction == "y") {
|
||||
// Total entries in mirrored ZCORN. Eight corners per cell.
|
||||
const int entries = dimensions[0]*dimensions[1]*2*dimensions[2]*8;
|
||||
zcorn_mirrored.assign(entries, 0.0);
|
||||
// Entries per line in x-direction. Two for each cell.
|
||||
const int entries_per_line_x = dimensions[0]*2;
|
||||
// Entries per layer of corner-points. Four for each cell
|
||||
const int entries_per_layer = dimensions[0]*dimensions[1]*4;
|
||||
std::vector<double>::iterator it_new = zcorn_mirrored.begin();
|
||||
std::vector<double>::iterator it_orig = zcorn.begin();
|
||||
// Loop through each layer and copy old corner-points and add new (which are the old reordered)
|
||||
for ( ; it_orig != zcorn.end(); it_orig += entries_per_layer) {
|
||||
// Copy old corner-points
|
||||
copy(it_orig, it_orig + entries_per_layer, it_new);
|
||||
it_new += entries_per_layer;
|
||||
// Add new corner-points
|
||||
std::vector<double> next_vec(it_orig, it_orig + entries_per_layer);
|
||||
std::vector<double> next_reordered(entries_per_layer, 0.0);
|
||||
std::vector<double>::iterator it_next = next_vec.end();
|
||||
std::vector<double>::iterator it_reordered = next_reordered.begin();
|
||||
// Reorder next entries
|
||||
for ( ; it_reordered != next_reordered.end(); it_reordered += entries_per_line_x) {
|
||||
copy(it_next - entries_per_line_x, it_next, it_reordered);
|
||||
it_next -= entries_per_line_x;
|
||||
}
|
||||
copy(next_reordered.begin(), next_reordered.end(), it_new);
|
||||
it_new += entries_per_layer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "Direction should be either x or y" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
// Write new ZCORN values to output file
|
||||
printKeywordValues(out, "ZCORN", zcorn_mirrored, 8);
|
||||
}
|
||||
|
||||
std::vector<int> getKeywordValues(std::string keyword, Opm::DeckConstPtr deck, int /*dummy*/) {
|
||||
return deck->getKeyword(keyword)->getIntData();
|
||||
}
|
||||
|
||||
std::vector<double> getKeywordValues(std::string keyword, Opm::DeckConstPtr deck, double /*dummy*/) {
|
||||
return deck->getKeyword(keyword)->getRawDoubleData();
|
||||
}
|
||||
|
||||
std::vector<double> getMapaxesValues(Opm::DeckConstPtr deck)
|
||||
{
|
||||
Opm::DeckRecordConstPtr mapaxesRecord = deck->getKeyword("MAPAXES")->getRecord(0);
|
||||
std::vector<double> result;
|
||||
for (size_t itemIdx = 0; itemIdx < mapaxesRecord->size(); ++itemIdx) {
|
||||
Opm::DeckItemConstPtr curItem = mapaxesRecord->getItem(itemIdx);
|
||||
|
||||
for (size_t dataItemIdx = 0; dataItemIdx < curItem->size(); ++dataItemIdx) {
|
||||
result.push_back(curItem->getRawDouble(dataItemIdx));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Mirror keywords that have one value for each cell
|
||||
template <class T>
|
||||
void mirror_celldata(std::string keyword, Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) {
|
||||
if ( ! deck->hasKeyword(keyword)) {
|
||||
std::cout << "Ignoring keyword " << keyword << " as it was not found." << std::endl;
|
||||
return;
|
||||
}
|
||||
// Get data from eclipse deck
|
||||
Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0);
|
||||
std::vector<int> dimensions(3);
|
||||
dimensions[0] = specgridRecord->getItem("NX")->getInt(0);
|
||||
dimensions[1] = specgridRecord->getItem("NY")->getInt(0);
|
||||
dimensions[2] = specgridRecord->getItem("NZ")->getInt(0);
|
||||
std::vector<T> values = getKeywordValues(keyword, deck, T(0.0));
|
||||
std::vector<T> values_mirrored(2*dimensions[0]*dimensions[1]*dimensions[2], 0.0);
|
||||
// Handle the two directions differently due to ordering of the pillars.
|
||||
if (direction == "x") {
|
||||
typename std::vector<T>::iterator it_orig = values.begin();
|
||||
typename std::vector<T>::iterator it_new = values_mirrored.begin();
|
||||
// Loop through each line and copy old cell data and add new (which are the old reversed)
|
||||
for ( ; it_orig != values.end(); it_orig += dimensions[0]) {
|
||||
// Copy old cell data
|
||||
copy(it_orig, it_orig + dimensions[0], it_new);
|
||||
it_new += dimensions[0];
|
||||
// Add new cell data
|
||||
std::vector<double> next_vec(it_orig, it_orig + dimensions[0]);
|
||||
std::vector<double> next_reversed = next_vec;
|
||||
reverse(next_reversed.begin(), next_reversed.end());
|
||||
copy(next_reversed.begin(), next_reversed.end(), it_new);
|
||||
it_new += dimensions[0];
|
||||
}
|
||||
}
|
||||
else if (direction =="y") {
|
||||
typename std::vector<T>::iterator it_orig = values.begin();
|
||||
typename std::vector<T>::iterator it_new = values_mirrored.begin();
|
||||
// Entries per layer
|
||||
const int entries_per_layer = dimensions[0]*dimensions[1];
|
||||
// Loop through each layer and copy old cell data and add new (which are the old reordered)
|
||||
for ( ; it_orig != values.end(); it_orig += entries_per_layer) {
|
||||
// Copy old cell data
|
||||
copy(it_orig, it_orig + entries_per_layer, it_new);
|
||||
it_new += entries_per_layer;
|
||||
// Add new cell data
|
||||
std::vector<T> next_vec(it_orig, it_orig + entries_per_layer);
|
||||
std::vector<T> next_reordered(entries_per_layer, 0.0);
|
||||
typename std::vector<T>::iterator it_next = next_vec.end();
|
||||
typename std::vector<T>::iterator it_reordered = next_reordered.begin();
|
||||
// Reorder next entries
|
||||
for ( ; it_reordered != next_reordered.end(); it_reordered += dimensions[0]) {
|
||||
copy(it_next - dimensions[0], it_next, it_reordered);
|
||||
it_next -= dimensions[0];
|
||||
}
|
||||
copy(next_reordered.begin(), next_reordered.end(), it_new);
|
||||
it_new += entries_per_layer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "Direction should be either x or y" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
// Write new keyword values to output file
|
||||
printKeywordValues(out, keyword, values_mirrored, 8);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Set output precision
|
||||
int decimals = 16;
|
||||
|
||||
// Process input parameters
|
||||
if (argc != 3) {
|
||||
std::cout << "Usage: mirror_grid filename.grdecl direction" << std::endl;
|
||||
std::cout << "(replace direction with either x or y)" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
const char* eclipsefilename = argv[1];
|
||||
std::string direction(argv[2]);
|
||||
if ( ! ((direction == "x") || (direction == "y")) ) {
|
||||
std::cerr << "Unrecognized input parameter for direction: '" << direction
|
||||
<< "'. Should be either x or y (maybe also z later)." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Parse grdecl file
|
||||
std::cout << "Parsing grid file '" << eclipsefilename << "' ..." << std::endl;
|
||||
Opm::ParserPtr parser(new Opm::Parser);
|
||||
Opm::ParseMode parseMode;
|
||||
Opm::DeckConstPtr deck(parser->parseFile(eclipsefilename , parseMode));
|
||||
if ( ! (deck->hasKeyword("SPECGRID") && deck->hasKeyword("COORD") && deck->hasKeyword("ZCORN")) ) {
|
||||
std::cerr << "Grid file " << eclipsefilename << "are missing keywords SPECGRID, COORD or ZCORN!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create new grid file
|
||||
std::string mirrored_eclipsefilename = std::string(eclipsefilename);
|
||||
std::string::size_type last_dot = mirrored_eclipsefilename.find_last_of('.');
|
||||
mirrored_eclipsefilename = mirrored_eclipsefilename.substr(0, last_dot) + "_mirrored-" + direction + ".grdecl";
|
||||
std::ofstream outfile;
|
||||
outfile.open(mirrored_eclipsefilename.c_str(), std::ios::out | std::ios::trunc);
|
||||
if (!outfile) {
|
||||
std::cerr << "Can't open output file " << mirrored_eclipsefilename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
outfile.precision(decimals);
|
||||
outfile.setf(std::ios::fixed);
|
||||
|
||||
// Print init message
|
||||
printInitMessage(outfile, eclipsefilename, direction);
|
||||
|
||||
// Mirror keywords
|
||||
mirror_mapaxes(deck, direction, outfile);
|
||||
mirror_specgrid(deck, direction, outfile);
|
||||
mirror_coord(deck, direction, outfile);
|
||||
mirror_zcorn(deck, direction, outfile);
|
||||
mirror_celldata<int>("ACTNUM", deck, direction, outfile);
|
||||
mirror_celldata<double>("PERMX", deck, direction, outfile);
|
||||
mirror_celldata<double>("PERMY", deck, direction, outfile);
|
||||
mirror_celldata<double>("PERMZ", deck, direction, outfile);
|
||||
mirror_celldata<double>("PORO", deck, direction, outfile);
|
||||
mirror_celldata<int>("SATNUM", deck, direction, outfile);
|
||||
mirror_celldata<double>("NTG", deck, direction, outfile);
|
||||
mirror_celldata<double>("SWCR", deck, direction, outfile);
|
||||
mirror_celldata<double>("SOWCR", deck, direction, outfile);
|
||||
}
|
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/props/BlackoilPropertiesBasic.hpp>
|
||||
#include <opm/core/props/BlackoilPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/simulator/SimulatorCompressibleTwophase.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param)
|
||||
{
|
||||
if (param.anyUnused()) {
|
||||
std::cout << "-------------------- Unused parameters: --------------------\n";
|
||||
param.displayUsage();
|
||||
std::cout << "----------------------------------------------------------------" << std::endl;
|
||||
}
|
||||
}
|
||||
} // anon namespace
|
||||
|
||||
|
||||
|
||||
// ----------------- Main program -----------------
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
using namespace Opm;
|
||||
|
||||
std::cout << "\n================ Test program for weakly compressible two-phase flow ===============\n\n";
|
||||
parameter::ParameterGroup param(argc, argv, false);
|
||||
std::cout << "--------------- Reading parameters ---------------" << std::endl;
|
||||
|
||||
// If we have a "deck_filename", grid and props will be read from that.
|
||||
bool use_deck = param.has("deck_filename");
|
||||
EclipseStateConstPtr eclipseState;
|
||||
std::unique_ptr<GridManager> grid;
|
||||
std::unique_ptr<BlackoilPropertiesInterface> props;
|
||||
std::unique_ptr<RockCompressibility> rock_comp;
|
||||
|
||||
ParserPtr parser(new Opm::Parser());
|
||||
Opm::DeckConstPtr deck;
|
||||
|
||||
BlackoilState state;
|
||||
// bool check_well_controls = false;
|
||||
// int max_well_control_iterations = 0;
|
||||
double gravity[3] = { 0.0 };
|
||||
if (use_deck) {
|
||||
ParseMode parseMode;
|
||||
std::string deck_filename = param.get<std::string>("deck_filename");
|
||||
deck = parser->parseFile(deck_filename , parseMode);
|
||||
eclipseState.reset(new EclipseState(deck, parseMode));
|
||||
|
||||
// Grid init
|
||||
grid.reset(new GridManager(deck));
|
||||
// Rock and fluid init
|
||||
props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid(), param));
|
||||
// check_well_controls = param.getDefault("check_well_controls", false);
|
||||
// max_well_control_iterations = param.getDefault("max_well_control_iterations", 10);
|
||||
// Rock compressibility.
|
||||
rock_comp.reset(new RockCompressibility(deck, eclipseState));
|
||||
// Gravity.
|
||||
gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity;
|
||||
// Init state variables (saturation and pressure).
|
||||
if (param.has("init_saturation")) {
|
||||
initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
|
||||
} else {
|
||||
initStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state);
|
||||
}
|
||||
initBlackoilSurfvol(*grid->c_grid(), *props, state);
|
||||
} else {
|
||||
// Grid init.
|
||||
const int nx = param.getDefault("nx", 100);
|
||||
const int ny = param.getDefault("ny", 100);
|
||||
const int nz = param.getDefault("nz", 1);
|
||||
const double dx = param.getDefault("dx", 1.0);
|
||||
const double dy = param.getDefault("dy", 1.0);
|
||||
const double dz = param.getDefault("dz", 1.0);
|
||||
grid.reset(new GridManager(nx, ny, nz, dx, dy, dz));
|
||||
// Rock and fluid init.
|
||||
props.reset(new BlackoilPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells));
|
||||
// Rock compressibility.
|
||||
rock_comp.reset(new RockCompressibility(param));
|
||||
// Gravity.
|
||||
gravity[2] = param.getDefault("gravity", 0.0);
|
||||
// Init state variables (saturation and pressure).
|
||||
initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
|
||||
initBlackoilSurfvol(*grid->c_grid(), *props, state);
|
||||
}
|
||||
|
||||
bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
|
||||
const double *grav = use_gravity ? &gravity[0] : 0;
|
||||
|
||||
// Initialising src
|
||||
int num_cells = grid->c_grid()->number_of_cells;
|
||||
std::vector<double> src(num_cells, 0.0);
|
||||
if (use_deck) {
|
||||
// Do nothing, wells will be the driving force, not source terms.
|
||||
} else {
|
||||
// Compute pore volumes, in order to enable specifying injection rate
|
||||
// terms of total pore volume.
|
||||
std::vector<double> porevol;
|
||||
if (rock_comp->isActive()) {
|
||||
computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol);
|
||||
} else {
|
||||
computePorevolume(*grid->c_grid(), props->porosity(), porevol);
|
||||
}
|
||||
const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0);
|
||||
const double default_injection = use_gravity ? 0.0 : 0.1;
|
||||
const double flow_per_sec = param.getDefault<double>("injected_porevolumes_per_day", default_injection)
|
||||
*tot_porevol_init/unit::day;
|
||||
src[0] = flow_per_sec;
|
||||
src[num_cells - 1] = -flow_per_sec;
|
||||
}
|
||||
|
||||
// Boundary conditions.
|
||||
FlowBCManager bcs;
|
||||
if (param.getDefault("use_pside", false)) {
|
||||
int pside = param.get<int>("pside");
|
||||
double pside_pressure = param.get<double>("pside_pressure");
|
||||
bcs.pressureSide(*grid->c_grid(), FlowBCManager::Side(pside), pside_pressure);
|
||||
}
|
||||
|
||||
// Linear solver.
|
||||
LinearSolverFactory linsolver(param);
|
||||
|
||||
// Write parameters used for later reference.
|
||||
bool output = param.getDefault("output", true);
|
||||
std::ofstream epoch_os;
|
||||
std::string output_dir;
|
||||
if (output) {
|
||||
output_dir =
|
||||
param.getDefault("output_dir", std::string("output"));
|
||||
boost::filesystem::path fpath(output_dir);
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
std::string filename = output_dir + "/epoch_timing.param";
|
||||
epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out);
|
||||
// open file to clean it. The file is appended to in SimulatorTwophase
|
||||
filename = output_dir + "/step_timing.param";
|
||||
std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out);
|
||||
step_os.close();
|
||||
param.writeParam(output_dir + "/simulation.param");
|
||||
}
|
||||
|
||||
|
||||
std::cout << "\n\n================ Starting main simulation loop ===============\n";
|
||||
|
||||
SimulatorReport rep;
|
||||
if (!use_deck) {
|
||||
// Simple simulation without a deck.
|
||||
WellsManager wells; // no wells.
|
||||
SimulatorCompressibleTwophase simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
src,
|
||||
bcs.c_bcs(),
|
||||
linsolver,
|
||||
grav);
|
||||
SimulatorTimer simtimer;
|
||||
simtimer.init(param);
|
||||
warnIfUnusedParams(param);
|
||||
WellState well_state;
|
||||
well_state.init(0, state);
|
||||
rep = simulator.run(simtimer, state, well_state);
|
||||
} else {
|
||||
// With a deck, we may have more epochs etc.
|
||||
WellState well_state;
|
||||
int step = 0;
|
||||
SimulatorTimer simtimer;
|
||||
// Use timer for last epoch to obtain total time.
|
||||
Opm::TimeMapPtr timeMap(new Opm::TimeMap(deck));
|
||||
simtimer.init(timeMap);
|
||||
const double total_time = simtimer.totalTime();
|
||||
for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) {
|
||||
simtimer.setCurrentStepNum(step);
|
||||
simtimer.setTotalTime(total_time);
|
||||
|
||||
// Report on start of report step.
|
||||
std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------"
|
||||
<< "\n (number of steps: "
|
||||
<< simtimer.numSteps() - step << ")\n\n" << std::flush;
|
||||
|
||||
// Create new wells, well_state
|
||||
WellsManager wells(eclipseState , reportStepIdx , *grid->c_grid(), props->permeability());
|
||||
// @@@ HACK: we should really make a new well state and
|
||||
// properly transfer old well state to it every report step,
|
||||
// since number of wells may change etc.
|
||||
if (reportStepIdx == 0) {
|
||||
well_state.init(wells.c_wells(), state);
|
||||
}
|
||||
|
||||
// Create and run simulator.
|
||||
SimulatorCompressibleTwophase simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
src,
|
||||
bcs.c_bcs(),
|
||||
linsolver,
|
||||
grav);
|
||||
if (reportStepIdx == 0) {
|
||||
warnIfUnusedParams(param);
|
||||
}
|
||||
SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state);
|
||||
if (output) {
|
||||
epoch_rep.reportParam(epoch_os);
|
||||
}
|
||||
// Update total timing report and remember step number.
|
||||
rep += epoch_rep;
|
||||
step = simtimer.currentStepNum();
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n\n================ End of simulation ===============\n\n";
|
||||
rep.report(std::cout);
|
||||
|
||||
if (output) {
|
||||
std::string filename = output_dir + "/walltime.param";
|
||||
std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out);
|
||||
rep.reportParam(tot_os);
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
|
@ -1,309 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/props/IncompPropertiesBasic.hpp>
|
||||
#include <opm/core/props/IncompPropertiesFromDeck.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
|
||||
#include <opm/core/simulator/TwophaseState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/simulator/SimulatorIncompTwophase.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param)
|
||||
{
|
||||
if (param.anyUnused()) {
|
||||
std::cout << "-------------------- Unused parameters: --------------------\n";
|
||||
param.displayUsage();
|
||||
std::cout << "----------------------------------------------------------------" << std::endl;
|
||||
}
|
||||
}
|
||||
} // anon namespace
|
||||
|
||||
|
||||
|
||||
// ----------------- Main program -----------------
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
using namespace Opm;
|
||||
|
||||
std::cout << "\n================ Test program for incompressible two-phase flow ===============\n\n";
|
||||
parameter::ParameterGroup param(argc, argv, false);
|
||||
std::cout << "--------------- Reading parameters ---------------" << std::endl;
|
||||
|
||||
#if ! HAVE_SUITESPARSE_UMFPACK_H
|
||||
// This is an extra check to intercept a potentially invalid request for the
|
||||
// implicit transport solver as early as possible for the user.
|
||||
{
|
||||
const bool use_reorder = param.getDefault("use_reorder", true);
|
||||
if (!use_reorder) {
|
||||
OPM_THROW(std::runtime_error, "Cannot use implicit transport solver without UMFPACK. "
|
||||
"Either reconfigure opm-core with SuiteSparse/UMFPACK support and recompile, "
|
||||
"or use the reordering solver (use_reorder=true).");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we have a "deck_filename", grid and props will be read from that.
|
||||
bool use_deck = param.has("deck_filename");
|
||||
EclipseStateConstPtr eclipseState;
|
||||
|
||||
Opm::DeckConstPtr deck;
|
||||
std::unique_ptr<GridManager> grid;
|
||||
std::unique_ptr<IncompPropertiesInterface> props;
|
||||
std::unique_ptr<RockCompressibility> rock_comp;
|
||||
TwophaseState state;
|
||||
// bool check_well_controls = false;
|
||||
// int max_well_control_iterations = 0;
|
||||
double gravity[3] = { 0.0 };
|
||||
if (use_deck) {
|
||||
ParserPtr parser(new Opm::Parser());
|
||||
ParseMode parseMode;
|
||||
|
||||
std::string deck_filename = param.get<std::string>("deck_filename");
|
||||
deck = parser->parseFile(deck_filename , parseMode);
|
||||
eclipseState.reset( new EclipseState(deck, parseMode));
|
||||
// Grid init
|
||||
grid.reset(new GridManager(deck));
|
||||
// Rock and fluid init
|
||||
props.reset(new IncompPropertiesFromDeck(deck, eclipseState, *grid->c_grid()));
|
||||
// check_well_controls = param.getDefault("check_well_controls", false);
|
||||
// max_well_control_iterations = param.getDefault("max_well_control_iterations", 10);
|
||||
// Rock compressibility.
|
||||
rock_comp.reset(new RockCompressibility(deck, eclipseState));
|
||||
// Gravity.
|
||||
gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity;
|
||||
// Init state variables (saturation and pressure).
|
||||
if (param.has("init_saturation")) {
|
||||
initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
|
||||
} else {
|
||||
initStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state);
|
||||
}
|
||||
} else {
|
||||
// Grid init.
|
||||
const int nx = param.getDefault("nx", 100);
|
||||
const int ny = param.getDefault("ny", 100);
|
||||
const int nz = param.getDefault("nz", 1);
|
||||
const double dx = param.getDefault("dx", 1.0);
|
||||
const double dy = param.getDefault("dy", 1.0);
|
||||
const double dz = param.getDefault("dz", 1.0);
|
||||
grid.reset(new GridManager(nx, ny, nz, dx, dy, dz));
|
||||
// Rock and fluid init.
|
||||
props.reset(new IncompPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells));
|
||||
// Rock compressibility.
|
||||
rock_comp.reset(new RockCompressibility(param));
|
||||
// Gravity.
|
||||
gravity[2] = param.getDefault("gravity", 0.0);
|
||||
// Init state variables (saturation and pressure).
|
||||
initStateBasic(*grid->c_grid(), *props, param, gravity[2], state);
|
||||
}
|
||||
|
||||
// Warn if gravity but no density difference.
|
||||
bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
|
||||
if (use_gravity) {
|
||||
if (props->density()[0] == props->density()[1]) {
|
||||
std::cout << "**** Warning: nonzero gravity, but zero density difference." << std::endl;
|
||||
}
|
||||
}
|
||||
const double *grav = use_gravity ? &gravity[0] : 0;
|
||||
|
||||
// Initialising src
|
||||
int num_cells = grid->c_grid()->number_of_cells;
|
||||
std::vector<double> src(num_cells, 0.0);
|
||||
if (use_deck) {
|
||||
// Do nothing, wells will be the driving force, not source terms.
|
||||
} else {
|
||||
// Compute pore volumes, in order to enable specifying injection rate
|
||||
// terms of total pore volume.
|
||||
std::vector<double> porevol;
|
||||
if (rock_comp->isActive()) {
|
||||
computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol);
|
||||
} else {
|
||||
computePorevolume(*grid->c_grid(), props->porosity(), porevol);
|
||||
}
|
||||
const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0);
|
||||
const double default_injection = use_gravity ? 0.0 : 0.1;
|
||||
const double flow_per_sec = param.getDefault<double>("injected_porevolumes_per_day", default_injection)
|
||||
*tot_porevol_init/unit::day;
|
||||
src[0] = flow_per_sec;
|
||||
src[num_cells - 1] = -flow_per_sec;
|
||||
}
|
||||
|
||||
// Boundary conditions.
|
||||
FlowBCManager bcs;
|
||||
if (param.getDefault("use_pside", false)) {
|
||||
int pside = param.get<int>("pside");
|
||||
double pside_pressure = param.get<double>("pside_pressure");
|
||||
bcs.pressureSide(*grid->c_grid(), FlowBCManager::Side(pside), pside_pressure);
|
||||
}
|
||||
|
||||
// Linear solver.
|
||||
LinearSolverFactory linsolver(param);
|
||||
|
||||
// Write parameters used for later reference.
|
||||
bool output = param.getDefault("output", true);
|
||||
std::ofstream epoch_os;
|
||||
std::string output_dir;
|
||||
if (output) {
|
||||
output_dir =
|
||||
param.getDefault("output_dir", std::string("output"));
|
||||
boost::filesystem::path fpath(output_dir);
|
||||
try {
|
||||
create_directories(fpath);
|
||||
}
|
||||
catch (...) {
|
||||
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
||||
}
|
||||
std::string filename = output_dir + "/epoch_timing.param";
|
||||
epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out);
|
||||
// open file to clean it. The file is appended to in SimulatorTwophase
|
||||
filename = output_dir + "/step_timing.param";
|
||||
std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out);
|
||||
step_os.close();
|
||||
param.writeParam(output_dir + "/simulation.param");
|
||||
}
|
||||
|
||||
SimulatorReport rep;
|
||||
if (!use_deck) {
|
||||
std::cout << "\n\n================ Starting main simulation loop ===============\n"
|
||||
<< " (number of report steps: 1)\n\n" << std::flush;
|
||||
// Simple simulation without a deck.
|
||||
WellsManager wells; // no wells.
|
||||
SimulatorIncompTwophase simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
src,
|
||||
bcs.c_bcs(),
|
||||
linsolver,
|
||||
grav);
|
||||
SimulatorTimer simtimer;
|
||||
simtimer.init(param);
|
||||
warnIfUnusedParams(param);
|
||||
WellState well_state;
|
||||
well_state.init(0, state);
|
||||
rep = simulator.run(simtimer, state, well_state);
|
||||
} else {
|
||||
// With a deck, we may have more epochs etc.
|
||||
Opm::TimeMapConstPtr timeMap = eclipseState->getSchedule()->getTimeMap();
|
||||
|
||||
std::cout << "\n\n================ Starting main simulation loop ===============\n"
|
||||
<< " (number of report steps: "
|
||||
<< timeMap->numTimesteps() << ")\n\n" << std::flush;
|
||||
WellState well_state;
|
||||
int step = 0;
|
||||
SimulatorTimer simtimer;
|
||||
// Use timer for last epoch to obtain total time.
|
||||
simtimer.init(timeMap);
|
||||
const double total_time = simtimer.totalTime();
|
||||
// for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) {
|
||||
size_t reportStepIdx = 0; // Only handle a single, unchanging well setup.
|
||||
{
|
||||
// Update the timer.
|
||||
simtimer.setCurrentStepNum(step);
|
||||
simtimer.setTotalTime(total_time);
|
||||
|
||||
// Report on start of report step.
|
||||
// std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------"
|
||||
// << "\n (number of time steps: "
|
||||
// << simtimer.numSteps() - step << ")\n\n" << std::flush;
|
||||
|
||||
// Create new wells, well_state
|
||||
WellsManager wells(eclipseState , reportStepIdx , *grid->c_grid(), props->permeability());
|
||||
// @@@ HACK: we should really make a new well state and
|
||||
// properly transfer old well state to it every report step,
|
||||
// since number of wells may change etc.
|
||||
if (reportStepIdx == 0) {
|
||||
well_state.init(wells.c_wells(), state);
|
||||
}
|
||||
|
||||
// Create and run simulator.
|
||||
SimulatorIncompTwophase simulator(param,
|
||||
*grid->c_grid(),
|
||||
*props,
|
||||
rock_comp->isActive() ? rock_comp.get() : 0,
|
||||
wells,
|
||||
src,
|
||||
bcs.c_bcs(),
|
||||
linsolver,
|
||||
grav);
|
||||
if (reportStepIdx == 0) {
|
||||
warnIfUnusedParams(param);
|
||||
}
|
||||
SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state);
|
||||
if (output) {
|
||||
epoch_rep.reportParam(epoch_os);
|
||||
}
|
||||
// Update total timing report and remember step number.
|
||||
rep += epoch_rep;
|
||||
step = simtimer.currentStepNum();
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n\n================ End of simulation ===============\n\n";
|
||||
rep.report(std::cout);
|
||||
|
||||
if (output) {
|
||||
std::string filename = output_dir + "/walltime.param";
|
||||
std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out);
|
||||
rep.reportParam(tot_os);
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <opm/core/simulator/initState.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/pressure/IncompTpfa.hpp>
|
||||
#include <opm/core/props/IncompPropertiesFromDeck.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
#include <opm/core/simulator/TwophaseState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/pressure/FlowBCManager.hpp>
|
||||
#include <opm/core/linalg/LinearSolverFactory.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
using namespace Opm::parameter;
|
||||
using namespace Opm;
|
||||
ParameterGroup parameters(argc, argv, false);
|
||||
std::string file_name = parameters.getDefault<std::string > ("inputdeck", "data.data");
|
||||
|
||||
SimulatorTimer simtimer;
|
||||
simtimer.init(parameters);
|
||||
|
||||
// Read input file
|
||||
ParseMode parseMode;
|
||||
Opm::ParserPtr parser(new Opm::Parser());
|
||||
Opm::DeckConstPtr deck = parser->parseFile(file_name , parseMode);
|
||||
Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(deck , parseMode));
|
||||
std::cout << "Done!" << std::endl;
|
||||
|
||||
// Setup grid
|
||||
GridManager grid(deck);
|
||||
|
||||
// Define rock and fluid properties
|
||||
IncompPropertiesFromDeck incomp_properties(deck, eclipseState, *grid.c_grid());
|
||||
RockCompressibility rock_comp(deck, eclipseState);
|
||||
|
||||
// Finally handle the wells
|
||||
WellsManager wells(eclipseState , 0 , *grid.c_grid(), incomp_properties.permeability());
|
||||
|
||||
double gravity[3] = {0.0, 0.0, parameters.getDefault<double>("gravity", 0.0)};
|
||||
Opm::LinearSolverFactory linsolver(parameters);
|
||||
double nl_pressure_residual_tolerance = 1e-8;
|
||||
double nl_pressure_change_tolerance = 0.0;
|
||||
int nl_pressure_maxiter = 100;
|
||||
if (rock_comp.isActive()) {
|
||||
nl_pressure_residual_tolerance = parameters.getDefault("nl_pressure_residual_tolerance", 1e-8);
|
||||
nl_pressure_change_tolerance = parameters.getDefault("nl_pressure_change_tolerance", 1.0); // in Pascal
|
||||
nl_pressure_maxiter = parameters.getDefault("nl_pressure_maxiter", 10);
|
||||
}
|
||||
|
||||
std::vector<double> src;
|
||||
Opm::FlowBCManager bcs;
|
||||
|
||||
// EXPERIMENT_ISTL
|
||||
IncompTpfa pressure_solver(*grid.c_grid(), incomp_properties, &rock_comp, linsolver,
|
||||
nl_pressure_residual_tolerance, nl_pressure_change_tolerance, nl_pressure_maxiter,
|
||||
gravity, wells.c_wells(), src, bcs.c_bcs());
|
||||
|
||||
|
||||
std::vector<int> all_cells;
|
||||
for (int i = 0; i < grid.c_grid()->number_of_cells; i++) {
|
||||
all_cells.push_back(i);
|
||||
}
|
||||
|
||||
Opm::TwophaseState state;
|
||||
|
||||
initStateFromDeck(*grid.c_grid(), incomp_properties, deck, gravity[2], state);
|
||||
|
||||
Opm::WellState well_state;
|
||||
well_state.init(wells.c_wells(), state);
|
||||
|
||||
pressure_solver.solve(simtimer.currentStepLength(), state, well_state);
|
||||
|
||||
const int np = incomp_properties.numPhases();
|
||||
std::vector<double> fractional_flows(grid.c_grid()->number_of_cells*np, 0.0);
|
||||
computeFractionalFlow(incomp_properties, all_cells, state.saturation(), fractional_flows);
|
||||
|
||||
// This will be refactored into a separate function once done
|
||||
std::vector<double> well_resflows(wells.c_wells()->number_of_wells*np, 0.0);
|
||||
computePhaseFlowRatesPerWell(*wells.c_wells(), well_state.perfRates(), fractional_flows, well_resflows);
|
||||
// We approximate (for _testing_ that resflows = surfaceflows)
|
||||
for (int wc_iter = 0; wc_iter < 10 && !wells.conditionsMet(well_state.bhp(), well_resflows, well_resflows); ++wc_iter) {
|
||||
std::cout << "Conditions not met for well, trying again" << std::endl;
|
||||
pressure_solver.solve(simtimer.currentStepLength(), state, well_state);
|
||||
std::cout << "Solved" << std::endl;
|
||||
|
||||
computePhaseFlowRatesPerWell(*wells.c_wells(), well_state.perfRates(), fractional_flows, well_resflows);
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::vector<double> porevol;
|
||||
computePorevolume(*grid->c_grid(), incomp_properties, porevol);
|
||||
|
||||
|
||||
|
||||
TwophaseFluid fluid(incomp_properties);
|
||||
TransportModel model(fluid, *grid->c_grid(), porevol, gravity[2], true);
|
||||
|
||||
TransportSolver tsolver(model);
|
||||
|
||||
TransportSource* tsrc = create_transport_source(2, 2);
|
||||
double ssrc[] = {1.0, 0.0};
|
||||
double ssink[] = {0.0, 1.0};
|
||||
double zdummy[] = {0.0, 0.0};
|
||||
|
||||
{
|
||||
int well_cell_index = 0;
|
||||
for (int well = 0; well < wells.c_wells()->number_of_wells; ++well) {
|
||||
for (int cell = wells.c_wells()->well_connpos[well]; cell < wells.c_wells()->well_connpos[well + 1]; ++cell) {
|
||||
if (well_rate_per_cell[well_cell_index] > 0.0) {
|
||||
append_transport_source(well_cell_index, 2, 0,
|
||||
well_rate_per_cell[well_cell_index], ssrc, zdummy, tsrc);
|
||||
} else if (well_rate_per_cell[well_cell_index] < 0.0) {
|
||||
append_transport_source(well_cell_index, 2, 0,
|
||||
well_rate_per_cell[well_cell_index], ssink, zdummy, tsrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tsolver.solve(*grid->c_grid(), tsrc, stepsize, ctrl, state, linsolve, rpt);
|
||||
|
||||
Opm::computeInjectedProduced(*props, state.saturation(), src, stepsize, injected, produced);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
||||
throw;
|
||||
}
|
14
jenkins/README.md
Normal file
14
jenkins/README.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# opm-core jenkins build scripts:
|
||||
|
||||
**build.sh**:
|
||||
This script will build dependencies, then build opm-core and execute its tests.
|
||||
It also inspects the $ghbPrBuildComment environmental variable and builds
|
||||
downstreams if requested. It inspects the $ghbPrBuildComment
|
||||
environmental variable to obtain a pull request to use for the modules.
|
||||
|
||||
It is intended for pre-merge builds of pull requests.
|
||||
|
||||
To specify a given pull request to use for upstreams and downstreams,
|
||||
trigger line needs to contain <module-name>=<pull request number>.
|
||||
|
||||
To build with downstreams the trigger line needs to contain 'with downstreams'.
|
57
jenkins/build.sh
Executable file
57
jenkins/build.sh
Executable file
|
@ -0,0 +1,57 @@
|
|||
#!/bin/bash
|
||||
|
||||
declare -a upstreams
|
||||
upstreams=(opm-common
|
||||
libecl
|
||||
opm-parser
|
||||
opm-material
|
||||
opm-output
|
||||
opm-grid)
|
||||
|
||||
declare -A upstreamRev
|
||||
upstreamRev[opm-common]=master
|
||||
upstreamRev[libecl]=master
|
||||
upstreamRev[opm-parser]=master
|
||||
upstreamRev[opm-material]=master
|
||||
upstreamRev[opm-output]=master
|
||||
upstreamRev[opm-grid]=master
|
||||
|
||||
if grep -q "opm-common=" <<< $ghprbCommentBody
|
||||
then
|
||||
upstreamRev[opm-common]=pull/`echo $ghprbCommentBody | sed -r 's/.*opm-common=([0-9]+).*/\1/g'`/merge
|
||||
fi
|
||||
|
||||
# Downstream revisions
|
||||
declare -a downstreams
|
||||
downstreams=(ewoms
|
||||
opm-simulators
|
||||
opm-upscaling)
|
||||
|
||||
declare -A downstreamRev
|
||||
downstreamRev[ewoms]=master
|
||||
downstreamRev[opm-simulators]=master
|
||||
downstreamRev[opm-upscaling]=master
|
||||
|
||||
# Clone opm-common
|
||||
pushd .
|
||||
mkdir -p $WORKSPACE/deps/opm-common
|
||||
cd $WORKSPACE/deps/opm-common
|
||||
git init .
|
||||
git remote add origin https://github.com/OPM/opm-common
|
||||
git fetch --depth 1 origin ${upstreamRev[opm-common]}:branch_to_build
|
||||
test $? -eq 0 || exit 1
|
||||
git checkout branch_to_build
|
||||
popd
|
||||
|
||||
source $WORKSPACE/deps/opm-common/jenkins/build-opm-module.sh
|
||||
|
||||
parseRevisions
|
||||
printHeader opm-core
|
||||
|
||||
# Setup opm-data
|
||||
if grep -q "with downstreams" <<< $ghprbCommentBody
|
||||
then
|
||||
source $WORKSPACE/deps/opm-common/jenkins/setup-opm-data.sh
|
||||
fi
|
||||
|
||||
build_module_full opm-core
|
49
opm-core-prereqs.cmake
Normal file
49
opm-core-prereqs.cmake
Normal file
|
@ -0,0 +1,49 @@
|
|||
# defines that must be present in config.h for our headers
|
||||
set (opm-core_CONFIG_VAR
|
||||
HAVE_ERT
|
||||
HAVE_SUITESPARSE_UMFPACK_H
|
||||
HAVE_DUNE_ISTL
|
||||
HAVE_MPI
|
||||
HAVE_PETSC
|
||||
DUNE_ISTL_VERSION_MAJOR
|
||||
DUNE_ISTL_VERSION_MINOR
|
||||
DUNE_ISTL_VERSION_REVISION
|
||||
)
|
||||
|
||||
# dependencies
|
||||
set (opm-core_DEPS
|
||||
# compile with C99 support if available
|
||||
"C99"
|
||||
# compile with C++0x/11 support if available
|
||||
"CXX11Features REQUIRED"
|
||||
# various runtime library enhancements
|
||||
"Boost 1.44.0
|
||||
COMPONENTS date_time filesystem system unit_test_framework REQUIRED"
|
||||
# matrix library
|
||||
"BLAS REQUIRED"
|
||||
"LAPACK REQUIRED"
|
||||
# Tim Davis' SuiteSparse archive
|
||||
"SuiteSparse COMPONENTS umfpack"
|
||||
# solver
|
||||
"SuperLU"
|
||||
# Eclipse I/O tools
|
||||
"ecl REQUIRED"
|
||||
# Look for MPI support
|
||||
"MPI"
|
||||
# PETSc numerical backend
|
||||
"PETSc"
|
||||
# DUNE dependency
|
||||
"dune-common"
|
||||
"dune-istl"
|
||||
"opm-common REQUIRED"
|
||||
# Parser library for ECL-type simulation models
|
||||
"opm-parser REQUIRED"
|
||||
# the code which implements the material laws
|
||||
"opm-material REQUIRED"
|
||||
# the code which implements the output routines
|
||||
"opm-output REQUIRED"
|
||||
# the code which implements grids
|
||||
"opm-grid REQUIRED"
|
||||
)
|
||||
|
||||
find_package_deps(opm-core)
|
|
@ -48,7 +48,7 @@ creation and destruction of an UnstructuredGrid. The method
|
|||
Opm::GridManager::c_grid() provides access to the underlying
|
||||
UnstructuredGrid structure. This class also provides an easy way
|
||||
to initialize a grid from an ECLIPSE-format input deck, via the
|
||||
constructor taking an Opm::DeckConstPtr.
|
||||
constructor taking an const Opm::Deck.
|
||||
|
||||
|
||||
<h3>Well handling</h3>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <opm/core/flowdiagnostics/AnisotropicEikonal.hpp>
|
||||
#include <opm/core/grid/GridUtilities.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Opm
|
|||
|
||||
/// Construct solver.
|
||||
TofDiscGalReorder::TofDiscGalReorder(const UnstructuredGrid& grid,
|
||||
const parameter::ParameterGroup& param)
|
||||
const ParameterGroup& param)
|
||||
: grid_(grid),
|
||||
use_cvi_(false),
|
||||
use_limiter_(false),
|
||||
|
|
|
@ -32,9 +32,9 @@ namespace Opm
|
|||
{
|
||||
|
||||
class IncompPropertiesInterface;
|
||||
class ParameterGroup;
|
||||
class VelocityInterpolationInterface;
|
||||
class DGBasisInterface;
|
||||
namespace parameter { class ParameterGroup; }
|
||||
template <typename T> class SparseTable;
|
||||
|
||||
/// Implements a discontinuous Galerkin solver for
|
||||
|
@ -72,7 +72,7 @@ namespace Opm
|
|||
/// - AsSimultaneousPostProcess -- Apply to each cell independently, using un-
|
||||
/// limited solution in neighbouring cells.
|
||||
TofDiscGalReorder(const UnstructuredGrid& grid,
|
||||
const parameter::ParameterGroup& param);
|
||||
const ParameterGroup& param);
|
||||
|
||||
|
||||
/// Solve for time-of-flight.
|
||||
|
|
313
opm/core/grid.h
313
opm/core/grid.h
|
@ -1,313 +0,0 @@
|
|||
/*
|
||||
Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GRID_HEADER_INCLUDED
|
||||
#define OPM_GRID_HEADER_INCLUDED
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Main OPM-Core grid data structure along with helper functions for
|
||||
* construction, destruction and reading a grid representation from disk.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
---- synopsis of grid.h ----
|
||||
|
||||
struct UnstructuredGrid
|
||||
{
|
||||
int dimensions;
|
||||
int number_of_cells;
|
||||
int number_of_faces;
|
||||
int number_of_nodes;
|
||||
int *face_nodes;
|
||||
int *face_nodepos;
|
||||
int *face_cells;
|
||||
int *cell_faces;
|
||||
int *cell_facepos;
|
||||
double *node_coordinates;
|
||||
double *face_centroids;
|
||||
double *face_areas;
|
||||
double *face_normals;
|
||||
double *cell_centroids;
|
||||
double *cell_volumes;
|
||||
int *global_cell;
|
||||
int cartdims[3];
|
||||
int *cell_facetag;
|
||||
};
|
||||
|
||||
void destroy_grid(struct UnstructuredGrid *g);
|
||||
|
||||
struct UnstructuredGrid *
|
||||
create_grid_empty(void);
|
||||
|
||||
struct UnstructuredGrid *
|
||||
allocate_grid(size_t ndims ,
|
||||
size_t ncells ,
|
||||
size_t nfaces ,
|
||||
size_t nfacenodes,
|
||||
size_t ncellfaces,
|
||||
size_t nnodes );
|
||||
|
||||
struct UnstructuredGrid *
|
||||
read_grid(const char *fname);
|
||||
|
||||
---- end of synopsis of grid.h ----
|
||||
*/
|
||||
|
||||
/**
|
||||
Data structure for an unstructured grid, unstructured meaning that
|
||||
any cell may have an arbitrary number of adjacent cells. The struct
|
||||
contains both topological and geometrical data.
|
||||
|
||||
The grid consists of a set of cells, which are assumed to partion
|
||||
the grid domain. A face is defined as the nonempty intersection of
|
||||
(the closure of) two grid cells (the grid is a cell complex).
|
||||
|
||||
The topology information is limited to some adjacency relations
|
||||
between cells, faces and nodes only. The data structure does not
|
||||
contain any information pertaining to edges (except in 2d, where
|
||||
edges are the same as faces).
|
||||
|
||||
The geometry information is limited to centroids, areas/volumes and
|
||||
normals.
|
||||
*/
|
||||
struct UnstructuredGrid
|
||||
{
|
||||
/**
|
||||
The topological and geometrical dimensionality of the
|
||||
grid. Note that we do not support grids that are embedded in
|
||||
higher-dimensional spaces, such as 2d grids embedded in 3d.
|
||||
This number must be 2 or 3.
|
||||
*/
|
||||
int dimensions;
|
||||
|
||||
/** The number of cells in the grid. */
|
||||
int number_of_cells;
|
||||
/** The number of faces in the grid. */
|
||||
int number_of_faces;
|
||||
/** The number of nodes in the grid. */
|
||||
int number_of_nodes;
|
||||
|
||||
/**
|
||||
Contains for each face, the indices of its adjacent nodes.
|
||||
The size of the array is equal to the sum over all faces of
|
||||
each face's number of adjacent nodes, which also is equal to
|
||||
face_nodepos[number_of_faces].
|
||||
*/
|
||||
int *face_nodes;
|
||||
/**
|
||||
For a face f, face_nodepos[f] contains the starting index
|
||||
for f's nodes in the face_nodes array.
|
||||
The size of the array is equal to (number_of_faces + 1).
|
||||
*/
|
||||
int *face_nodepos;
|
||||
/**
|
||||
For a face f, face_cells[2*f] and face_cells[2*f + 1] contain
|
||||
the cell indices of the cells adjacent to f. The number -1
|
||||
indicates the outer boundary.
|
||||
The order is significant, as it gives the orientation: if
|
||||
face_cells[2*f] == a and face_cells[2*f + 1] == b, f is
|
||||
oriented from a to b. The inverse of this mapping is stored in
|
||||
cell_faces and cell_facepos.
|
||||
The size of the array is equal to (2*number_of_faces).
|
||||
*/
|
||||
int *face_cells;
|
||||
|
||||
/**
|
||||
Contains for each cell, the indices of its adjacent faces.
|
||||
The size of the array is equal to the sum over all cells of
|
||||
each cell's number of adjacent faces, which also is equal to
|
||||
cell_facepos[number_of_cells].
|
||||
*/
|
||||
int *cell_faces;
|
||||
/**
|
||||
For a cell c, cell_facepos[c] contains the starting index
|
||||
for c's faces in the cell_faces array.
|
||||
The size of the array is equal to (number_of_cells + 1).
|
||||
*/
|
||||
int *cell_facepos;
|
||||
|
||||
/**
|
||||
Node coordinates, stored consecutively for each node. That is,
|
||||
for a node i, node_coordinates[dimensions*i + d] contains the
|
||||
d'th coordinate of node i.
|
||||
The size of the array is equal to (dimensions*number_of_nodes).
|
||||
*/
|
||||
double *node_coordinates;
|
||||
|
||||
/**
|
||||
Exact or approximate face centroids, stored consecutively for each face. That is,
|
||||
for a face f, face_centroids[dimensions*f + d] contains the
|
||||
d'th coordinate of f's centroid.
|
||||
The size of the array is equal to (dimensions*number_of_faces).
|
||||
*/
|
||||
double *face_centroids;
|
||||
/**
|
||||
Exact or approximate face areas.
|
||||
The size of the array is equal to number_of_faces.
|
||||
*/
|
||||
double *face_areas;
|
||||
/**
|
||||
Exact or approximate face normals, stored consecutively for
|
||||
each face. That is, for a face f, face_normals[dimensions*f + d]
|
||||
contains the d'th coordinate of f's normal.
|
||||
The size of the array is equal to (dimensions*number_of_faces).
|
||||
|
||||
IMPORTANT: the normals are not normalized to have unit length!
|
||||
They are assumed to always have length equal to the
|
||||
corresponding face's area.
|
||||
*/
|
||||
double *face_normals;
|
||||
|
||||
/**
|
||||
Exact or approximate cell centroids, stored consecutively for each cell. That is,
|
||||
for a cell c, cell_centroids[dimensions*c + d] contains the
|
||||
d'th coordinate of c's centroid.
|
||||
The size of the array is equal to (dimensions*number_of_cells).
|
||||
*/
|
||||
double *cell_centroids;
|
||||
/**
|
||||
Exact or approximate cell volumes.
|
||||
The size of the array is equal to number_of_cells.
|
||||
*/
|
||||
double *cell_volumes;
|
||||
|
||||
|
||||
/**
|
||||
If non-null, this array contains the logical cartesian indices
|
||||
(in a lexicographic ordering) of each cell.
|
||||
In that case, the array size is equal to number_of_cells.
|
||||
|
||||
This field is intended for grids that have a (possibly
|
||||
degenerate) logical cartesian structure, for example
|
||||
cornerpoint grids.
|
||||
|
||||
If null, this indicates that the element indices coincide
|
||||
with the logical cartesian indices, _or_ that the grid has
|
||||
no inherent Cartesian structure. Due to this ambiguity, this
|
||||
field should not be used to check if the grid is Cartesian.
|
||||
*/
|
||||
int *global_cell;
|
||||
|
||||
/**
|
||||
Contains the size of the logical cartesian structure (if any) of the grid.
|
||||
|
||||
This field is intended for grids that have a (possibly
|
||||
degenerate) logical cartesian structure, for example
|
||||
cornerpoint grids.
|
||||
|
||||
If the grid is unstructured (non-Cartesian), then at least one
|
||||
of the items in the (sub-)array cartdims[0..dimensions-1]
|
||||
_could_ have the value 0 to signal this.
|
||||
*/
|
||||
int cartdims[3];
|
||||
/**
|
||||
If non-null, this array contains a number for cell-face
|
||||
adjacency indicating the face's position with respect to the
|
||||
cell, in a logical cartesian sense. The tags are in [0, ..., 5]
|
||||
meaning [I-, I+, J-, J+, K-, K+], where I, J, K are the logical
|
||||
cartesian principal directions.
|
||||
The structure of this array is identical to cell_faces, and
|
||||
cell_facepos indices into cell_facetag as well.
|
||||
|
||||
If non-null, the array size is equal to
|
||||
cell_facepos[number_of_cells].
|
||||
|
||||
This field is intended for grids that have a (possibly
|
||||
degenerate) logical cartesian structure, for example
|
||||
cornerpoint grids.
|
||||
*/
|
||||
int *cell_facetag;
|
||||
};
|
||||
|
||||
/**
|
||||
Destroy and deallocate an UnstructuredGrid and all its data.
|
||||
|
||||
This function assumes that all arrays of the UnstructuredGrid (if
|
||||
non-null) have been individually allocated by malloc(). They will
|
||||
be deallocated with free().
|
||||
*/
|
||||
void destroy_grid(struct UnstructuredGrid *g);
|
||||
|
||||
/**
|
||||
Allocate and initialise an empty UnstructuredGrid.
|
||||
|
||||
This is the moral equivalent of a C++ default constructor.
|
||||
|
||||
\return Fully formed UnstructuredGrid with all fields zero or
|
||||
<code>NULL</code> as appropriate. <code>NULL</code> in case of
|
||||
allocation failure.
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
create_grid_empty(void);
|
||||
|
||||
/**
|
||||
Allocate and initialise an UnstructuredGrid where pointers are set
|
||||
to location with correct size.
|
||||
|
||||
\param[in] ndims Number of physical dimensions.
|
||||
\param[in] ncells Number of cells.
|
||||
\param[in] nfaces Number of faces.
|
||||
\param[in] nfacenodes Size of mapping from faces to nodes.
|
||||
\param[in] ncellfaces Size of mapping from cells to faces.
|
||||
(i.e., the number of `half-faces')
|
||||
\param[in] nnodes Number of Nodes.
|
||||
|
||||
\return Fully formed UnstructuredGrid with all fields except
|
||||
<code>global_cell</code> allocated, but not filled with meaningful
|
||||
values. <code>NULL</code> in case of allocation failure.
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
allocate_grid(size_t ndims ,
|
||||
size_t ncells ,
|
||||
size_t nfaces ,
|
||||
size_t nfacenodes,
|
||||
size_t ncellfaces,
|
||||
size_t nnodes );
|
||||
|
||||
|
||||
/**
|
||||
* Import a grid from a character representation stored in file.
|
||||
*
|
||||
* @param[in] fname File name.
|
||||
* @return Fully formed UnstructuredGrid with all fields allocated and filled.
|
||||
* Returns @c NULL in case of allocation failure.
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
read_grid(const char *fname);
|
||||
|
||||
|
||||
|
||||
|
||||
bool
|
||||
grid_equal(const struct UnstructuredGrid * grid1 , const struct UnstructuredGrid * grid2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_GRID_HEADER_INCLUDED */
|
|
@ -1,260 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_CELLQUADRATURE_HEADER_INCLUDED
|
||||
#define OPM_CELLQUADRATURE_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
namespace {
|
||||
/// Calculates the determinant of a 3 x 3 matrix, represented as
|
||||
/// three three-dimensional arrays.
|
||||
inline double determinantOf(const double* a0,
|
||||
const double* a1,
|
||||
const double* a2)
|
||||
{
|
||||
return
|
||||
a0[0] * (a1[1] * a2[2] - a2[1] * a1[2]) -
|
||||
a0[1] * (a1[0] * a2[2] - a2[0] * a1[2]) +
|
||||
a0[2] * (a1[0] * a2[1] - a2[0] * a1[1]);
|
||||
}
|
||||
|
||||
/// Computes the volume of a tetrahedron consisting of 4 vertices
|
||||
/// with 3-dimensional coordinates
|
||||
inline double tetVolume(const double* p0,
|
||||
const double* p1,
|
||||
const double* p2,
|
||||
const double* p3)
|
||||
{
|
||||
double a[3] = { p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] };
|
||||
double b[3] = { p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2] };
|
||||
double c[3] = { p3[0] - p0[0], p3[1] - p0[1], p3[2] - p0[2] };
|
||||
return std::fabs(determinantOf(a, b, c) / 6.0);
|
||||
}
|
||||
|
||||
/// Calculates the area of a triangle consisting of 3 vertices
|
||||
/// with 2-dimensional coordinates
|
||||
inline double triangleArea2d(const double* p0,
|
||||
const double* p1,
|
||||
const double* p2)
|
||||
{
|
||||
double a[2] = { p1[0] - p0[0], p1[1] - p0[1] };
|
||||
double b[2] = { p2[0] - p0[0], p2[1] - p0[1] };
|
||||
double a_cross_b = a[0]*b[1] - a[1]*b[0];
|
||||
return 0.5*std::fabs(a_cross_b);
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
/// A class providing numerical quadrature for cells.
|
||||
/// In general: \int_{cell} g(x) dx = \sum_{i=0}^{n-1} w_i g(x_i).
|
||||
/// Note that this class does multiply weights by cell volume,
|
||||
/// so weights always sum to cell volume.
|
||||
///
|
||||
/// Degree 1 method:
|
||||
/// Midpoint (centroid) method.
|
||||
/// n = 1, w_0 = cell volume, x_0 = cell centroid
|
||||
///
|
||||
/// Degree 2 method for 2d (but see the note):
|
||||
/// Based on subdivision of the cell into triangles,
|
||||
/// with the centroid as a common vertex, and the triangle
|
||||
/// edge midpoint rule.
|
||||
/// Triangle i consists of the centroid C, nodes N_i and N_{i+1}.
|
||||
/// Its area is A_i.
|
||||
/// n = 2 * nn (nn = num nodes in face)
|
||||
/// For i = 0..(nn-1):
|
||||
/// w_i = 1/3 A_i.
|
||||
/// w_{nn+i} = 1/3 A_{i-1} + 1/3 A_i
|
||||
/// x_i = (N_i + N_{i+1})/2
|
||||
/// x_{nn+i} = (C + N_i)/2
|
||||
/// All N and A indices are interpreted cyclic, modulus nn.
|
||||
/// Note: for simplicity of implementation, we currently use
|
||||
/// n = 3 * nn
|
||||
/// For i = 0..(nn-1):
|
||||
/// w_{3*i + {0,1,2}} = 1/3 A_i
|
||||
/// x_{3*i} = (N_i + N_{i+1})/2
|
||||
/// x_{3*i + {1,2}} = (C + N_{i,i+1})/2
|
||||
/// This is simpler, because we can implement it easily
|
||||
/// based on iteration over faces without requiring any
|
||||
/// particular (cyclic) ordering.
|
||||
///
|
||||
/// Degree 2 method for 3d:
|
||||
/// Based on subdivision of each cell face into triangles
|
||||
/// with the face centroid as a common vertex, and then
|
||||
/// subdividing the cell into tetrahedra with the cell
|
||||
/// centroid as a common vertex. Then apply the tetrahedron
|
||||
/// rule with the following 4 nodes (uniform weights):
|
||||
/// a = 0.138196601125010515179541316563436
|
||||
/// x_i has all barycentric coordinates = a, except for
|
||||
/// the i'th coordinate which is = 1 - 3a.
|
||||
/// This rule is from http://nines.cs.kuleuven.be/ecf,
|
||||
/// it is the second degree 2 4-point rule for tets,
|
||||
/// referenced to Stroud(1971).
|
||||
/// The tetrahedra are numbered T_{i,j}, and are given by the
|
||||
/// cell centroid C, the face centroid FC_i, and two nodes
|
||||
/// of face i: FN_{i,j}, FN_{i,j+1}.
|
||||
class CellQuadrature
|
||||
{
|
||||
public:
|
||||
CellQuadrature(const UnstructuredGrid& grid,
|
||||
const int cell,
|
||||
const int degree)
|
||||
: grid_(grid), cell_(cell), degree_(degree)
|
||||
{
|
||||
if (grid.dimensions > 3) {
|
||||
OPM_THROW(std::runtime_error, "CellQuadrature only implemented for up to 3 dimensions.");
|
||||
}
|
||||
if (degree > 2) {
|
||||
OPM_THROW(std::runtime_error, "CellQuadrature exact for polynomial degrees > 1 not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
int numQuadPts() const
|
||||
{
|
||||
if (degree_ < 2 || grid_.dimensions == 1) {
|
||||
return 1;
|
||||
}
|
||||
// Degree 2 case.
|
||||
if (grid_.dimensions == 2) {
|
||||
return 3*(grid_.cell_facepos[cell_ + 1] - grid_.cell_facepos[cell_]);
|
||||
}
|
||||
assert(grid_.dimensions == 3);
|
||||
int sumnodes = 0;
|
||||
for (int hf = grid_.cell_facepos[cell_]; hf < grid_.cell_facepos[cell_ + 1]; ++hf) {
|
||||
const int face = grid_.cell_faces[hf];
|
||||
sumnodes += grid_.face_nodepos[face + 1] - grid_.face_nodepos[face];
|
||||
}
|
||||
return 4*sumnodes;
|
||||
}
|
||||
|
||||
void quadPtCoord(const int index, double* coord) const
|
||||
{
|
||||
const int dim = grid_.dimensions;
|
||||
const double* cc = grid_.cell_centroids + dim*cell_;
|
||||
if (degree_ < 2) {
|
||||
std::copy(cc, cc + dim, coord);
|
||||
return;
|
||||
}
|
||||
// Degree 2 case.
|
||||
if (dim == 2) {
|
||||
if (index % 3 == 0) {
|
||||
// Boundary midpoint. This is the face centroid.
|
||||
const int hface = grid_.cell_facepos[cell_] + index/3;
|
||||
const int face = grid_.cell_faces[hface];
|
||||
const double* fc = grid_.face_centroids + dim*face;
|
||||
std::copy(fc, fc + dim, coord);
|
||||
} else {
|
||||
// Interiour midpoint. This is the average of the
|
||||
// cell centroid and a face node (they should
|
||||
// always have two nodes in 2d).
|
||||
const int hface = grid_.cell_facepos[cell_] + index/3;
|
||||
const int face = grid_.cell_faces[hface];
|
||||
const int nodeoff = (index % 3) - 1; // == 0 or 1
|
||||
const int node = grid_.face_nodes[grid_.face_nodepos[face] + nodeoff];
|
||||
const double* nc = grid_.node_coordinates + dim*node;
|
||||
for (int dd = 0; dd < dim; ++dd) {
|
||||
coord[dd] = 0.5*(nc[dd] + cc[dd]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
assert(dim == 3);
|
||||
int tetindex = index / 4;
|
||||
const int subindex = index % 4;
|
||||
const double* nc = grid_.node_coordinates;
|
||||
for (int hf = grid_.cell_facepos[cell_]; hf < grid_.cell_facepos[cell_ + 1]; ++hf) {
|
||||
const int face = grid_.cell_faces[hf];
|
||||
const int nfn = grid_.face_nodepos[face + 1] - grid_.face_nodepos[face];
|
||||
if (nfn <= tetindex) {
|
||||
// Our tet is not associated with this face.
|
||||
tetindex -= nfn;
|
||||
continue;
|
||||
}
|
||||
const double* fc = grid_.face_centroids + dim*face;
|
||||
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face];
|
||||
const int node0 = fnodes[tetindex];
|
||||
const int node1 = fnodes[(tetindex + 1) % nfn];
|
||||
const double* n0c = nc + dim*node0;
|
||||
const double* n1c = nc + dim*node1;
|
||||
const double a = 0.138196601125010515179541316563436;
|
||||
// Barycentric coordinates of our point in the tet.
|
||||
double baryc[4] = { a, a, a, a };
|
||||
baryc[subindex] = 1.0 - 3.0*a;
|
||||
for (int dd = 0; dd < dim; ++dd) {
|
||||
coord[dd] = baryc[0]*cc[dd] + baryc[1]*fc[dd] + baryc[2]*n0c[dd] + baryc[3]*n1c[dd];
|
||||
}
|
||||
return;
|
||||
}
|
||||
OPM_THROW(std::runtime_error, "Should never reach this point.");
|
||||
}
|
||||
|
||||
double quadPtWeight(const int index) const
|
||||
{
|
||||
if (degree_ < 2) {
|
||||
return grid_.cell_volumes[cell_];
|
||||
}
|
||||
// Degree 2 case.
|
||||
const int dim = grid_.dimensions;
|
||||
const double* cc = grid_.cell_centroids + dim*cell_;
|
||||
if (dim == 2) {
|
||||
const int hface = grid_.cell_facepos[cell_] + index/3;
|
||||
const int face = grid_.cell_faces[hface];
|
||||
const int* nptr = grid_.face_nodes + grid_.face_nodepos[face];
|
||||
const double* nc0 = grid_.node_coordinates + dim*nptr[0];
|
||||
const double* nc1 = grid_.node_coordinates + dim*nptr[1];
|
||||
return triangleArea2d(nc0, nc1, cc)/3.0;
|
||||
}
|
||||
assert(dim == 3);
|
||||
int tetindex = index / 4;
|
||||
const double* nc = grid_.node_coordinates;
|
||||
for (int hf = grid_.cell_facepos[cell_]; hf < grid_.cell_facepos[cell_ + 1]; ++hf) {
|
||||
const int face = grid_.cell_faces[hf];
|
||||
const int nfn = grid_.face_nodepos[face + 1] - grid_.face_nodepos[face];
|
||||
if (nfn <= tetindex) {
|
||||
// Our tet is not associated with this face.
|
||||
tetindex -= nfn;
|
||||
continue;
|
||||
}
|
||||
const double* fc = grid_.face_centroids + dim*face;
|
||||
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face];
|
||||
const int node0 = fnodes[tetindex];
|
||||
const int node1 = fnodes[(tetindex + 1) % nfn];
|
||||
const double* n0c = nc + dim*node0;
|
||||
const double* n1c = nc + dim*node1;
|
||||
return 0.25*tetVolume(cc, fc, n0c, n1c);
|
||||
}
|
||||
OPM_THROW(std::runtime_error, "Should never reach this point.");
|
||||
}
|
||||
|
||||
private:
|
||||
const UnstructuredGrid& grid_;
|
||||
const int cell_;
|
||||
const int degree_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_CELLQUADRATURE_HEADER_INCLUDED
|
|
@ -1,122 +0,0 @@
|
|||
#include <opm/core/grid.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
namespace {
|
||||
|
||||
/// Helper struct for extractColumn
|
||||
/// Compares the underlying k-index
|
||||
struct ExtractColumnCompare
|
||||
{
|
||||
ExtractColumnCompare(const UnstructuredGrid& g)
|
||||
: grid(g)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
bool operator()(const int i, const int j)
|
||||
{
|
||||
// Extract k-index
|
||||
int index_i = grid.global_cell ? grid.global_cell[i] : i;
|
||||
int k_i = index_i / grid.cartdims[0] / grid.cartdims[1];
|
||||
int index_j = grid.global_cell ? grid.global_cell[j] : j;
|
||||
int k_j = index_j / grid.cartdims[0] / grid.cartdims[1];
|
||||
|
||||
return k_i < k_j;
|
||||
}
|
||||
|
||||
const UnstructuredGrid& grid;
|
||||
};
|
||||
|
||||
|
||||
/// Neighbourhood query.
|
||||
/// \return true if two cells are neighbours.
|
||||
bool neighbours(const UnstructuredGrid& grid, const int c0, const int c1)
|
||||
{
|
||||
for (int hf = grid.cell_facepos[c0]; hf < grid.cell_facepos[c0 + 1]; ++hf) {
|
||||
const int f = grid.cell_faces[hf];
|
||||
if (grid.face_cells[2*f] == c1 || grid.face_cells[2*f+1] == c1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
/// Extract each column of the grid.
|
||||
/// \note Assumes the pillars of the grid are all vertically aligned.
|
||||
/// \param grid The grid from which to extract the columns.
|
||||
/// \param columns will for each (i, j) where (i, j) represents a non-empty column,
|
||||
//// contain the cell indices contained in the column
|
||||
/// centered at (i, j) in the second variable, and i+jN in the first variable.
|
||||
inline void extractColumn( const UnstructuredGrid& grid, std::vector<std::vector<int> >& columns )
|
||||
{
|
||||
const int* dims = grid.cartdims;
|
||||
|
||||
// Keeps track of column_index ---> index of vector
|
||||
std::map<int, int> global_to_local;
|
||||
for (int cell = 0; cell < grid.number_of_cells; ++cell) {
|
||||
// Extract Cartesian coordinates
|
||||
int index = grid.global_cell ? grid.global_cell[cell] : cell; // If null, assume mapping is identity.
|
||||
int i_cart = index % dims[0];
|
||||
int k_cart = index / dims[0] / dims[1];
|
||||
int j_cart = (index - k_cart*dims[0]*dims[1])/ dims[0];
|
||||
|
||||
int local_index;
|
||||
std::map<int, int>::iterator local_index_iterator = global_to_local.find(i_cart+j_cart*dims[0]);
|
||||
if (local_index_iterator != global_to_local.end()) {
|
||||
local_index = local_index_iterator->second;
|
||||
} else {
|
||||
local_index = columns.size();
|
||||
global_to_local[i_cart+j_cart*dims[0]] = local_index;
|
||||
columns.push_back(std::vector<int>());
|
||||
}
|
||||
columns[local_index].push_back(cell);
|
||||
}
|
||||
|
||||
int num_cols = columns.size();
|
||||
for (int col = 0; col < num_cols; ++col) {
|
||||
std::sort(columns[col].begin(), columns[col].end(), ExtractColumnCompare(grid));
|
||||
}
|
||||
|
||||
// At this point, a column may contain multiple disjoint sets of cells.
|
||||
// We must split these columns into connected parts.
|
||||
std::vector< std::vector<int> > new_columns;
|
||||
for (int col = 0; col < num_cols; ++col) {
|
||||
const int colsz = columns[col].size();
|
||||
int first_of_col = 0;
|
||||
for (int k = 1; k < colsz; ++k) {
|
||||
const int c0 = columns[col][k-1];
|
||||
const int c1 = columns[col][k];
|
||||
if (!neighbours(grid, c0, c1)) {
|
||||
// Must split. Move the cells [first_of_col, ... , k-1] to
|
||||
// a new column, known to be connected.
|
||||
new_columns.push_back(std::vector<int>());
|
||||
new_columns.back().assign(columns[col].begin() + first_of_col, columns[col].begin() + k);
|
||||
// The working column now starts with index k.
|
||||
first_of_col = k;
|
||||
}
|
||||
}
|
||||
if (first_of_col != 0) {
|
||||
// The column was split, the working part should be
|
||||
// the entire column. We erase the cells before first_of_col.
|
||||
// (Could be more efficient if we instead chop off end.)
|
||||
columns[col].erase(columns[col].begin(), columns[col].begin() + first_of_col);
|
||||
}
|
||||
}
|
||||
|
||||
// Must tack on the new columns to complete the set.
|
||||
const int num_cols_all = num_cols + new_columns.size();
|
||||
columns.resize(num_cols_all);
|
||||
for (int col = num_cols; col < num_cols_all; ++col) {
|
||||
columns[col].swap(new_columns[col - num_cols]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Opm
|
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_FACEQUADRATURE_HEADER_INCLUDED
|
||||
#define OPM_FACEQUADRATURE_HEADER_INCLUDED
|
||||
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
namespace {
|
||||
/// Calculates the cross product of two 3-vectors, represented as
|
||||
/// 3-element arrays. Calculates res = a X b. The res array must
|
||||
/// already be allocated with room for 3 elements.
|
||||
inline void cross(const double* a, const double* b, double* res)
|
||||
{
|
||||
res[0] = a[1]*b[2] - a[2]*b[1];
|
||||
res[1] = a[2]*b[0] - a[0]*b[2];
|
||||
res[2] = a[0]*b[1] - a[1]*b[0];
|
||||
}
|
||||
|
||||
/// Calculates the area of a triangle consisting of 3 vertices
|
||||
/// with 3-dimensional coordinates
|
||||
inline double triangleArea3d(const double* p0,
|
||||
const double* p1,
|
||||
const double* p2)
|
||||
{
|
||||
double a[3] = { p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] };
|
||||
double b[3] = { p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2] };
|
||||
double cr[3];
|
||||
cross(a, b, cr);
|
||||
return 0.5*std::sqrt(cr[0]*cr[0] + cr[1]*cr[1] + cr[2]*cr[2]);
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
/// A class providing numerical quadrature for faces.
|
||||
/// In general: \int_{face} g(x) dx = \sum_{i=0}^{n-1} w_i g(x_i).
|
||||
/// Note that this class does multiply weights by face area,
|
||||
/// so weights always sum to face area.
|
||||
///
|
||||
/// Degree 1 method:
|
||||
/// Midpoint (centroid) method.
|
||||
/// n = 1, w_0 = face area, x_0 = face centroid
|
||||
///
|
||||
/// Degree 2 method for 2d:
|
||||
/// Simpson's method (actually this is degree 3).
|
||||
///
|
||||
/// Degree 2 method for 3d:
|
||||
/// Based on subdivision of the face into triangles,
|
||||
/// with the centroid as a common vertex, and the triangle
|
||||
/// edge midpoint rule.
|
||||
/// Triangle i consists of the centroid C, nodes N_i and N_{i+1}.
|
||||
/// Its area is A_i.
|
||||
/// n = 2 * nn (nn = num nodes in face)
|
||||
/// For i = 0..(nn-1):
|
||||
/// w_i = 1/3 A_i.
|
||||
/// w_{nn+i} = 1/3 A_{i-1} + 1/3 A_i
|
||||
/// x_i = (N_i + N_{i+1})/2
|
||||
/// x_{nn+i} = (C + N_i)/2
|
||||
/// All N and A indices are interpreted cyclic, modulus nn.
|
||||
class FaceQuadrature
|
||||
{
|
||||
public:
|
||||
FaceQuadrature(const UnstructuredGrid& grid,
|
||||
const int face,
|
||||
const int degree)
|
||||
: grid_(grid), face_(face), degree_(degree)
|
||||
{
|
||||
if (grid_.dimensions > 3) {
|
||||
OPM_THROW(std::runtime_error, "FaceQuadrature only implemented for up to 3 dimensions.");
|
||||
}
|
||||
if (degree_ > 2) {
|
||||
OPM_THROW(std::runtime_error, "FaceQuadrature exact for polynomial degrees > 2 not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
int numQuadPts() const
|
||||
{
|
||||
if (degree_ < 2 || grid_.dimensions < 2) {
|
||||
return 1;
|
||||
}
|
||||
// Degree 2 case.
|
||||
if (grid_.dimensions == 2) {
|
||||
return 3;
|
||||
} else {
|
||||
return 2 * (grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_]);
|
||||
}
|
||||
}
|
||||
|
||||
void quadPtCoord(const int index, double* coord) const
|
||||
{
|
||||
const int dim = grid_.dimensions;
|
||||
const double* fc = grid_.face_centroids + dim*face_;
|
||||
if (degree_ < 2 || dim < 2) {
|
||||
std::copy(fc, fc + dim, coord);
|
||||
return;
|
||||
}
|
||||
// Degree 2 case.
|
||||
const int nn = grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_];
|
||||
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face_];
|
||||
const double* nc = grid_.node_coordinates;
|
||||
if (dim == 2) {
|
||||
assert(nn == 2);
|
||||
const double* pa[3] = { nc + dim*fnodes[0], fc, nc + dim*fnodes[1] };
|
||||
std::copy(pa[index], pa[index] + dim, coord);
|
||||
return;
|
||||
}
|
||||
assert(dim == 3);
|
||||
if (index < nn) {
|
||||
// Boundary edge midpoint.
|
||||
const int node0 = fnodes[index];
|
||||
const int node1 = fnodes[(index + 1)%nn];
|
||||
for (int dd = 0; dd < dim; ++dd) {
|
||||
coord[dd] = 0.5*(nc[dim*node0 + dd] + nc[dim*node1 + dd]);
|
||||
}
|
||||
} else {
|
||||
// Interiour edge midpoint.
|
||||
// Recall that index is now in [nn, 2*nn).
|
||||
const int node = fnodes[index - nn];
|
||||
for (int dd = 0; dd < dim; ++dd) {
|
||||
coord[dd] = 0.5*(nc[dim*node + dd] + fc[dd]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double quadPtWeight(const int index) const
|
||||
{
|
||||
if (degree_ < 2) {
|
||||
return grid_.face_areas[face_];
|
||||
}
|
||||
// Degree 2 case.
|
||||
const int dim = grid_.dimensions;
|
||||
if (dim == 2) {
|
||||
const double simpsonw[3] = { 1.0/6.0, 4.0/6.0, 1.0/6.0 };
|
||||
return grid_.face_areas[face_]*simpsonw[index];
|
||||
}
|
||||
assert(dim == 3);
|
||||
const double* fc = grid_.face_centroids + dim*face_;
|
||||
const int nn = grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_];
|
||||
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face_];
|
||||
const double* nc = grid_.node_coordinates;
|
||||
if (index < nn) {
|
||||
// Boundary edge midpoint.
|
||||
const int node0 = fnodes[index];
|
||||
const int node1 = fnodes[(index + 1)%nn];
|
||||
const double area = triangleArea3d(nc + dim*node1, nc + dim*node0, fc);
|
||||
return area / 3.0;
|
||||
} else {
|
||||
// Interiour edge midpoint.
|
||||
// Recall that index is now in [nn, 2*nn).
|
||||
const int node0 = fnodes[(index - 1) % nn];
|
||||
const int node1 = fnodes[index - nn];
|
||||
const int node2 = fnodes[(index + 1) % nn];
|
||||
const double area0 = triangleArea3d(nc + dim*node1, nc + dim*node0, fc);
|
||||
const double area1 = triangleArea3d(nc + dim*node2, nc + dim*node1, fc);
|
||||
return (area0 + area1) / 3.0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const UnstructuredGrid& grid_;
|
||||
const int face_;
|
||||
const int degree_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_FACEQUADRATURE_HEADER_INCLUDED
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
Copyright 2014, 2015 Dr. Markus Blatt - HPC-Simulation-Software & Services.
|
||||
Copyright 2014 Statoil AS
|
||||
Copyright 2015 NTNU
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <opm/core/grid/GridHelpers.hpp>
|
||||
namespace Opm
|
||||
{
|
||||
namespace UgGridHelpers
|
||||
{
|
||||
int numCells(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.number_of_cells;
|
||||
}
|
||||
|
||||
int numFaces(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.number_of_faces;
|
||||
}
|
||||
int dimensions(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.dimensions;
|
||||
}
|
||||
int numCellFaces(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.cell_facepos[grid.number_of_cells];
|
||||
}
|
||||
|
||||
const int* globalCell(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.global_cell;
|
||||
}
|
||||
|
||||
const int* cartDims(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.cartdims;
|
||||
}
|
||||
|
||||
const double* beginCellCentroids(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.cell_centroids;
|
||||
}
|
||||
|
||||
double cellCenterDepth(const UnstructuredGrid& grid, int cell_index)
|
||||
{
|
||||
// This method is an alternative to the method cellCentroidCoordinate(...) below.
|
||||
// The cell center depth is computed as a raw average of cell corner depths.
|
||||
// For cornerpoint grids, this is likely to give slightly different depths that seem
|
||||
// to agree with eclipse.
|
||||
assert(grid.dimensions == 3);
|
||||
const int nd = 3; // Assuming 3-dimensional grid ...
|
||||
const int nv = 8; // Assuming 2*4 vertices ...
|
||||
double zz = 0.0;
|
||||
// Traverse the bottom and top cell-face
|
||||
for (int i=grid.cell_facepos[cell_index+1]-2; i<grid.cell_facepos[cell_index+1]; ++i) {
|
||||
// Traverse the vertices associated with each face
|
||||
assert(grid.face_nodepos[grid.cell_faces[i]+1] - grid.face_nodepos[grid.cell_faces[i]] == nv/2);
|
||||
for (int j=grid.face_nodepos[grid.cell_faces[i]]; j<grid.face_nodepos[grid.cell_faces[i]+1]; ++j) {
|
||||
zz += (grid.node_coordinates+nd*(grid.face_nodes[j]))[nd-1];
|
||||
}
|
||||
}
|
||||
return zz/nv;
|
||||
}
|
||||
|
||||
double cellCentroidCoordinate(const UnstructuredGrid& grid, int cell_index,
|
||||
int coordinate)
|
||||
{
|
||||
return grid.cell_centroids[grid.dimensions*cell_index+coordinate];
|
||||
}
|
||||
|
||||
const double*
|
||||
cellCentroid(const UnstructuredGrid& grid, int cell_index)
|
||||
{
|
||||
return grid.cell_centroids+(cell_index*grid.dimensions);
|
||||
}
|
||||
|
||||
const double* beginCellVolumes(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.cell_volumes;
|
||||
}
|
||||
const double* endCellVolumes(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.cell_volumes+numCells(grid);
|
||||
}
|
||||
|
||||
const double* beginFaceCentroids(const UnstructuredGrid& grid)
|
||||
{
|
||||
return grid.face_centroids;
|
||||
}
|
||||
|
||||
const double* faceCentroid(const UnstructuredGrid& grid, int face_index)
|
||||
{
|
||||
return grid.face_centroids+face_index*grid.dimensions;
|
||||
}
|
||||
|
||||
const double* faceNormal(const UnstructuredGrid& grid, int face_index)
|
||||
{
|
||||
return grid.face_normals+face_index*grid.dimensions;
|
||||
}
|
||||
|
||||
double faceArea(const UnstructuredGrid& grid, int face_index)
|
||||
{
|
||||
return grid.face_areas[face_index];
|
||||
}
|
||||
|
||||
int faceTag(const UnstructuredGrid& grid,
|
||||
boost::iterator_range<const int*>::const_iterator face)
|
||||
{
|
||||
return grid.cell_facetag[face-cell2Faces(grid)[0].begin()];
|
||||
}
|
||||
|
||||
SparseTableView cell2Faces(const UnstructuredGrid& grid)
|
||||
{
|
||||
return SparseTableView(grid.cell_faces, grid.cell_facepos, numCells(grid));
|
||||
}
|
||||
|
||||
SparseTableView face2Vertices(const UnstructuredGrid& grid)
|
||||
{
|
||||
return SparseTableView(grid.face_nodes, grid.face_nodepos, numFaces(grid));
|
||||
}
|
||||
|
||||
const double* vertexCoordinates(const UnstructuredGrid& grid, int index)
|
||||
{
|
||||
return grid.node_coordinates+dimensions(grid)*index;
|
||||
}
|
||||
|
||||
double cellVolume(const UnstructuredGrid& grid, int cell_index)
|
||||
{
|
||||
return grid.cell_volumes[cell_index];
|
||||
}
|
||||
|
||||
FaceCellTraits<UnstructuredGrid>::Type faceCells(const UnstructuredGrid& grid)
|
||||
{
|
||||
return FaceCellsProxy(grid);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,355 +0,0 @@
|
|||
/*
|
||||
Copyright 2014, 2015 Dr. Markus Blatt - HPC-Simulation-Software & Services
|
||||
Copyright 2014 Statoil AS
|
||||
Copyright 2015
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef OPM_CORE_GRIDHELPERS_HEADER_INCLUDED
|
||||
#define OPM_CORE_GRIDHELPERS_HEADER_INCLUDED
|
||||
#include <opm/core/grid.h>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
namespace UgGridHelpers
|
||||
{
|
||||
|
||||
/// \brief Allows viewing a sparse table consisting out of C-array
|
||||
///
|
||||
/// This class can be used to convert two int array (like they are
|
||||
/// in UnstructuredGrid for representing the cell to faces mapping
|
||||
/// as a sparse table object.
|
||||
class SparseTableView
|
||||
{
|
||||
public:
|
||||
class IntRange : public boost::iterator_range<const int*>
|
||||
{
|
||||
public:
|
||||
typedef boost::iterator_range<const int*> BaseRowType;
|
||||
typedef BaseRowType::size_type size_type;
|
||||
typedef int value_type;
|
||||
|
||||
IntRange(const int* start_arg, const int* end_arg)
|
||||
: BaseRowType(start_arg, end_arg)
|
||||
{}
|
||||
};
|
||||
/// \brief The type of the roww.
|
||||
typedef boost::iterator_range<const int*> row_type;
|
||||
|
||||
/// \brief Creates a sparse table view
|
||||
/// \param data The array with data of the table.
|
||||
/// \param offset The offsets of the rows. Row i starts
|
||||
/// at offset[i] and ends a offset[i+1]
|
||||
/// \param size The number of entries/rows of the table
|
||||
SparseTableView(int* data, int *offset, std::size_t size_arg)
|
||||
: data_(data), offset_(offset), size_(size_arg)
|
||||
{}
|
||||
|
||||
/// \brief Get a row of the the table.
|
||||
/// \param row The row index.
|
||||
/// \return The corresponding row.
|
||||
row_type operator[](std::size_t row) const
|
||||
{
|
||||
assert(row<=size());
|
||||
return row_type(data_ + offset_[row], data_ + offset_[row+1]);
|
||||
}
|
||||
|
||||
/// \brief Get the size of the table.
|
||||
/// \return the number rows.
|
||||
std::size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/// \brief Get the number of non-zero entries.
|
||||
std::size_t noEntries() const
|
||||
{
|
||||
return offset_[size_];
|
||||
}
|
||||
|
||||
private:
|
||||
/// \brief The array with data of the table.
|
||||
const int* data_;
|
||||
/// \brief offset The offsets of the rows.
|
||||
///
|
||||
/// Row i starts at offset[i] and ends a offset[i+1]
|
||||
const int* offset_;
|
||||
/// \brief The size, i.e. the number of rows.
|
||||
std::size_t size_;
|
||||
};
|
||||
|
||||
/// \brief Get the number of cells of a grid.
|
||||
int numCells(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the number of faces of a grid.
|
||||
int numFaces(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the dimensions of a grid
|
||||
int dimensions(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the number of faces, where each face counts as many times as there are adjacent faces
|
||||
int numCellFaces(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the cartesion dimension of the underlying structured grid.
|
||||
const int* cartDims(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the local to global index mapping.
|
||||
///
|
||||
/// The global index is the index of the active cell
|
||||
/// in the underlying structured grid.
|
||||
const int* globalCell(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Traits of the cell centroids of a grid.
|
||||
///
|
||||
/// This class exports two types: IteratorType, the type of the iterator
|
||||
/// over the cell centroids, and the ValueTpe, the type of the cell centroid.
|
||||
/// \tpatam G The type of the grid.
|
||||
template<class G>
|
||||
struct CellCentroidTraits
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct CellCentroidTraits<UnstructuredGrid>
|
||||
{
|
||||
typedef const double* IteratorType;
|
||||
typedef const double* ValueType;
|
||||
};
|
||||
|
||||
/// \brief Get an iterator over the cell centroids positioned at the first cell.
|
||||
///
|
||||
/// The return type needs to be usable with the functions increment, and
|
||||
/// getCoordinate.
|
||||
CellCentroidTraits<UnstructuredGrid>::IteratorType
|
||||
beginCellCentroids(const UnstructuredGrid& grid);
|
||||
|
||||
|
||||
/// \brief Get vertical position of cell center ("zcorn" average.)
|
||||
/// \brief grid The grid.
|
||||
/// \brief cell_index The index of the specific cell.
|
||||
double cellCenterDepth(const UnstructuredGrid& grid, int cell_index);
|
||||
|
||||
|
||||
/// \brief Get a coordinate of a specific cell centroid.
|
||||
/// \brief grid The grid.
|
||||
/// \brief cell_index The index of the specific cell.
|
||||
/// \breif coordinate The coordinate index.
|
||||
double cellCentroidCoordinate(const UnstructuredGrid& grid, int cell_index,
|
||||
int coordinate);
|
||||
|
||||
|
||||
/// \brief Get the centroid of a cell.
|
||||
/// \param grid The grid whose cell centroid we query.
|
||||
/// \param cell_index The index of the corresponding cell.
|
||||
const double* cellCentroid(const UnstructuredGrid& grid, int cell_index);
|
||||
|
||||
|
||||
/// \brief Get the volume of a cell.
|
||||
/// \param grid The grid the cell belongs to.
|
||||
/// \param cell_index The index of the cell.
|
||||
double cellVolume(const UnstructuredGrid& grid, int cell_index);
|
||||
|
||||
/// \brief The mapping of the grid type to type of the iterator over
|
||||
/// the cell volumes.
|
||||
///
|
||||
/// The value of the mapping is stored in nested type IteratorType
|
||||
/// \tparam T The type of the grid.
|
||||
template<class T>
|
||||
struct CellVolumeIteratorTraits
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct CellVolumeIteratorTraits<UnstructuredGrid>
|
||||
{
|
||||
typedef const double* IteratorType;
|
||||
};
|
||||
|
||||
/// \brief Get an iterator over the cell volumes of a grid positioned at the first cell.
|
||||
const double* beginCellVolumes(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get an iterator over the cell volumes of a grid positioned after the last cell.
|
||||
const double* endCellVolumes(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the cell centroid of a face.
|
||||
/// \param grid The grid whose cell centroid we query.
|
||||
/// \param face_index The index of the corresponding face.
|
||||
const double* faceCentroid(const UnstructuredGrid& grid, int face_index);
|
||||
|
||||
|
||||
/// \brief Traits of the face centroids of a grid.
|
||||
///
|
||||
/// This class exports two types: IteratorType, the type of the iterator
|
||||
/// over the face centroids, and the ValueTpe, the type of the face centroid.
|
||||
/// \tpatam G The type of the grid.
|
||||
template<class G>
|
||||
struct FaceCentroidTraits
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FaceCentroidTraits<UnstructuredGrid>
|
||||
{
|
||||
typedef const double* IteratorType;
|
||||
typedef const double* ValueType;
|
||||
};
|
||||
|
||||
/// \brief Get an iterator over the face centroids positioned at the first cell.
|
||||
FaceCentroidTraits<UnstructuredGrid>::IteratorType
|
||||
beginFaceCentroids(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get a coordinate of a specific face centroid.
|
||||
/// \param grid The grid.
|
||||
/// \param face_index The index of the specific face.
|
||||
/// \param coordinate The coordinate index.
|
||||
FaceCentroidTraits<UnstructuredGrid>::ValueType
|
||||
faceCentroid(const UnstructuredGrid& grid, int face_index);
|
||||
|
||||
/// \brief Get the normal of a face.
|
||||
/// \param grid The grid that the face is part of.
|
||||
/// \param face_index The index of the face in the grid.
|
||||
const double* faceNormal(const UnstructuredGrid& grid, int face_index);
|
||||
|
||||
/// \brief Get the area of a face
|
||||
/// \param grid The grid that the face is part of.
|
||||
/// \param face_index The index of the face in the grid.
|
||||
double faceArea(const UnstructuredGrid& grid, int face_index);
|
||||
|
||||
/// \brief Get Eclipse Cartesian tag of a face
|
||||
/// \param grid The grid that the face is part of.
|
||||
/// \param cell_face The face attached to a cell as obtained from cell2Faces()
|
||||
/// \return 0, 1, 2, 3, 4, 5 for I-, I+, J-, J+, K-, K+
|
||||
int faceTag(const UnstructuredGrid& grid, boost::iterator_range<const int*>::const_iterator cell_face);
|
||||
|
||||
/// \brief Maps the grid type to the associated type of the cell to faces mapping.
|
||||
///
|
||||
/// Provides a type named Type.
|
||||
/// \tparam T The type of the grid.
|
||||
template<class T>
|
||||
struct Cell2FacesTraits
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Cell2FacesTraits<UnstructuredGrid>
|
||||
{
|
||||
typedef SparseTableView Type;
|
||||
};
|
||||
|
||||
/// \brief Maps the grid type to the associated type of the face to vertices mapping.
|
||||
///
|
||||
/// Provides a type named Type.
|
||||
/// \tparam T The type of the grid.
|
||||
template<class T>
|
||||
struct Face2VerticesTraits
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Face2VerticesTraits<UnstructuredGrid>
|
||||
{
|
||||
typedef SparseTableView Type;
|
||||
};
|
||||
|
||||
/// \brief Get the cell to faces mapping of a grid.
|
||||
Cell2FacesTraits<UnstructuredGrid>::Type
|
||||
cell2Faces(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the face to vertices mapping of a grid.
|
||||
Face2VerticesTraits<UnstructuredGrid>::Type
|
||||
face2Vertices(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Get the coordinates of a vertex of the grid.
|
||||
/// \param grid The grid the vertex is part of.
|
||||
/// \param index The index identifying the vertex.
|
||||
const double* vertexCoordinates(const UnstructuredGrid& grid, int index);
|
||||
|
||||
class FaceCellsProxy
|
||||
{
|
||||
public:
|
||||
FaceCellsProxy(const UnstructuredGrid& grid)
|
||||
: face_cells_(grid.face_cells)
|
||||
{}
|
||||
int operator()(int face_index, int local_index) const
|
||||
{
|
||||
return face_cells_[2*face_index+local_index];
|
||||
}
|
||||
private:
|
||||
const int* face_cells_;
|
||||
};
|
||||
|
||||
/// \brief Traits of the face to attached cell mappping of a grid.
|
||||
///
|
||||
/// Exports the type Type, the type of the mapping
|
||||
/// \tparam T The type of the grid
|
||||
template<class T>
|
||||
struct FaceCellTraits
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct FaceCellTraits<UnstructuredGrid>
|
||||
{
|
||||
typedef FaceCellsProxy Type;
|
||||
};
|
||||
|
||||
/// \brief Get the face to cell mapping of a grid.
|
||||
FaceCellTraits<UnstructuredGrid>::Type faceCells(const UnstructuredGrid& grid);
|
||||
|
||||
/// \brief Increment an iterator over an array that reresents a dense row-major
|
||||
/// matrix with dims columns
|
||||
/// \param cc The iterator.
|
||||
/// \param i The nzumber of rows to increment
|
||||
/// \param dim The number of columns of the matrix.
|
||||
template<class T>
|
||||
T* increment(T* cc, int i, int dim)
|
||||
{
|
||||
return cc+(i*dim);
|
||||
}
|
||||
/// \brief Increment an iterator over an array that reresents a dense row-major
|
||||
/// matrix with dims columns
|
||||
/// \param cc The iterator.
|
||||
/// \param i The nzumber of rows to increment
|
||||
template<class T>
|
||||
T increment(const T& t, int i, int)
|
||||
{
|
||||
return t+i;
|
||||
}
|
||||
|
||||
/// \brief Get the i-th corrdinate of a centroid.
|
||||
/// \param cc The array with the coordinates.
|
||||
/// \param i The index of the coordinate.
|
||||
/// \tparam T The type of the coordinate of the centroid.
|
||||
template<class T>
|
||||
double getCoordinate(T* cc, int i)
|
||||
{
|
||||
return cc[i];
|
||||
}
|
||||
|
||||
/// \brief Get the i-th corrdinate of an array.
|
||||
/// \param t The iterator over the centroids
|
||||
/// \brief i The index of the coordinate.
|
||||
/// \tparam T The type of the iterator representing the centroid.
|
||||
/// Its value_type has to provide an operator[] to access the coordinates.
|
||||
template<class T>
|
||||
double getCoordinate(T t, int i)
|
||||
{
|
||||
return (*t)[i];
|
||||
}
|
||||
|
||||
} // end namespace UGGridHelpers
|
||||
} // end namespace OPM
|
||||
#endif
|
|
@ -1,232 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <opm/core/grid/GridManager.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/cart_grid.h>
|
||||
#include <opm/core/grid/cornerpoint_grid.h>
|
||||
#include <opm/core/grid/MinpvProcessor.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Construct a 3d corner-point grid from a deck.
|
||||
GridManager::GridManager(Opm::EclipseGridConstPtr eclipseGrid)
|
||||
: ug_(0)
|
||||
{
|
||||
initFromEclipseGrid(eclipseGrid, std::vector<double>());
|
||||
}
|
||||
|
||||
|
||||
GridManager::GridManager(Opm::DeckConstPtr deck)
|
||||
: ug_(0)
|
||||
{
|
||||
auto eclipseGrid = std::make_shared<const Opm::EclipseGrid>(deck);
|
||||
initFromEclipseGrid(eclipseGrid, std::vector<double>());
|
||||
}
|
||||
|
||||
|
||||
GridManager::GridManager(Opm::EclipseGridConstPtr eclipseGrid,
|
||||
const std::vector<double>& poreVolumes)
|
||||
: ug_(0)
|
||||
{
|
||||
initFromEclipseGrid(eclipseGrid, poreVolumes);
|
||||
}
|
||||
|
||||
|
||||
/// Construct a 2d cartesian grid with cells of unit size.
|
||||
GridManager::GridManager(int nx, int ny)
|
||||
{
|
||||
ug_ = create_grid_cart2d(nx, ny, 1.0, 1.0);
|
||||
if (!ug_) {
|
||||
OPM_THROW(std::runtime_error, "Failed to construct grid.");
|
||||
}
|
||||
}
|
||||
|
||||
GridManager::GridManager(int nx, int ny,double dx, double dy)
|
||||
{
|
||||
ug_ = create_grid_cart2d(nx, ny, dx, dy);
|
||||
if (!ug_) {
|
||||
OPM_THROW(std::runtime_error, "Failed to construct grid.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Construct a 3d cartesian grid with cells of unit size.
|
||||
GridManager::GridManager(int nx, int ny, int nz)
|
||||
{
|
||||
ug_ = create_grid_cart3d(nx, ny, nz);
|
||||
if (!ug_) {
|
||||
OPM_THROW(std::runtime_error, "Failed to construct grid.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// Construct a 3d cartesian grid with cells of size [dx, dy, dz].
|
||||
GridManager::GridManager(int nx, int ny, int nz,
|
||||
double dx, double dy, double dz)
|
||||
{
|
||||
ug_ = create_grid_hexa3d(nx, ny, nz, dx, dy, dz);
|
||||
if (!ug_) {
|
||||
OPM_THROW(std::runtime_error, "Failed to construct grid.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// Construct a grid from an input file.
|
||||
/// The file format used is currently undocumented,
|
||||
/// and is therefore only suited for internal use.
|
||||
GridManager::GridManager(const std::string& input_filename)
|
||||
{
|
||||
ug_ = read_grid(input_filename.c_str());
|
||||
if (!ug_) {
|
||||
OPM_THROW(std::runtime_error, "Failed to read grid from file " << input_filename);
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
GridManager::~GridManager()
|
||||
{
|
||||
destroy_grid(ug_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// Access the managed UnstructuredGrid.
|
||||
/// The method is named similarly to c_str() in std::string,
|
||||
/// to make it clear that we are returning a C-compatible struct.
|
||||
const UnstructuredGrid* GridManager::c_grid() const
|
||||
{
|
||||
return ug_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Construct corner-point grid from EclipseGrid.
|
||||
void GridManager::initFromEclipseGrid(Opm::EclipseGridConstPtr eclipseGrid,
|
||||
const std::vector<double>& poreVolumes)
|
||||
{
|
||||
struct grdecl g;
|
||||
std::vector<int> actnum;
|
||||
std::vector<double> coord;
|
||||
std::vector<double> zcorn;
|
||||
std::vector<double> mapaxes;
|
||||
|
||||
g.dims[0] = eclipseGrid->getNX();
|
||||
g.dims[1] = eclipseGrid->getNY();
|
||||
g.dims[2] = eclipseGrid->getNZ();
|
||||
|
||||
eclipseGrid->exportMAPAXES( mapaxes );
|
||||
eclipseGrid->exportCOORD( coord );
|
||||
eclipseGrid->exportZCORN( zcorn );
|
||||
eclipseGrid->exportACTNUM( actnum );
|
||||
|
||||
g.coord = coord.data();
|
||||
g.zcorn = zcorn.data();
|
||||
g.actnum = actnum.data();
|
||||
g.mapaxes = mapaxes.data();
|
||||
|
||||
if (!poreVolumes.empty() && (eclipseGrid->getMinpvMode() != MinpvMode::ModeEnum::Inactive)) {
|
||||
MinpvProcessor mp(g.dims[0], g.dims[1], g.dims[2]);
|
||||
const double minpv_value = eclipseGrid->getMinpvValue();
|
||||
mp.process(poreVolumes, minpv_value, actnum, zcorn.data());
|
||||
}
|
||||
|
||||
const double z_tolerance = eclipseGrid->isPinchActive() ?
|
||||
eclipseGrid->getPinchThresholdThickness() : 0.0;
|
||||
ug_ = create_grid_cornerpoint(&g, z_tolerance);
|
||||
if (!ug_) {
|
||||
OPM_THROW(std::runtime_error, "Failed to construct grid.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void GridManager::createGrdecl(Opm::DeckConstPtr deck, struct grdecl &grdecl)
|
||||
{
|
||||
// Extract data from deck.
|
||||
const std::vector<double>& zcorn = deck->getKeyword("ZCORN")->getSIDoubleData();
|
||||
const std::vector<double>& coord = deck->getKeyword("COORD")->getSIDoubleData();
|
||||
const int* actnum = NULL;
|
||||
if (deck->hasKeyword("ACTNUM")) {
|
||||
actnum = &(deck->getKeyword("ACTNUM")->getIntData()[0]);
|
||||
}
|
||||
|
||||
std::array<int, 3> dims;
|
||||
if (deck->hasKeyword("DIMENS")) {
|
||||
Opm::DeckKeywordConstPtr dimensKeyword = deck->getKeyword("DIMENS");
|
||||
dims[0] = dimensKeyword->getRecord(0)->getItem(0)->getInt(0);
|
||||
dims[1] = dimensKeyword->getRecord(0)->getItem(1)->getInt(0);
|
||||
dims[2] = dimensKeyword->getRecord(0)->getItem(2)->getInt(0);
|
||||
} else if (deck->hasKeyword("SPECGRID")) {
|
||||
Opm::DeckKeywordConstPtr specgridKeyword = deck->getKeyword("SPECGRID");
|
||||
dims[0] = specgridKeyword->getRecord(0)->getItem(0)->getInt(0);
|
||||
dims[1] = specgridKeyword->getRecord(0)->getItem(1)->getInt(0);
|
||||
dims[2] = specgridKeyword->getRecord(0)->getItem(2)->getInt(0);
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Deck must have either DIMENS or SPECGRID.");
|
||||
}
|
||||
|
||||
// Collect in input struct for preprocessing.
|
||||
|
||||
grdecl.zcorn = &zcorn[0];
|
||||
grdecl.coord = &coord[0];
|
||||
grdecl.actnum = actnum;
|
||||
grdecl.dims[0] = dims[0];
|
||||
grdecl.dims[1] = dims[1];
|
||||
grdecl.dims[2] = dims[2];
|
||||
|
||||
if (deck->hasKeyword("MAPAXES")) {
|
||||
Opm::DeckKeywordConstPtr mapaxesKeyword = deck->getKeyword("MAPAXES");
|
||||
Opm::DeckRecordConstPtr mapaxesRecord = mapaxesKeyword->getRecord(0);
|
||||
|
||||
// memleak alert: here we need to make sure that C code
|
||||
// can properly take ownership of the grdecl.mapaxes
|
||||
// object. if it is not freed, it will result in a
|
||||
// memleak...
|
||||
double *cWtfMapaxes = static_cast<double*>(malloc(sizeof(double)*mapaxesRecord->size()));
|
||||
for (unsigned i = 0; i < mapaxesRecord->size(); ++i)
|
||||
cWtfMapaxes[i] = mapaxesRecord->getItem(i)->getSIDouble(0);
|
||||
grdecl.mapaxes = cWtfMapaxes;
|
||||
} else
|
||||
grdecl.mapaxes = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GRIDMANAGER_HEADER_INCLUDED
|
||||
#define OPM_GRIDMANAGER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
struct grdecl;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
/// This class manages an Opm::UnstructuredGrid in the sense that it
|
||||
/// encapsulates creation and destruction of the grid.
|
||||
/// The following grid types can be constructed:
|
||||
/// - 3d corner-point grids (from deck input)
|
||||
/// - 3d tensor grids (from deck input)
|
||||
/// - 2d cartesian grids
|
||||
/// - 3d cartesian grids
|
||||
/// The resulting UnstructuredGrid is available through the c_grid() method.
|
||||
class GridManager
|
||||
{
|
||||
public:
|
||||
/// Construct a 3d corner-point grid or tensor grid from a deck.
|
||||
explicit GridManager(Opm::DeckConstPtr deck);
|
||||
|
||||
/// Construct a grid from an EclipseState::EclipseGrid instance.
|
||||
explicit GridManager(Opm::EclipseGridConstPtr eclipseGrid);
|
||||
|
||||
/// Construct a grid from an EclipseState::EclipseGrid instance,
|
||||
/// giving an explicit set of pore volumes to be used for MINPV
|
||||
/// considerations.
|
||||
/// \input[in] eclipseGrid encapsulates a corner-point grid given from a deck
|
||||
/// \input[in] poreVolumes one element per logical cartesian grid element
|
||||
GridManager(Opm::EclipseGridConstPtr eclipseGrid,
|
||||
const std::vector<double>& poreVolumes);
|
||||
|
||||
/// Construct a 2d cartesian grid with cells of unit size.
|
||||
GridManager(int nx, int ny);
|
||||
|
||||
/// Construct a 2d cartesian grid with cells of size [dx, dy].
|
||||
GridManager(int nx, int ny, double dx, double dy);
|
||||
|
||||
/// Construct a 3d cartesian grid with cells of unit size.
|
||||
GridManager(int nx, int ny, int nz);
|
||||
|
||||
/// Construct a 3d cartesian grid with cells of size [dx, dy, dz].
|
||||
GridManager(int nx, int ny, int nz,
|
||||
double dx, double dy, double dz);
|
||||
|
||||
/// Construct a grid from an input file.
|
||||
/// The file format used is currently undocumented,
|
||||
/// and is therefore only suited for internal use.
|
||||
explicit GridManager(const std::string& input_filename);
|
||||
|
||||
/// Destructor.
|
||||
~GridManager();
|
||||
|
||||
/// Access the managed UnstructuredGrid.
|
||||
/// The method is named similarly to c_str() in std::string,
|
||||
/// to make it clear that we are returning a C-compatible struct.
|
||||
const UnstructuredGrid* c_grid() const;
|
||||
|
||||
static void createGrdecl(Opm::DeckConstPtr deck, struct grdecl &grdecl);
|
||||
|
||||
private:
|
||||
// Disable copying and assignment.
|
||||
GridManager(const GridManager& other);
|
||||
GridManager& operator=(const GridManager& other);
|
||||
|
||||
// Construct corner-point grid from EclipseGrid.
|
||||
void initFromEclipseGrid(Opm::EclipseGridConstPtr eclipseGrid,
|
||||
const std::vector<double>& poreVolumes);
|
||||
|
||||
// The managed UnstructuredGrid.
|
||||
UnstructuredGrid* ug_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_GRIDMANAGER_HEADER_INCLUDED
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <opm/core/grid/GridUtilities.hpp>
|
||||
#include <opm/core/grid/GridHelpers.hpp>
|
||||
|
||||
#include <opm/common/utility/platform_dependent/disable_warnings.h>
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
/// For each cell, find indices of all other cells sharing a vertex with it.
|
||||
/// \param[in] grid A grid object.
|
||||
/// \return A table of neighbour cell-indices by cell.
|
||||
SparseTable<int> cellNeighboursAcrossVertices(const UnstructuredGrid& grid)
|
||||
{
|
||||
// 1. Create vertex->cell mapping. We do this by iterating
|
||||
// over all faces, and adding both its cell neighbours
|
||||
// to each of its vertices' data.
|
||||
using namespace UgGridHelpers;
|
||||
const int num_vertices = grid.number_of_nodes;
|
||||
std::vector<std::set<int>> v2c(num_vertices);
|
||||
const int num_faces = numFaces(grid);
|
||||
const auto fc = faceCells(grid);
|
||||
for (int face = 0; face < num_faces; ++face) {
|
||||
for (int nodepos = grid.face_nodepos[face]; nodepos < grid.face_nodepos[face + 1]; ++nodepos) {
|
||||
const int vertex = grid.face_nodes[nodepos];
|
||||
for (int face_nb = 0; face_nb < 2; ++face_nb) {
|
||||
const int face_nb_cell = fc(face, face_nb);
|
||||
if (face_nb_cell >= 0) {
|
||||
v2c[vertex].insert(face_nb_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. For each cell, iterate over its faces, iterate over
|
||||
// their vertices, and collect all those vertices' cell
|
||||
// neighbours. Add as row to sparse table.
|
||||
SparseTable<int> cell_nb;
|
||||
const int num_cells = numCells(grid);
|
||||
const auto c2f = cell2Faces(grid);
|
||||
// Reserve sufficient room for cartesian grids in 2 and 3
|
||||
// dimensions. Note that this is not a limit, just an
|
||||
// optimization similar to std::vector.
|
||||
cell_nb.reserve(num_cells, (dimensions(grid) == 2 ? 8 : 26) * num_cells);
|
||||
std::set<int> nb;
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
nb.clear();
|
||||
const auto cell_faces = c2f[cell];
|
||||
const int num_cell_faces = cell_faces.size();
|
||||
for (int local_face = 0; local_face < num_cell_faces; ++local_face) {
|
||||
const int face = cell_faces[local_face];
|
||||
for (int nodepos = grid.face_nodepos[face]; nodepos < grid.face_nodepos[face + 1]; ++nodepos) {
|
||||
const int vertex = grid.face_nodes[nodepos];
|
||||
nb.insert(v2c[vertex].begin(), v2c[vertex].end());
|
||||
}
|
||||
}
|
||||
nb.erase(cell);
|
||||
cell_nb.appendRow(nb.begin(), nb.end());
|
||||
}
|
||||
|
||||
// 3. Done. Return.
|
||||
return cell_nb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// For each cell, order the (cell) neighbours counterclockwise.
|
||||
/// \param[in] grid A 2d grid object.
|
||||
/// \param[in, out] nb A cell-cell neighbourhood table, such as from cellNeighboursAcrossVertices().
|
||||
void orderCounterClockwise(const UnstructuredGrid& grid,
|
||||
SparseTable<int>& nb)
|
||||
{
|
||||
if (grid.dimensions != 2) {
|
||||
OPM_THROW(std::logic_error, "Cannot use orderCounterClockwise in " << grid.dimensions << " dimensions.");
|
||||
}
|
||||
const int num_cells = grid.number_of_cells;
|
||||
if (nb.size() != num_cells) {
|
||||
OPM_THROW(std::logic_error, "Inconsistent arguments for orderCounterClockwise().");
|
||||
}
|
||||
|
||||
// For each cell, compute each neighbour's angle with the x axis,
|
||||
// sort that to find the correct permutation of the neighbours.
|
||||
typedef std::pair<double, int> AngleAndPos;
|
||||
std::vector<AngleAndPos> angle_and_pos;
|
||||
std::vector<int> original;
|
||||
for (int cell = 0; cell < num_cells; ++cell) {
|
||||
const int num_nb = nb[cell].size();
|
||||
angle_and_pos.clear();
|
||||
angle_and_pos.resize(num_nb);
|
||||
for (int ii = 0; ii < num_nb; ++ii) {
|
||||
const int cell2 = nb[cell][ii];
|
||||
const double v[2] = { grid.cell_centroids[2*cell2] - grid.cell_centroids[2*cell],
|
||||
grid.cell_centroids[2*cell2 + 1] - grid.cell_centroids[2*cell + 1] };
|
||||
// The formula below gives an angle in [0, 2*pi] with the positive x axis.
|
||||
const double angle = boost::math::constants::pi<double>() - std::atan2(v[1], -v[0]);
|
||||
angle_and_pos[ii] = std::make_pair(angle, ii);
|
||||
}
|
||||
original.assign(nb[cell].begin(), nb[cell].end());
|
||||
std::sort(angle_and_pos.begin(), angle_and_pos.end());
|
||||
for (int ii = 0; ii < num_nb; ++ii) {
|
||||
nb[cell][ii] = original[angle_and_pos[ii].second];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Opm
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_GRIDUTILITIES_HEADER_INCLUDED
|
||||
#define OPM_GRIDUTILITIES_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/utility/SparseTable.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// For each cell, find indices of all cells sharing a vertex with it.
|
||||
/// \param[in] grid A grid object.
|
||||
/// \return A table of neighbour cell-indices by cell.
|
||||
SparseTable<int> cellNeighboursAcrossVertices(const UnstructuredGrid& grid);
|
||||
|
||||
/// For each cell, order the (cell) neighbours counterclockwise.
|
||||
/// \param[in] grid A 2d grid object.
|
||||
/// \param[in, out] nb A cell-cell neighbourhood table, such as from vertexNeighbours().
|
||||
void orderCounterClockwise(const UnstructuredGrid& grid,
|
||||
SparseTable<int>& nb);
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_GRIDUTILITIES_HEADER_INCLUDED
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_MINPVPROCESSOR_HEADER_INCLUDED
|
||||
#define OPM_MINPVPROCESSOR_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// \brief Transform a corner-point grid ZCORN field to account for MINPV processing.
|
||||
class MinpvProcessor
|
||||
{
|
||||
public:
|
||||
/// \brief Create a processor.
|
||||
/// \param[in] nx logical cartesian number of cells in I-direction
|
||||
/// \param[in] ny logical cartesian number of cells in J-direction
|
||||
/// \param[in] nz logical cartesian number of cells in K-direction
|
||||
MinpvProcessor(const int nx, const int ny, const int nz);
|
||||
/// Change zcorn so that it respects the minpv property.
|
||||
/// \param[in] pv pore volumes of all logical cartesian cells
|
||||
/// \param[in] minpv minimum pore volume to accept a cell
|
||||
/// \param[in] actnum active cells, inactive cells are not considered
|
||||
/// \param[in, out] zcorn ZCORN array to be manipulated
|
||||
/// After processing, all cells that have lower pore volume than minpv
|
||||
/// will have the zcorn numbers changed so they are zero-thickness. Any
|
||||
/// cell below will be changed to include the deleted volume.
|
||||
void process(const std::vector<double>& pv,
|
||||
const double minpv,
|
||||
const std::vector<int>& actnum,
|
||||
double* zcorn) const;
|
||||
private:
|
||||
std::array<int,8> cornerIndices(const int i, const int j, const int k) const;
|
||||
std::array<double, 8> getCellZcorn(const int i, const int j, const int k, const double* z) const;
|
||||
void setCellZcorn(const int i, const int j, const int k, const std::array<double, 8>& cellz, double* z) const;
|
||||
std::array<int, 3> dims_;
|
||||
std::array<int, 3> delta_;
|
||||
};
|
||||
|
||||
inline MinpvProcessor::MinpvProcessor(const int nx, const int ny, const int nz)
|
||||
{
|
||||
// Not doing init-list init since bracket-init not available
|
||||
// for all compilers we support (gcc 4.4).
|
||||
dims_[0] = nx;
|
||||
dims_[1] = ny;
|
||||
dims_[2] = nz;
|
||||
delta_[0] = 1;
|
||||
delta_[1] = 2*nx;
|
||||
delta_[2] = 4*nx*ny;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void MinpvProcessor::process(const std::vector<double>& pv,
|
||||
const double minpv,
|
||||
const std::vector<int>& actnum,
|
||||
double* zcorn) const
|
||||
{
|
||||
// Algorithm:
|
||||
// 1. Process each column of cells (with same i and j
|
||||
// coordinates) from top (low k) to bottom (high k).
|
||||
// 2. For each cell 'c' visited, check if its pore volume
|
||||
// pv[c] is less than minpv.
|
||||
// 3. If below the minpv threshold, move the lower four
|
||||
// zcorn associated with the cell c to coincide with
|
||||
// the upper four (so it becomes degenerate). Also move
|
||||
// the higher four zcorn associated with the cell below
|
||||
// to these values (so it gains the deleted volume).
|
||||
|
||||
// Check for sane input sizes.
|
||||
const size_t log_size = dims_[0] * dims_[1] * dims_[2];
|
||||
if (pv.size() != log_size) {
|
||||
OPM_THROW(std::runtime_error, "Wrong size of PORV input, must have one element per logical cartesian cell.");
|
||||
}
|
||||
if (!actnum.empty() && actnum.size() != log_size) {
|
||||
OPM_THROW(std::runtime_error, "Wrong size of ACTNUM input, must have one element per logical cartesian cell.");
|
||||
}
|
||||
|
||||
// Main loop.
|
||||
for (int kk = 0; kk < dims_[2]; ++kk) {
|
||||
for (int jj = 0; jj < dims_[1]; ++jj) {
|
||||
for (int ii = 0; ii < dims_[0]; ++ii) {
|
||||
const int c = ii + dims_[0] * (jj + dims_[1] * kk);
|
||||
if (pv[c] < minpv && (actnum.empty() || actnum[c])) {
|
||||
// Move deeper (higher k) coordinates to lower k coordinates.
|
||||
std::array<double, 8> cz = getCellZcorn(ii, jj, kk, zcorn);
|
||||
for (int count = 0; count < 4; ++count) {
|
||||
cz[count + 4] = cz[count];
|
||||
}
|
||||
setCellZcorn(ii, jj, kk, cz, zcorn);
|
||||
// Check if there is a cell below.
|
||||
if (pv[c] > 0.0 && kk < dims_[2] - 1) {
|
||||
// Set lower k coordinates of cell below to upper cells's coordinates.
|
||||
std::array<double, 8> cz_below = getCellZcorn(ii, jj, kk + 1, zcorn);
|
||||
for (int count = 0; count < 4; ++count) {
|
||||
cz_below[count] = cz[count];
|
||||
}
|
||||
setCellZcorn(ii, jj, kk + 1, cz_below, zcorn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline std::array<int,8> MinpvProcessor::cornerIndices(const int i, const int j, const int k) const
|
||||
{
|
||||
const int ix = 2*(i*delta_[0] + j*delta_[1] + k*delta_[2]);
|
||||
std::array<int, 8> ixs = {{ ix, ix + delta_[0],
|
||||
ix + delta_[1], ix + delta_[1] + delta_[0],
|
||||
ix + delta_[2], ix + delta_[2] + delta_[0],
|
||||
ix + delta_[2] + delta_[1], ix + delta_[2] + delta_[1] + delta_[0] }};
|
||||
|
||||
return ixs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Returns the eight z-values associated with a given cell.
|
||||
// The ordering is such that i runs fastest. That is, with
|
||||
// L = low and H = high:
|
||||
// {LLL, HLL, LHL, HHL, LLH, HLH, LHH, HHH }.
|
||||
inline std::array<double, 8> MinpvProcessor::getCellZcorn(const int i, const int j, const int k, const double* z) const
|
||||
{
|
||||
const std::array<int, 8> ixs = cornerIndices(i, j, k);
|
||||
std::array<double, 8> cellz;
|
||||
for (int count = 0; count < 8; ++count) {
|
||||
cellz[count] = z[ixs[count]];
|
||||
}
|
||||
return cellz;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void MinpvProcessor::setCellZcorn(const int i, const int j, const int k, const std::array<double, 8>& cellz, double* z) const
|
||||
{
|
||||
const std::array<int, 8> ixs = cornerIndices(i, j, k);
|
||||
for (int count = 0; count < 8; ++count) {
|
||||
z[ixs[count]] = cellz[count];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_MINPVPROCESSOR_HEADER_INCLUDED
|
|
@ -1,496 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_PINCHPROCESSOR_HEADER_INCLUDED
|
||||
#define OPM_PINCHPROCESSOR_HEADER_INCLUDED
|
||||
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/grid/GridHelpers.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/FaceDir.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/PinchMode.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <limits>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
template <class Grid>
|
||||
class PinchProcessor
|
||||
{
|
||||
public:
|
||||
/// \brief Create a Pinch processor.
|
||||
/// \param[in] minpvValue value in MINPV keyword
|
||||
/// \param[in] thickness item 2 in PINCH keyword
|
||||
/// \param[in] transMode item 4 in PINCH keyword
|
||||
/// \param[in] multzMode item 5 in PINCH keyword
|
||||
PinchProcessor(const double minpvValue,
|
||||
const double thickness,
|
||||
const PinchMode::ModeEnum transMode,
|
||||
const PinchMode::ModeEnum multzMode);
|
||||
/// Generate NNCs for cells which pv is less than MINPV.
|
||||
/// \param[in] Grid cpgrid or unstructured grid
|
||||
/// \param[in] htrans half cell transmissibility, size is number of cellfaces.
|
||||
/// \param[in] multz Z+ transmissibility multiplier for all active cells
|
||||
/// \param[in] pv pore volume for all the cartesian cells
|
||||
/// \param[in] nnc non-neighbor connection class
|
||||
/// Algorithm:
|
||||
/// 1. Mark all the cells which pv less than minpvValue.
|
||||
/// 2. Find out proper pinchouts column and associate top and bottom cells.
|
||||
/// 3. Compute transmissibility for nncs.
|
||||
/// 4. Apply multz due to different multz options.
|
||||
void process(const Grid& grid,
|
||||
const std::vector<double>& htrans,
|
||||
const std::vector<int>& actnum,
|
||||
const std::vector<double>& multz,
|
||||
const std::vector<double>& pv,
|
||||
NNC& nnc);
|
||||
|
||||
private:
|
||||
double minpvValue_;
|
||||
double thickness_;
|
||||
PinchMode::ModeEnum transMode_;
|
||||
PinchMode::ModeEnum multzMode_;
|
||||
|
||||
/// Mark minpved cells.
|
||||
std::vector<int> getMinpvCells_(const std::vector<int>& actnum,
|
||||
const std::vector<double>& pv);
|
||||
|
||||
/// Get the interface for two cells.
|
||||
int interface_(const Grid& grid,
|
||||
const int cellIdx1,
|
||||
const int cellIdx2);
|
||||
|
||||
/// Get the proper face for one cell.
|
||||
int interface_(const Grid& grid,
|
||||
const int cellIdx,
|
||||
const Opm::FaceDir::DirEnum& faceDir);
|
||||
|
||||
/// Get pinchouts column.
|
||||
std::vector<std::vector<int> >
|
||||
getPinchoutsColumn_(const Grid& grid,
|
||||
const std::vector<int>& actnum,
|
||||
const std::vector<double>& pv);
|
||||
|
||||
/// Get global cell index.
|
||||
int getGlobalIndex_(const int i, const int j, const int k, const int* dims);
|
||||
|
||||
/// Get cartesian index.
|
||||
std::array<int, 3> getCartIndex_(const int idx,
|
||||
const int* dims);
|
||||
|
||||
/// Compute transmissibility for nnc.
|
||||
std::vector<double> transCompute_(const Grid& grid,
|
||||
const std::vector<double>& htrans,
|
||||
const std::vector<int>& pinCells,
|
||||
const std::vector<int>& pinFaces);
|
||||
|
||||
/// Get map between half-trans index and the pair of face index and cell index.
|
||||
std::vector<int> getHfIdxMap_(const Grid& grid);
|
||||
|
||||
/// Get active cell index.
|
||||
int getActiveCellIdx_(const Grid& grid,
|
||||
const int globalIdx);
|
||||
|
||||
/// Item 4 in PINCH keyword.
|
||||
void transTopbot_(const Grid& grid,
|
||||
const std::vector<double>& htrans,
|
||||
const std::vector<int>& actnum,
|
||||
const std::vector<double>& multz,
|
||||
const std::vector<double>& pv,
|
||||
NNC& nnc);
|
||||
|
||||
/// Item 5 in PINCH keyword.
|
||||
std::unordered_multimap<int, double> multzOptions_(const Grid& grid,
|
||||
const std::vector<int>& pinCells,
|
||||
const std::vector<int>& pinFaces,
|
||||
const std::vector<double>& multz,
|
||||
const std::vector<std::vector<int> >& seg);
|
||||
|
||||
/// Apply multz vector to face transmissibility.
|
||||
void applyMultz_(std::vector<double>& trans,
|
||||
const std::unordered_multimap<int, double>& multzmap);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
inline PinchProcessor<Grid>::PinchProcessor(const double minpv,
|
||||
const double thickness,
|
||||
const PinchMode::ModeEnum transMode,
|
||||
const PinchMode::ModeEnum multzMode)
|
||||
{
|
||||
minpvValue_ = minpv;
|
||||
thickness_ = thickness;
|
||||
transMode_ = transMode;
|
||||
multzMode_ = multzMode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
inline int PinchProcessor<Grid>::getGlobalIndex_(const int i, const int j, const int k, const int* dims)
|
||||
{
|
||||
return i + dims[0] * (j + dims[1] * k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class Grid>
|
||||
inline std::array<int, 3> PinchProcessor<Grid>::getCartIndex_(const int idx,
|
||||
const int* dims)
|
||||
{
|
||||
std::array<int, 3> ijk;
|
||||
ijk[0] = (idx % dims[0]);
|
||||
ijk[1] = ((idx / dims[0]) % dims[1]);
|
||||
ijk[2] = ((idx / dims[0]) / dims[1]);
|
||||
|
||||
return ijk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline int PinchProcessor<Grid>::interface_(const Grid& grid,
|
||||
const int cellIdx1,
|
||||
const int cellIdx2)
|
||||
{
|
||||
const auto cell_faces = Opm::UgGridHelpers::cell2Faces(grid);
|
||||
int commonFace = -1;
|
||||
const int actCellIdx1 = getActiveCellIdx_(grid, cellIdx1);
|
||||
const int actCellIdx2 = getActiveCellIdx_(grid, cellIdx2);
|
||||
const auto cellFacesRange1 = cell_faces[actCellIdx1];
|
||||
const auto cellFacesRange2 = cell_faces[actCellIdx2];
|
||||
for (const auto& f1 : cellFacesRange1) {
|
||||
for (const auto& f2 : cellFacesRange2) {
|
||||
if (f1 == f2) {
|
||||
commonFace = f1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (commonFace == -1) {
|
||||
const auto dims = Opm::UgGridHelpers::cartDims(grid);
|
||||
const auto ijk1 = getCartIndex_(cellIdx1, dims);
|
||||
const auto ijk2 = getCartIndex_(cellIdx2, dims);
|
||||
|
||||
OPM_THROW(std::logic_error, "Couldn't find the common face for cell "
|
||||
<< cellIdx1<< "("<<ijk1[0]<<","<<ijk1[1]<<","<<ijk1[2]<<")"
|
||||
<< " and " << cellIdx2<<"("<<ijk2[0]<<","<<ijk2[1]<<","<<ijk2[2]<<")");
|
||||
}
|
||||
|
||||
return commonFace;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline int PinchProcessor<Grid>::interface_(const Grid& grid,
|
||||
const int cellIdx,
|
||||
const Opm::FaceDir::DirEnum& faceDir)
|
||||
{
|
||||
const auto actCellIdx = getActiveCellIdx_(grid, cellIdx);
|
||||
const auto cell_faces = Opm::UgGridHelpers::cell2Faces(grid);
|
||||
const auto cellFacesRange = cell_faces[actCellIdx];
|
||||
int faceIdx = -1;
|
||||
for (auto cellFaceIter = cellFacesRange.begin(); cellFaceIter != cellFacesRange.end(); ++cellFaceIter) {
|
||||
int tag = Opm::UgGridHelpers::faceTag(grid, cellFaceIter);
|
||||
if ( (faceDir == Opm::FaceDir::ZMinus && tag == 4) || (faceDir == Opm::FaceDir::ZPlus && tag == 5) ) {
|
||||
faceIdx = *cellFaceIter;
|
||||
}
|
||||
}
|
||||
|
||||
if (faceIdx == -1) {
|
||||
OPM_THROW(std::logic_error, "Couldn't find the face for cell ." << cellIdx);
|
||||
}
|
||||
|
||||
return faceIdx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline std::vector<int> PinchProcessor<Grid>::getMinpvCells_(const std::vector<int>& actnum,
|
||||
const std::vector<double>& pv)
|
||||
{
|
||||
std::vector<int> minpvCells(pv.size(), 0);
|
||||
for (int idx = 0; idx < static_cast<int>(pv.size()); ++idx) {
|
||||
if (actnum[idx]) {
|
||||
if (pv[idx] < minpvValue_) {
|
||||
minpvCells[idx] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return minpvCells;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline std::vector<int> PinchProcessor<Grid>::getHfIdxMap_(const Grid& grid)
|
||||
{
|
||||
std::vector<int> hf_ix(2*Opm::UgGridHelpers::numFaces(grid), -1);
|
||||
const auto& f2c = Opm::UgGridHelpers::faceCells(grid);
|
||||
const auto& cf = Opm::UgGridHelpers::cell2Faces(grid);
|
||||
|
||||
for (int c = 0, i = 0; c < Opm::UgGridHelpers::numCells(grid); ++c) {
|
||||
for (const auto& f: cf[c]) {
|
||||
const auto off = 0 + (f2c(f, 0) != c);
|
||||
hf_ix[2*f + off] = i++;
|
||||
}
|
||||
}
|
||||
return hf_ix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline int PinchProcessor<Grid>::getActiveCellIdx_(const Grid& grid,
|
||||
const int globalIdx)
|
||||
{
|
||||
const int nc = Opm::UgGridHelpers::numCells(grid);
|
||||
const int* global_cell = Opm::UgGridHelpers::globalCell(grid);
|
||||
int idx = -1;
|
||||
for (int i = 0; i < nc; ++i) {
|
||||
if (global_cell[i] == globalIdx) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline std::vector<double> PinchProcessor<Grid>::transCompute_(const Grid& grid,
|
||||
const std::vector<double>& htrans,
|
||||
const std::vector<int>& pinCells,
|
||||
const std::vector<int>& pinFaces)
|
||||
{
|
||||
const int nc = Opm::UgGridHelpers::numCells(grid);
|
||||
const int nf = Opm::UgGridHelpers::numFaces(grid);
|
||||
std::vector<double> trans(nf, 0);
|
||||
int cellFaceIdx = 0;
|
||||
auto cell_faces = Opm::UgGridHelpers::cell2Faces(grid);
|
||||
const auto& hfmap = getHfIdxMap_(grid);
|
||||
const auto& f2c = Opm::UgGridHelpers::faceCells(grid);
|
||||
for (int cellIdx = 0; cellIdx < nc; ++cellIdx) {
|
||||
auto cellFacesRange = cell_faces[cellIdx];
|
||||
for (auto cellFaceIter = cellFacesRange.begin(); cellFaceIter != cellFacesRange.end(); ++cellFaceIter, ++cellFaceIdx) {
|
||||
const int faceIdx = *cellFaceIter;
|
||||
const auto pos = std::find(pinFaces.begin(), pinFaces.end(), faceIdx);
|
||||
if (pos == pinFaces.end()) {
|
||||
trans[faceIdx] += 1. / htrans[cellFaceIdx];
|
||||
} else {
|
||||
const int idx1 = std::distance(std::begin(pinFaces), pos);
|
||||
int idx2;
|
||||
if (idx1 % 2 == 0) {
|
||||
idx2 = idx1 + 1;
|
||||
} else {
|
||||
idx2 = idx1 - 1;
|
||||
}
|
||||
const int f1 = hfmap[2*pinFaces[idx1] + (f2c(pinFaces[idx1], 0) != getActiveCellIdx_(grid, pinCells[idx1]))];
|
||||
const int f2 = hfmap[2*pinFaces[idx2] + (f2c(pinFaces[idx2], 0) != getActiveCellIdx_(grid, pinCells[idx2]))];
|
||||
trans[faceIdx] = (1. / htrans[f1] + 1. / htrans[f2]);
|
||||
trans[pinFaces[idx2]] = trans[faceIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto f = 0; f < nf; ++f) {
|
||||
trans[f] = 1. / trans[f];
|
||||
}
|
||||
|
||||
return trans;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline std::vector<std::vector<int>> PinchProcessor<Grid>::getPinchoutsColumn_(const Grid& grid,
|
||||
const std::vector<int>& actnum,
|
||||
const std::vector<double>& pv)
|
||||
{
|
||||
const int* dims = Opm::UgGridHelpers::cartDims(grid);
|
||||
std::vector<int> minpvCells = getMinpvCells_(actnum, pv);
|
||||
std::vector<std::vector<int>> segment;
|
||||
for (int z = 0; z < dims[2]; ++z) {
|
||||
for (int y = 0; y < dims[1]; ++y) {
|
||||
for (int x = 0; x < dims[0]; ++x) {
|
||||
const int c = getGlobalIndex_(x, y, z, dims);
|
||||
std::vector<int> seg;
|
||||
if (minpvCells[c]) {
|
||||
seg.push_back(c);
|
||||
minpvCells[c] = 0;
|
||||
for (int zz = z+1; zz < dims[2]; ++zz) {
|
||||
const int cc = getGlobalIndex_(x, y, zz, dims);
|
||||
if (minpvCells[cc]) {
|
||||
seg.push_back(cc);
|
||||
minpvCells[cc] = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
segment.push_back(seg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline void PinchProcessor<Grid>::transTopbot_(const Grid& grid,
|
||||
const std::vector<double>& htrans,
|
||||
const std::vector<int>& actnum,
|
||||
const std::vector<double>& multz,
|
||||
const std::vector<double>& pv,
|
||||
NNC& nnc)
|
||||
{
|
||||
const int* dims = Opm::UgGridHelpers::cartDims(grid);
|
||||
std::vector<int> pinFaces;
|
||||
std::vector<int> pinCells;
|
||||
std::vector<std::vector<int> > newSeg;
|
||||
auto minpvSeg = getPinchoutsColumn_(grid, actnum, pv);
|
||||
for (auto& seg : minpvSeg) {
|
||||
std::array<int, 3> ijk1 = getCartIndex_(seg.front(), dims);
|
||||
std::array<int, 3> ijk2 = getCartIndex_(seg.back(), dims);
|
||||
auto tmp = seg;
|
||||
if ((ijk1[2]-1) >= 0 && (ijk2[2]+1) < dims[2]) {
|
||||
int topCell = getGlobalIndex_(ijk1[0], ijk1[1], ijk1[2]-1, dims);
|
||||
int botCell = getGlobalIndex_(ijk2[0], ijk2[1], ijk2[2]+1, dims);
|
||||
/// for any segments, we need to find the active top and bottom cells.
|
||||
/// if the original segment's top and bottom is inactive, we need to lookup
|
||||
/// the column until they're found otherwise just ignore this segment.
|
||||
if (!actnum[topCell]) {
|
||||
seg.insert(seg.begin(), topCell);
|
||||
for (int topk = ijk1[2]-2; topk > 0; --topk) {
|
||||
topCell = getGlobalIndex_(ijk1[0], ijk1[1], topk, dims);
|
||||
if (actnum[topCell]) {
|
||||
break;
|
||||
} else {
|
||||
auto it = seg.begin();
|
||||
seg.insert(it, topCell);
|
||||
}
|
||||
}
|
||||
pinFaces.push_back(interface_(grid, topCell, Opm::FaceDir::ZPlus));
|
||||
} else {
|
||||
pinFaces.push_back(interface_(grid, topCell, seg.front()));
|
||||
}
|
||||
tmp.insert(tmp.begin(), topCell);
|
||||
newSeg.push_back(tmp);
|
||||
pinCells.push_back(topCell);
|
||||
if (!actnum[botCell]) {
|
||||
seg.push_back(botCell);
|
||||
for (int botk = ijk2[2]+2; botk < dims[2]; ++botk) {
|
||||
botCell = getGlobalIndex_(ijk2[0], ijk2[1], botk, dims);
|
||||
if (actnum[botCell]) {
|
||||
break;
|
||||
} else {
|
||||
seg.push_back(botCell);
|
||||
}
|
||||
}
|
||||
pinFaces.push_back(interface_(grid, botCell, Opm::FaceDir::ZMinus));
|
||||
} else {
|
||||
pinFaces.push_back(interface_(grid, seg.back(), botCell));
|
||||
}
|
||||
pinCells.push_back(botCell);
|
||||
}
|
||||
}
|
||||
|
||||
auto faceTrans = transCompute_(grid, htrans, pinCells, pinFaces);
|
||||
auto multzmap = multzOptions_(grid, pinCells, pinFaces, multz, newSeg);
|
||||
applyMultz_(faceTrans, multzmap);
|
||||
for (int i = 0; i < static_cast<int>(pinCells.size())/2; ++i) {
|
||||
nnc.addNNC(static_cast<int>(pinCells[2*i]), static_cast<int>(pinCells[2*i+1]), faceTrans[pinFaces[2*i]]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline std::unordered_multimap<int, double> PinchProcessor<Grid>::multzOptions_(const Grid& grid,
|
||||
const std::vector<int>& pinCells,
|
||||
const std::vector<int>& pinFaces,
|
||||
const std::vector<double>& multz,
|
||||
const std::vector<std::vector<int> >& segs)
|
||||
{
|
||||
std::unordered_multimap<int, double> multzmap;
|
||||
if (multzMode_ == PinchMode::ModeEnum::TOP) {
|
||||
for (int i = 0; i < static_cast<int>(pinFaces.size())/2; ++i) {
|
||||
multzmap.insert(std::make_pair(pinFaces[2*i], multz[getActiveCellIdx_(grid, pinCells[2*i])]));
|
||||
multzmap.insert(std::make_pair(pinFaces[2*i+1],multz[getActiveCellIdx_(grid, pinCells[2*i])]));
|
||||
}
|
||||
} else if (multzMode_ == PinchMode::ModeEnum::ALL) {
|
||||
for (auto& seg : segs) {
|
||||
//find the min multz in seg cells.
|
||||
auto multzValue = std::numeric_limits<double>::max();
|
||||
for (auto& cellIdx : seg) {
|
||||
auto activeIdx = getActiveCellIdx_(grid, cellIdx);
|
||||
if (activeIdx != -1) {
|
||||
multzValue = std::min(multzValue, multz[activeIdx]);
|
||||
}
|
||||
}
|
||||
//find the right face.
|
||||
auto index = std::distance(std::begin(pinCells), std::find(pinCells.begin(), pinCells.end(), seg.front()));
|
||||
multzmap.insert(std::make_pair(pinFaces[index], multzValue));
|
||||
multzmap.insert(std::make_pair(pinFaces[index+1], multzValue));
|
||||
}
|
||||
}
|
||||
|
||||
return multzmap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline void PinchProcessor<Grid>::applyMultz_(std::vector<double>& trans,
|
||||
const std::unordered_multimap<int, double>& multzmap)
|
||||
{
|
||||
for (auto& x : multzmap) {
|
||||
trans[x.first] *= x.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Grid>
|
||||
inline void PinchProcessor<Grid>::process(const Grid& grid,
|
||||
const std::vector<double>& htrans,
|
||||
const std::vector<int>& actnum,
|
||||
const std::vector<double>& multz,
|
||||
const std::vector<double>& pv,
|
||||
NNC& nnc)
|
||||
{
|
||||
transTopbot_(grid, htrans, actnum, multz, pv, nnc);
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
#endif // OPM_PINCHPROCESSOR_HEADER_INCLUDED
|
|
@ -1,708 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: cart_grid.c
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2011 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2011 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/cornerpoint_grid.h>
|
||||
|
||||
#include <opm/core/grid/cart_grid.h>
|
||||
|
||||
static struct UnstructuredGrid *allocate_cart_grid_3d(int nx, int ny, int nz);
|
||||
static void fill_cart_topology_3d(struct UnstructuredGrid *G);
|
||||
static void fill_cart_geometry_3d(struct UnstructuredGrid *G,
|
||||
const double *x,
|
||||
const double *y,
|
||||
const double *z);
|
||||
static void
|
||||
fill_layered_geometry_3d(struct UnstructuredGrid *G,
|
||||
const double *x,
|
||||
const double *y,
|
||||
const double *z,
|
||||
const double *depthz);
|
||||
|
||||
struct UnstructuredGrid *
|
||||
create_grid_cart3d(int nx, int ny, int nz)
|
||||
{
|
||||
return create_grid_hexa3d(nx, ny, nz, 1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
struct UnstructuredGrid *
|
||||
create_grid_hexa3d(int nx, int ny, int nz,
|
||||
double dx, double dy, double dz)
|
||||
{
|
||||
int i;
|
||||
double *x, *y, *z;
|
||||
struct UnstructuredGrid *G;
|
||||
|
||||
x = malloc((nx + 1) * sizeof *x);
|
||||
y = malloc((ny + 1) * sizeof *y);
|
||||
z = malloc((nz + 1) * sizeof *z);
|
||||
|
||||
if ((x == NULL) || (y == NULL) || (z == NULL)) {
|
||||
G = NULL;
|
||||
} else {
|
||||
for (i = 0; i < nx + 1; i++) { x[i] = i * dx; }
|
||||
for (i = 0; i < ny + 1; i++) { y[i] = i * dy; }
|
||||
for (i = 0; i < nz + 1; i++) { z[i] = i * dz; }
|
||||
|
||||
G = create_grid_tensor3d(nx, ny, nz, x, y, z,
|
||||
(const double *) NULL);
|
||||
}
|
||||
|
||||
free(z); free(y); free(x);
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct UnstructuredGrid *allocate_cart_grid_2d(int nx, int ny);
|
||||
static void fill_cart_topology_2d(struct UnstructuredGrid *G);
|
||||
static void fill_cart_geometry_2d(struct UnstructuredGrid *G,
|
||||
const double *x,
|
||||
const double *y);
|
||||
|
||||
struct UnstructuredGrid*
|
||||
create_grid_cart2d(int nx, int ny, double dx, double dy)
|
||||
{
|
||||
int i;
|
||||
double *x, *y;
|
||||
struct UnstructuredGrid *G;
|
||||
|
||||
x = malloc((nx + 1) * sizeof *x);
|
||||
y = malloc((ny + 1) * sizeof *y);
|
||||
|
||||
if ((x == NULL) || (y == NULL)) {
|
||||
G = NULL;
|
||||
} else {
|
||||
|
||||
for (i = 0; i < nx + 1; i++) { x[i] = i*dx; }
|
||||
for (i = 0; i < ny + 1; i++) { y[i] = i*dy; }
|
||||
|
||||
G = create_grid_tensor2d(nx, ny, x, y);
|
||||
}
|
||||
|
||||
free(y); free(x);
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
struct UnstructuredGrid *
|
||||
create_grid_tensor2d(int nx, int ny, const double *x, const double *y)
|
||||
{
|
||||
struct UnstructuredGrid *G;
|
||||
|
||||
G = allocate_cart_grid_2d(nx, ny);
|
||||
|
||||
if (G != NULL)
|
||||
{
|
||||
fill_cart_topology_2d(G);
|
||||
fill_cart_geometry_2d(G, x, y);
|
||||
}
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
struct UnstructuredGrid *
|
||||
create_grid_tensor3d(int nx ,
|
||||
int ny ,
|
||||
int nz ,
|
||||
const double *x ,
|
||||
const double *y ,
|
||||
const double *z ,
|
||||
const double *depthz)
|
||||
{
|
||||
struct UnstructuredGrid *G;
|
||||
|
||||
G = allocate_cart_grid_3d(nx, ny, nz);
|
||||
|
||||
if (G != NULL)
|
||||
{
|
||||
fill_cart_topology_3d(G);
|
||||
|
||||
if (depthz == NULL) {
|
||||
fill_cart_geometry_3d(G, x, y, z);
|
||||
}
|
||||
else {
|
||||
fill_layered_geometry_3d(G, x, y, z, depthz);
|
||||
}
|
||||
}
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* Static functions follow: */
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
|
||||
static struct UnstructuredGrid *
|
||||
allocate_cart_grid(size_t ndims ,
|
||||
size_t ncells,
|
||||
size_t nfaces,
|
||||
size_t nnodes)
|
||||
{
|
||||
size_t nfacenodes, ncellfaces;
|
||||
|
||||
nfacenodes = nfaces * (2 * (ndims - 1));
|
||||
ncellfaces = ncells * (2 * ndims);
|
||||
|
||||
return allocate_grid(ndims, ncells, nfaces,
|
||||
nfacenodes, ncellfaces, nnodes);
|
||||
}
|
||||
|
||||
|
||||
static struct UnstructuredGrid*
|
||||
allocate_cart_grid_3d(int nx, int ny, int nz)
|
||||
{
|
||||
struct UnstructuredGrid *G;
|
||||
int Nx, Ny, Nz;
|
||||
int nxf, nyf, nzf;
|
||||
|
||||
int ncells, nfaces, nnodes;
|
||||
|
||||
Nx = nx + 1;
|
||||
Ny = ny + 1;
|
||||
Nz = nz +1;
|
||||
|
||||
nxf = Nx * ny * nz;
|
||||
nyf = nx * Ny * nz;
|
||||
nzf = nx * ny * Nz;
|
||||
|
||||
ncells = nx * ny * nz ;
|
||||
nfaces = nxf + nyf + nzf;
|
||||
nnodes = Nx * Ny * Nz ;
|
||||
|
||||
G = allocate_cart_grid(3, ncells, nfaces, nnodes);
|
||||
|
||||
if (G != NULL)
|
||||
{
|
||||
G->dimensions = 3 ;
|
||||
G->cartdims[0] = nx;
|
||||
G->cartdims[1] = ny;
|
||||
G->cartdims[2] = nz;
|
||||
|
||||
G->number_of_cells = ncells;
|
||||
G->number_of_faces = nfaces;
|
||||
G->number_of_nodes = nnodes;
|
||||
}
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
fill_cart_topology_3d(struct UnstructuredGrid *G)
|
||||
{
|
||||
int nx, ny, nz;
|
||||
int Nx, Ny;
|
||||
int nxf, nyf;
|
||||
int i,j,k;
|
||||
|
||||
int *cfaces, *cfacepos, *fnodes, *fnodepos, *fcells;
|
||||
|
||||
nx = G->cartdims[0];
|
||||
ny = G->cartdims[1];
|
||||
nz = G->cartdims[2];
|
||||
|
||||
Nx = nx+1;
|
||||
Ny = ny+1;
|
||||
|
||||
nxf = Nx*ny*nz;
|
||||
nyf = nx*Ny*nz;
|
||||
|
||||
cfaces = G->cell_faces;
|
||||
cfacepos = G->cell_facepos;
|
||||
cfacepos[0] = 0;
|
||||
for (k=0; k<nz; ++k) {
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
*cfaces++ = i+ Nx*(j+ ny* k );
|
||||
*cfaces++ = i+1+Nx*(j+ ny* k );
|
||||
*cfaces++ = i+ nx*(j+ Ny* k ) +nxf;
|
||||
*cfaces++ = i+ nx*(j+1+Ny* k ) +nxf;
|
||||
*cfaces++ = i+ nx*(j+ ny* k ) +nxf+nyf;
|
||||
*cfaces++ = i+ nx*(j+ ny*(k+1)) +nxf+nyf;
|
||||
|
||||
cfacepos[1] = cfacepos[0]+6;
|
||||
++cfacepos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < nx * ny * nz; ++k) {
|
||||
for (i = 0; i < 6; ++i) {
|
||||
G->cell_facetag[k*6 + i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
fnodes = G->face_nodes;
|
||||
fnodepos = G->face_nodepos;
|
||||
fcells = G->face_cells;
|
||||
fnodepos[0] = 0;
|
||||
|
||||
/* Faces with x-normal */
|
||||
for (k=0; k<nz; ++k) {
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx+1; ++i) {
|
||||
*fnodes++ = i+Nx*(j + Ny * k );
|
||||
*fnodes++ = i+Nx*(j+1 + Ny * k );
|
||||
*fnodes++ = i+Nx*(j+1 + Ny *(k+1));
|
||||
*fnodes++ = i+Nx*(j + Ny *(k+1));
|
||||
fnodepos[1] = fnodepos[0] + 4;
|
||||
++fnodepos;
|
||||
if (i==0) {
|
||||
*fcells++ = -1;
|
||||
*fcells++ = i+nx*(j+ny*k);
|
||||
}
|
||||
else if (i == nx) {
|
||||
*fcells++ = i-1+nx*(j+ny*k);
|
||||
*fcells++ = -1;
|
||||
}
|
||||
else {
|
||||
*fcells++ = i-1 + nx*(j+ny*k);
|
||||
*fcells++ = i + nx*(j+ny*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Faces with y-normal */
|
||||
for (k=0; k<nz; ++k) {
|
||||
for (j=0; j<ny+1; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
*fnodes++ = i+ Nx*(j + Ny * k );
|
||||
*fnodes++ = i + Nx*(j + Ny *(k+1));
|
||||
*fnodes++ = i+1 + Nx*(j + Ny *(k+1));
|
||||
*fnodes++ = i+1 + Nx*(j + Ny * k );
|
||||
fnodepos[1] = fnodepos[0] + 4;
|
||||
++fnodepos;
|
||||
if (j==0) {
|
||||
*fcells++ = -1;
|
||||
*fcells++ = i+nx*(j+ny*k);
|
||||
}
|
||||
else if (j == ny) {
|
||||
*fcells++ = i+nx*(j-1+ny*k);
|
||||
*fcells++ = -1;
|
||||
}
|
||||
else {
|
||||
*fcells++ = i+nx*(j-1+ny*k);
|
||||
*fcells++ = i+nx*(j+ny*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Faces with z-normal */
|
||||
for (k=0; k<nz+1; ++k) {
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
*fnodes++ = i+ Nx*(j + Ny * k);
|
||||
*fnodes++ = i+1 + Nx*(j + Ny * k);
|
||||
*fnodes++ = i+1 + Nx*(j+1 + Ny * k);
|
||||
*fnodes++ = i+ Nx*(j+1 + Ny * k);
|
||||
fnodepos[1] = fnodepos[0] + 4;
|
||||
++fnodepos;
|
||||
if (k==0) {
|
||||
*fcells++ = -1;
|
||||
*fcells++ = i+nx*(j+ny*k);
|
||||
}
|
||||
else if (k == nz) {
|
||||
*fcells++ = i+nx*(j+ny*(k-1));
|
||||
*fcells++ = -1;
|
||||
}
|
||||
else {
|
||||
*fcells++ = i+nx*(j+ny*(k-1));
|
||||
*fcells++ = i+nx*(j+ny*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
fill_cart_geometry_3d(struct UnstructuredGrid *G,
|
||||
const double *x,
|
||||
const double *y,
|
||||
const double *z)
|
||||
{
|
||||
int nx, ny, nz;
|
||||
int i,j,k;
|
||||
|
||||
double dx, dy, dz;
|
||||
|
||||
double *coord, *ccentroids, *cvolumes;
|
||||
double *fnormals, *fcentroids, *fareas;
|
||||
|
||||
nx = G->cartdims[0];
|
||||
ny = G->cartdims[1];
|
||||
nz = G->cartdims[2];
|
||||
|
||||
ccentroids = G->cell_centroids;
|
||||
cvolumes = G->cell_volumes;
|
||||
for (k=0; k<nz; ++k) {
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
*ccentroids++ = (x[i] + x[i + 1]) / 2.0;
|
||||
*ccentroids++ = (y[j] + y[j + 1]) / 2.0;
|
||||
*ccentroids++ = (z[k] + z[k + 1]) / 2.0;
|
||||
|
||||
dx = x[i + 1] - x[i];
|
||||
dy = y[j + 1] - y[j];
|
||||
dz = z[k + 1] - z[k];
|
||||
|
||||
*cvolumes++ = dx * dy * dz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fnormals = G->face_normals;
|
||||
fcentroids = G->face_centroids;
|
||||
fareas = G->face_areas;
|
||||
|
||||
/* Faces with x-normal */
|
||||
for (k=0; k<nz; ++k) {
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx+1; ++i) {
|
||||
dy = y[j + 1] - y[j];
|
||||
dz = z[k + 1] - z[k];
|
||||
|
||||
*fnormals++ = dy * dz;
|
||||
*fnormals++ = 0;
|
||||
*fnormals++ = 0;
|
||||
|
||||
*fcentroids++ = x[i];
|
||||
*fcentroids++ = (y[j] + y[j + 1]) / 2.0;
|
||||
*fcentroids++ = (z[k] + z[k + 1]) / 2.0;
|
||||
|
||||
*fareas++ = dy * dz;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Faces with y-normal */
|
||||
for (k=0; k<nz; ++k) {
|
||||
for (j=0; j<ny+1; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
dx = x[i + 1] - x[i];
|
||||
dz = z[k + 1] - z[k];
|
||||
|
||||
*fnormals++ = 0;
|
||||
*fnormals++ = dx * dz;
|
||||
*fnormals++ = 0;
|
||||
|
||||
*fcentroids++ = (x[i] + x[i + 1]) / 2.0;
|
||||
*fcentroids++ = y[j];
|
||||
*fcentroids++ = (z[k] + z[k + 1]) / 2.0;
|
||||
|
||||
*fareas++ = dx * dz;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Faces with z-normal */
|
||||
for (k=0; k<nz+1; ++k) {
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
dx = x[i + 1] - x[i];
|
||||
dy = y[j + 1] - y[j];
|
||||
|
||||
*fnormals++ = 0;
|
||||
*fnormals++ = 0;
|
||||
*fnormals++ = dx * dy;
|
||||
|
||||
*fcentroids++ = (x[i] + x[i + 1]) / 2.0;
|
||||
*fcentroids++ = (y[j] + y[j + 1]) / 2.0;
|
||||
*fcentroids++ = z[k];
|
||||
|
||||
*fareas++ = dx * dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
coord = G->node_coordinates;
|
||||
for (k=0; k<nz+1; ++k) {
|
||||
for (j=0; j<ny+1; ++j) {
|
||||
for (i=0; i<nx+1; ++i) {
|
||||
*coord++ = x[i];
|
||||
*coord++ = y[j];
|
||||
*coord++ = z[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
fill_layered_geometry_3d(struct UnstructuredGrid *G,
|
||||
const double *x,
|
||||
const double *y,
|
||||
const double *z,
|
||||
const double *depthz)
|
||||
{
|
||||
int i , j , k ;
|
||||
int nx, ny, nz;
|
||||
|
||||
const double *depth;
|
||||
double *coord;
|
||||
|
||||
nx = G->cartdims[0]; ny = G->cartdims[1]; nz = G->cartdims[2];
|
||||
|
||||
coord = G->node_coordinates;
|
||||
for (k = 0; k < nz + 1; k++) {
|
||||
|
||||
depth = depthz;
|
||||
|
||||
for (j = 0; j < ny + 1; j++) {
|
||||
for (i = 0; i < nx + 1; i++) {
|
||||
*coord++ = x[i];
|
||||
*coord++ = y[j];
|
||||
*coord++ = z[k] + *depth++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compute_geometry(G);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct UnstructuredGrid*
|
||||
allocate_cart_grid_2d(int nx, int ny)
|
||||
{
|
||||
int nxf, nyf;
|
||||
int Nx , Ny ;
|
||||
|
||||
int ncells, nfaces, nnodes;
|
||||
|
||||
struct UnstructuredGrid *G;
|
||||
|
||||
Nx = nx + 1;
|
||||
Ny = ny + 1;
|
||||
|
||||
nxf = Nx * ny;
|
||||
nyf = nx * Ny;
|
||||
|
||||
ncells = nx * ny ;
|
||||
nfaces = nxf + nyf;
|
||||
nnodes = Nx * Ny ;
|
||||
|
||||
G = allocate_cart_grid(2, ncells, nfaces, nnodes);
|
||||
|
||||
if (G != NULL)
|
||||
{
|
||||
G->dimensions = 2 ;
|
||||
G->cartdims[0] = nx;
|
||||
G->cartdims[1] = ny;
|
||||
G->cartdims[2] = 1 ;
|
||||
|
||||
G->number_of_cells = ncells;
|
||||
G->number_of_faces = nfaces;
|
||||
G->number_of_nodes = nnodes;
|
||||
}
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
fill_cart_topology_2d(struct UnstructuredGrid *G)
|
||||
{
|
||||
int i,j;
|
||||
int nx, ny;
|
||||
int nxf;
|
||||
int Nx;
|
||||
|
||||
int *fnodes, *fnodepos, *fcells, *cfaces, *cfacepos;
|
||||
|
||||
cfaces = G->cell_faces;
|
||||
cfacepos = G->cell_facepos;
|
||||
|
||||
nx = G->cartdims[0];
|
||||
ny = G->cartdims[1];
|
||||
Nx = nx + 1;
|
||||
nxf = Nx * ny;
|
||||
|
||||
cfacepos[0] = 0;
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
*cfaces++ = i+ Nx*j;
|
||||
*cfaces++ = i+ nx*j +nxf;
|
||||
*cfaces++ = i+1+Nx*j;
|
||||
*cfaces++ = i+ nx*(j+1)+nxf;
|
||||
|
||||
cfacepos[1] = cfacepos[0]+4;
|
||||
++cfacepos;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < nx * ny; ++j) {
|
||||
G->cell_facetag[j*4 + 0] = 0;
|
||||
G->cell_facetag[j*4 + 1] = 2;
|
||||
G->cell_facetag[j*4 + 2] = 1;
|
||||
G->cell_facetag[j*4 + 3] = 3;
|
||||
}
|
||||
|
||||
|
||||
fnodes = G->face_nodes;
|
||||
fnodepos = G->face_nodepos;
|
||||
fcells = G->face_cells;
|
||||
fnodepos[0] = 0;
|
||||
|
||||
/* Faces with x-normal */
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx+1; ++i) {
|
||||
*fnodes++ = i+Nx*j;
|
||||
*fnodes++ = i+Nx*(j+1);
|
||||
fnodepos[1] = fnodepos[0] + 2;
|
||||
++fnodepos;
|
||||
if (i==0) {
|
||||
*fcells++ = -1;
|
||||
*fcells++ = i+nx*j;
|
||||
}
|
||||
else if (i == nx) {
|
||||
*fcells++ = i-1+nx*j;
|
||||
*fcells++ = -1;
|
||||
}
|
||||
else {
|
||||
*fcells++ = i-1 + nx*j;
|
||||
*fcells++ = i + nx*j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Faces with y-normal */
|
||||
for (j=0; j<ny+1; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
*fnodes++ = i+1 + Nx*j;
|
||||
*fnodes++ = i+ Nx*j;
|
||||
fnodepos[1] = fnodepos[0] + 2;
|
||||
++fnodepos;
|
||||
if (j==0) {
|
||||
*fcells++ = -1;
|
||||
*fcells++ = i+nx*j;
|
||||
}
|
||||
else if (j == ny) {
|
||||
*fcells++ = i+nx*(j-1);
|
||||
*fcells++ = -1;
|
||||
}
|
||||
else {
|
||||
*fcells++ = i+nx*(j-1);
|
||||
*fcells++ = i+nx*j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
fill_cart_geometry_2d(struct UnstructuredGrid *G,
|
||||
const double *x,
|
||||
const double *y)
|
||||
{
|
||||
int i,j;
|
||||
int nx, ny;
|
||||
|
||||
double dx, dy;
|
||||
|
||||
double *coord, *ccentroids, *cvolumes;
|
||||
double *fnormals, *fcentroids, *fareas;
|
||||
|
||||
nx = G->cartdims[0];
|
||||
ny = G->cartdims[1];
|
||||
|
||||
ccentroids = G->cell_centroids;
|
||||
cvolumes = G->cell_volumes;
|
||||
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
*ccentroids++ = (x[i] + x[i + 1]) / 2.0;
|
||||
*ccentroids++ = (y[j] + y[j + 1]) / 2.0;
|
||||
|
||||
dx = x[i + 1] - x[i];
|
||||
dy = y[j + 1] - y[j];
|
||||
|
||||
*cvolumes++ = dx * dy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fnormals = G->face_normals;
|
||||
fcentroids = G->face_centroids;
|
||||
fareas = G->face_areas;
|
||||
|
||||
/* Faces with x-normal */
|
||||
for (j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx+1; ++i) {
|
||||
dy = y[j + 1] - y[j];
|
||||
|
||||
*fnormals++ = dy;
|
||||
*fnormals++ = 0;
|
||||
|
||||
*fcentroids++ = x[i];
|
||||
*fcentroids++ = (y[j] + y[j + 1]) / 2.0;
|
||||
|
||||
*fareas++ = dy;
|
||||
}
|
||||
}
|
||||
|
||||
/* Faces with y-normal */
|
||||
for (j=0; j<ny+1; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
dx = x[i + 1] - x[i];
|
||||
|
||||
*fnormals++ = 0;
|
||||
*fnormals++ = dx;
|
||||
|
||||
*fcentroids++ = (x[i] + x[i + 1]) / 2.0;
|
||||
*fcentroids++ = y[j];
|
||||
|
||||
*fareas++ = dx;
|
||||
}
|
||||
}
|
||||
|
||||
coord = G->node_coordinates;
|
||||
for (j=0; j<ny+1; ++j) {
|
||||
for (i=0; i<nx+1; ++i) {
|
||||
*coord++ = x[i];
|
||||
*coord++ = y[j];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: cart_grid.h
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2011 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2011 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_CART_GRID_H_HEADER
|
||||
#define OPM_CART_GRID_H_HEADER
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Routines to construct fully formed grid structures from a simple Cartesian
|
||||
* (i.e., tensor product) description.
|
||||
*
|
||||
* The cells are lexicographically ordered with the @c i index cycling the most
|
||||
* rapidly, followed by the @c j index and then, in three space dimensions, the
|
||||
* @c k (`layer') index as the least rapidly cycling index.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Form geometrically Cartesian grid in two space dimensions with equally
|
||||
* sized cells.
|
||||
*
|
||||
* @param[in] nx Number of cells in @c x direction.
|
||||
* @param[in] ny Number of cells in @c y direction.
|
||||
* @param[in] dx Length, in meters, of each cell's @c x extent.
|
||||
* @param[in] dy Length, in meters, of each cell's @c y extent.
|
||||
*
|
||||
* @return Fully formed grid structure containing valid geometric primitives.
|
||||
* Must be destroyed using function destroy_grid().
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
create_grid_cart2d(int nx, int ny, double dx, double dy);
|
||||
|
||||
|
||||
/**
|
||||
* Form geometrically Cartesian grid in three space dimensions with unit-sized
|
||||
* cells.
|
||||
*
|
||||
* @param[in] nx Number of cells in @c x direction.
|
||||
* @param[in] ny Number of cells in @c y direction.
|
||||
* @param[in] nz Number of cells in @c z direction.
|
||||
*
|
||||
* @return Fully formed grid structure containing valid geometric primitives.
|
||||
* Must be destroyed using function destroy_grid().
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
create_grid_cart3d(int nx, int ny, int nz);
|
||||
|
||||
|
||||
/**
|
||||
* Form geometrically Cartesian grid in three space dimensions with equally
|
||||
* sized cells.
|
||||
*
|
||||
* Each cell has physical size (volume) \f$\mathit{dx}\times \mathit{dy}\times
|
||||
* \mathit{dz}\f$.
|
||||
*
|
||||
* @param[in] nx Number of cells in @c x direction.
|
||||
* @param[in] ny Number of cells in @c y direction.
|
||||
* @param[in] nz Number of cells in @c z direction.
|
||||
*
|
||||
* @param[in] dx Length, in meters, of each cell's @c x extent.
|
||||
* @param[in] dy Length, in meters, of each cell's @c y extent.
|
||||
* @param[in] dz Length, in meters, of each cell's @c z extent.
|
||||
*
|
||||
* @return Fully formed grid structure containing valid geometric primitives.
|
||||
* Must be destroyed using function destroy_grid().
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
create_grid_hexa3d(int nx, int ny, int nz,
|
||||
double dx, double dy, double dz);
|
||||
|
||||
|
||||
/**
|
||||
* Form tensor product (Cartesian) grid in two space dimensions.
|
||||
*
|
||||
* The size (volume) of cell \f$(i,j)\f$ is
|
||||
* \f[
|
||||
* v_{ij} = (x_{i+1} - x_i)\cdot (y_{j+1} - y_j)
|
||||
* \f]
|
||||
* Similar relations hold for the cell and interface centroids as well as the
|
||||
* interface areas and normal vectors. In other words, cell \f$(i,j)\f$ is the
|
||||
* convex hull bounded by the tensor product of nodes \f$x_i\f$, \f$x_{i+1}\f$,
|
||||
* \f$y_j\f$, and \f$y_{j+1}\f$.
|
||||
*
|
||||
* @param[in] nx Number of cells in @c x direction.
|
||||
* @param[in] ny Number of cells in @c y direction.
|
||||
*
|
||||
* @param[in] x Position along @c x axis of each grid line with constant @c x
|
||||
* coordinate. Array of size <CODE>nx + 1</CODE>.
|
||||
* @param[in] y Position along @c y axis of each grid line with constant @c y
|
||||
* coordinate. Array of size <CODE>ny + 1</CODE>.
|
||||
*
|
||||
* @return Fully formed grid structure containing valid geometric primitives.
|
||||
* Must be destroyed using function destroy_grid().
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
create_grid_tensor2d(int nx, int ny,
|
||||
const double *x , const double *y );
|
||||
|
||||
|
||||
/**
|
||||
* Form tensor product (i.e., topologically Cartesian) grid in three space
|
||||
* dimensions--possibly with a variable top-layer topography.
|
||||
*
|
||||
* If @c depthz is @c NULL, then geometric information such as volumes and
|
||||
* centroids is calculated from analytic expressions. Otherwise, these values
|
||||
* are computed using function compute_geometry().
|
||||
*
|
||||
* @param[in] nx Number of cells in @c x direction.
|
||||
* @param[in] ny Number of cells in @c y direction.
|
||||
* @param[in] nz Number of cells in @c z direction.
|
||||
*
|
||||
* @param[in] x Position along @c x axis of each grid line with constant @c x
|
||||
* coordinate. Array of size <CODE>nx + 1</CODE>.
|
||||
* @param[in] y Position along @c y axis of each grid line with constant @c y
|
||||
* coordinate. Array of size <CODE>ny + 1</CODE>.
|
||||
* @param[in] z Distance (depth) from top-layer measured along the @c z axis of
|
||||
* each grid line with constant @c z coordinate. Array of size
|
||||
* <CODE>nz + 1</CODE>.
|
||||
*
|
||||
* @param[in] depthz
|
||||
* Top-layer topography specification. If @c NULL, interpreted as
|
||||
* horizontal top-layer at <CODE>z=0</CODE>. Otherwise, must be
|
||||
* an array of size <CODE>(nx + 1) * (ny + 1)</CODE>, ordered
|
||||
* lexicographically, that defines the depth of each top-layer
|
||||
* pillar vertex.
|
||||
*
|
||||
* @return Fully formed grid structure containing valid geometric primitives.
|
||||
* Must be destroyed using function destroy_grid().
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
create_grid_tensor3d(int nx ,
|
||||
int ny ,
|
||||
int nz ,
|
||||
const double *x ,
|
||||
const double *y ,
|
||||
const double *z ,
|
||||
const double *depthz);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* OPM_CART_GRID_H_HEADER */
|
|
@ -1,235 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: cgridinterface.c
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2011 SINTEF ICT, Applied Mathematics.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <opm/core/grid/cornerpoint_grid.h>
|
||||
#include <opm/core/grid/cpgpreprocess/geometry.h>
|
||||
#include <opm/core/grid/cpgpreprocess/preprocess.h>
|
||||
#include <opm/core/grid.h>
|
||||
|
||||
|
||||
static int
|
||||
fill_cell_topology(struct processed_grid *pg,
|
||||
struct UnstructuredGrid *g )
|
||||
{
|
||||
int f, c1, c2, tag;
|
||||
size_t c, nc, nhf;
|
||||
|
||||
nc = g->number_of_cells;
|
||||
|
||||
g->cell_facepos = malloc((nc + 1) * sizeof *g->cell_facepos);
|
||||
|
||||
if (g->cell_facepos != NULL) {
|
||||
/* Allocate and initialise compressed cell-to-face topology. */
|
||||
|
||||
for (c = 0; c < nc + 1; c++) { g->cell_facepos[c] = 0; }
|
||||
|
||||
for (f = 0; f < g->number_of_faces; f++) {
|
||||
c1 = g->face_cells[2*f + 0];
|
||||
c2 = g->face_cells[2*f + 1];
|
||||
|
||||
if (c1 >= 0) { g->cell_facepos[c1 + 1] += 1; }
|
||||
if (c2 >= 0) { g->cell_facepos[c2 + 1] += 1; }
|
||||
}
|
||||
|
||||
for (c = 1; c <= nc; c++) {
|
||||
g->cell_facepos[0] += g->cell_facepos[c];
|
||||
g->cell_facepos[c] = g->cell_facepos[0] - g->cell_facepos[c];
|
||||
}
|
||||
|
||||
nhf = g->cell_facepos[0];
|
||||
g->cell_facepos[0] = 0;
|
||||
|
||||
g->cell_faces = malloc(nhf * sizeof *g->cell_faces );
|
||||
g->cell_facetag = malloc(nhf * sizeof *g->cell_facetag);
|
||||
|
||||
if ((g->cell_faces == NULL) || (g->cell_facetag == NULL)) {
|
||||
free(g->cell_facetag); g->cell_facetag = NULL;
|
||||
free(g->cell_faces); g->cell_faces = NULL;
|
||||
free(g->cell_facepos); g->cell_facepos = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (g->cell_facepos != NULL) {
|
||||
/* Compute final cell-to-face mapping and half-face tags.
|
||||
*
|
||||
* Process relies on preprocess() producing neighbourship
|
||||
* definitions for which the normals point (logically) in the
|
||||
* positive I,J,K directions *and* from ->face_cells[2*f+0] to
|
||||
* ->face_cells[2*f+1] (i.e., from first to second cell of
|
||||
* interface 'f'--be it internal or outer).
|
||||
*
|
||||
* For instance, a "LEFT" connection (pg->face_tag==LEFT==0)
|
||||
* for which the normal points into the cell (i.e., when
|
||||
* ->face_cells[2*f+1] >= 0), is a half-face of type 0.
|
||||
*
|
||||
* Simlarly, a "TOP" connection (pg->face_tag==TOP==2) for
|
||||
* which the normal points out of the cell (i.e., when
|
||||
* ->face_cells[2*f+0] >= 0), is a half-face of type 5. */
|
||||
|
||||
for (f = 0; f < g->number_of_faces; f++) {
|
||||
tag = 2 * pg->face_tag[f]; /* [0, 2, 4] */
|
||||
c1 = g->face_cells[2*f + 0];
|
||||
c2 = g->face_cells[2*f + 1];
|
||||
|
||||
if (c1 >= 0) {
|
||||
g->cell_faces [ g->cell_facepos[c1 + 1] ] = f;
|
||||
g->cell_facetag [ g->cell_facepos[c1 + 1] ] = tag + 1;
|
||||
|
||||
g->cell_facepos[c1 + 1] += 1;
|
||||
}
|
||||
|
||||
if (c2 >= 0) {
|
||||
g->cell_faces [ g->cell_facepos[c2 + 1] ] = f;
|
||||
g->cell_facetag [ g->cell_facepos[c2 + 1] ] = tag + 0;
|
||||
|
||||
g->cell_facepos[c2 + 1] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return g->cell_facepos != NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
allocate_geometry(struct UnstructuredGrid *g)
|
||||
{
|
||||
int ok;
|
||||
size_t nc, nf, nd;
|
||||
|
||||
assert (g->dimensions == 3);
|
||||
|
||||
nc = g->number_of_cells;
|
||||
nf = g->number_of_faces;
|
||||
nd = 3;
|
||||
|
||||
g->face_areas = malloc(nf * 1 * sizeof *g->face_areas);
|
||||
g->face_centroids = malloc(nf * nd * sizeof *g->face_centroids);
|
||||
g->face_normals = malloc(nf * nd * sizeof *g->face_normals);
|
||||
|
||||
g->cell_volumes = malloc(nc * 1 * sizeof *g->cell_volumes);
|
||||
g->cell_centroids = malloc(nc * nd * sizeof *g->cell_centroids);
|
||||
|
||||
ok = g->face_areas != NULL;
|
||||
ok += g->face_centroids != NULL;
|
||||
ok += g->face_normals != NULL;
|
||||
|
||||
ok += g->cell_volumes != NULL;
|
||||
ok += g->cell_centroids != NULL;
|
||||
|
||||
return ok == 5;
|
||||
}
|
||||
|
||||
|
||||
void compute_geometry(struct UnstructuredGrid *g)
|
||||
{
|
||||
assert (g != NULL);
|
||||
if (g!=NULL)
|
||||
{
|
||||
assert (g->face_centroids != NULL);
|
||||
assert (g->face_normals != NULL);
|
||||
assert (g->face_areas != NULL);
|
||||
assert (g->cell_centroids != NULL);
|
||||
assert (g->cell_volumes != NULL);
|
||||
|
||||
compute_face_geometry(g->dimensions , g->node_coordinates,
|
||||
g->number_of_faces, g->face_nodepos,
|
||||
g->face_nodes, g->face_normals,
|
||||
g->face_centroids, g->face_areas);
|
||||
|
||||
compute_cell_geometry(g->dimensions, g->node_coordinates,
|
||||
g->face_nodepos, g->face_nodes,
|
||||
g->face_cells, g->face_normals,
|
||||
g->face_centroids, g->number_of_cells,
|
||||
g->cell_facepos, g->cell_faces,
|
||||
g->cell_centroids, g->cell_volumes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct UnstructuredGrid *
|
||||
create_grid_cornerpoint(const struct grdecl *in, double tol)
|
||||
{
|
||||
struct UnstructuredGrid *g;
|
||||
int ok;
|
||||
struct processed_grid pg;
|
||||
|
||||
g = create_grid_empty();
|
||||
if (g == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
process_grdecl(in, tol, &pg);
|
||||
|
||||
/*
|
||||
* Convert "struct processed_grid" to "struct UnstructuredGrid".
|
||||
*
|
||||
* In particular, convey resource ownership from 'pg' to 'g'.
|
||||
* Consequently, memory resources obtained in process_grdecl()
|
||||
* will be released in destroy_grid().
|
||||
*/
|
||||
g->dimensions = 3;
|
||||
|
||||
g->number_of_nodes = pg.number_of_nodes;
|
||||
g->number_of_faces = pg.number_of_faces;
|
||||
g->number_of_cells = pg.number_of_cells;
|
||||
|
||||
g->node_coordinates = pg.node_coordinates;
|
||||
|
||||
g->face_nodes = pg.face_nodes;
|
||||
g->face_nodepos = pg.face_ptr;
|
||||
g->face_cells = pg.face_neighbors;
|
||||
|
||||
/* Explicitly relinquish resource references conveyed to 'g'. This
|
||||
* is needed to avoid creating dangling references in the
|
||||
* free_processed_grid() call. */
|
||||
pg.node_coordinates = NULL;
|
||||
pg.face_nodes = NULL;
|
||||
pg.face_ptr = NULL;
|
||||
pg.face_neighbors = NULL;
|
||||
|
||||
/* allocate and fill g->cell_faces/g->cell_facepos and
|
||||
* g->cell_facetag as well as the geometry-related fields. */
|
||||
ok = fill_cell_topology(&pg, g);
|
||||
ok = ok && allocate_geometry(g);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
destroy_grid(g);
|
||||
g = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
compute_geometry(g);
|
||||
|
||||
g->cartdims[0] = pg.dimensions[0];
|
||||
g->cartdims[1] = pg.dimensions[1];
|
||||
g->cartdims[2] = pg.dimensions[2];
|
||||
|
||||
g->global_cell = pg.local_cell_index;
|
||||
|
||||
/* Explicitly relinquish resource references conveyed to 'g'.
|
||||
* This is needed to avoid creating dangling references in the
|
||||
* free_processed_grid() call. */
|
||||
pg.local_cell_index = NULL;
|
||||
}
|
||||
|
||||
free_processed_grid(&pg);
|
||||
|
||||
return g;
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: preprocess.h
|
||||
//
|
||||
// Created: Fri Jun 19 08:43:04 2009
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
// $Date: 2010-08-27 19:12:16 +0200 (Fri, 27 Aug 2010) $
|
||||
//
|
||||
// $Revision: 930 $
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
/*
|
||||
Copyright 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_CORNERPOINT_GRID_HEADER_INCLUDED
|
||||
#define OPM_CORNERPOINT_GRID_HEADER_INCLUDED
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Routines to form a complete UnstructuredGrid from a corner-point
|
||||
* specification.
|
||||
*/
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/grid/cpgpreprocess/preprocess.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Construct grid representation from corner-point specification of a
|
||||
* particular geological model.
|
||||
*
|
||||
* Pinched cells will be removed irrespective of any explicit "active" map
|
||||
* in the geological model input specification. Geometric primitives such
|
||||
* as cell barycenters (i.e., centroids), volumes and interface areas are
|
||||
* computed internally using function compute_geometry(). The caller does
|
||||
* not need to compute this information separately.
|
||||
*
|
||||
* @param[in] in Corner-point specification. If "actnum" is NULL, then the
|
||||
* specification is interpreted as if all cells are initially
|
||||
* active.
|
||||
*
|
||||
* @param[in] tol Absolute tolerance of node-coincidence.
|
||||
*
|
||||
* @return Fully formed grid data structure that manages the grid defined by
|
||||
* the input corner-point specification. Must be destroyed using function
|
||||
* destroy_grid().
|
||||
*/
|
||||
struct UnstructuredGrid *
|
||||
create_grid_cornerpoint(const struct grdecl *in, double tol);
|
||||
|
||||
|
||||
/**
|
||||
* Compute derived geometric primitives in a grid.
|
||||
*
|
||||
* This function computes values for each of the following quantities
|
||||
* - Quantities pertaining to interfaces (connections, faces)
|
||||
* -# Barycenters (centroids), <CODE>g->dimensions</CODE> scalars per face
|
||||
* stored sequentially in <CODE>g->face_centroids</CODE>.
|
||||
* -# Areas, one scalar per face stored sequentially in
|
||||
* <CODE>g->face_areas</CODE>.
|
||||
* -# Normals, <CODE>g->dimensions</CODE> scalars per face stored
|
||||
* sequentially in <CODE>g->face_normals</CODE>. The Euclidian norm of
|
||||
* each normal is equal to the corresponding face's area.
|
||||
*
|
||||
* - Quantities pertaining to cells (volumes)
|
||||
* -# Barycenters (centroids), <CODE>g->dimensions</CODE> scalars per cell
|
||||
* stored sequentially in <CODE>g->cell_centroids</CODE>.
|
||||
* -# Volumes, one scalar per cell stored sequentially in
|
||||
* <CODE>g->cell_volumes</CODE>.
|
||||
*
|
||||
* These fields must be allocated prior to calling compute_geometry().
|
||||
*
|
||||
* @param[in,out] g Grid structure.
|
||||
*/
|
||||
void compute_geometry(struct UnstructuredGrid *g);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_CORNERPOINT_GRID_HEADER_INCLUDED */
|
|
@ -1,369 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: facetopology.c
|
||||
//
|
||||
// Created: Fri Jun 19 08:46:53 2009
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// $Revision$
|
||||
//
|
||||
//===========================================================================*/
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "preprocess.h"
|
||||
#include "facetopology.h"
|
||||
|
||||
/* No checking of input arguments in this code! */
|
||||
#define MIN(i,j) ((i)<(j) ? (i) : (j))
|
||||
#define MAX(i,j) ((i)>(j) ? (i) : (j))
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* */
|
||||
/* */
|
||||
/* Find connections for each pair of pillars */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/*------------------------------------------------------*/
|
||||
|
||||
|
||||
/* Determine face topology first, then compute intersection. */
|
||||
/* All intersections that occur are present in the final face geometry.*/
|
||||
static int *
|
||||
computeFaceTopology(const int *a1, const int *a2,
|
||||
const int *b1, const int *b2,
|
||||
int intersect[4], int *faces)
|
||||
{
|
||||
int mask[8];
|
||||
int k;
|
||||
int *f;
|
||||
|
||||
for (k = 0; k < 8; k++) { mask[k] = -1; }
|
||||
|
||||
/* Which pillar points should we use? */
|
||||
if (a1[1] > b1[1]){ mask[0] = b1[1]; } else { mask[0] = a1[1]; }
|
||||
if (a2[1] > b2[1]){ mask[2] = b2[1]; } else { mask[2] = a2[1]; }
|
||||
if (a2[0] > b2[0]){ mask[4] = a2[0]; } else { mask[4] = b2[0]; }
|
||||
if (a1[0] > b1[0]){ mask[6] = a1[0]; } else { mask[6] = b1[0]; }
|
||||
|
||||
#if DEBUG
|
||||
/* Illegal situations */
|
||||
if (mask [0] == mask[2] ||
|
||||
mask [0] == mask[4] ||
|
||||
mask [2] == mask[6] ||
|
||||
mask [4] == mask[6]){
|
||||
fprintf(stderr, "Illegal Partial pinch!\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Partial pinch of face */
|
||||
if (mask[0] == mask[6]){
|
||||
mask[6] = -1;
|
||||
}
|
||||
|
||||
|
||||
if (mask[2] == mask[4]){
|
||||
mask[4] = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get shape of face: */
|
||||
/* each new intersection will be part of the new face, */
|
||||
/* but not all pillar points. This is encoded in mask. */
|
||||
|
||||
|
||||
mask[1] = intersect[3]; /* top-top */
|
||||
mask[3] = -1;
|
||||
mask[5] = intersect[0]; /* bottom-bottom*/
|
||||
mask[7] = -1;
|
||||
|
||||
/* bottom-top */
|
||||
if (intersect[1] != -1){
|
||||
if(a1[0] > b1[1]){ /* intersection[1] left of (any) intersection[0] */
|
||||
mask[0] = -1;
|
||||
mask[6] = -1;
|
||||
mask[7] = intersect[1];
|
||||
}
|
||||
else{
|
||||
mask[2] = -1;
|
||||
mask[4] = -1;
|
||||
mask[3] = intersect[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* top-bottom */
|
||||
if (intersect[2] != -1){
|
||||
if(a1[1] < b1[0]){ /* intersection[2] left of (any) intersection[3] */
|
||||
mask[0] = -1;
|
||||
mask[6] = -1;
|
||||
mask[7] = intersect[2];
|
||||
}
|
||||
else{
|
||||
mask[2] = -1;
|
||||
mask[4] = -1;
|
||||
mask[3] = intersect[2];
|
||||
}
|
||||
}
|
||||
f = faces;
|
||||
for (k=7; k>=0; --k){
|
||||
if(mask[k] != -1){
|
||||
*f++ = mask[k];
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG>1
|
||||
/* Check for repeated nodes:*/
|
||||
int i;
|
||||
fprintf(stderr, "face: ");
|
||||
for (i=0; i<8; ++i){
|
||||
fprintf(stderr, "%d ", mask[i]);
|
||||
for (k=0; k<8; ++k){
|
||||
if (i!=k && mask[i] != -1 && mask[i] == mask[k]){
|
||||
fprintf(stderr, "Repeated node in faulted face\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
|
||||
|
||||
return f;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* a) If we assume that the index increase when z increase for each
|
||||
pillar (but only separately), we can use only the point indices,
|
||||
since we only need to compare z-values on one pillar at a time.
|
||||
|
||||
b) We assume input is preprocessed such that no intersections occur
|
||||
on the first and last lines, for instance by padding the grid with
|
||||
extra cells. This is convenient in the identification of (unique)
|
||||
intersections.
|
||||
|
||||
*/
|
||||
|
||||
#define LINE_INTERSECTION(a1, a2, b1, b2) \
|
||||
(((a1 > b1) && (a2 < b2)) || \
|
||||
((a1 < b1) && (a2 > b2)))
|
||||
|
||||
static int
|
||||
faceintersection(const int *a1, const int *a2,
|
||||
const int *b1, const int *b2)
|
||||
{
|
||||
return
|
||||
MAX(a1[0], b1[0]) < MIN(a1[1], b1[1]) ||
|
||||
MAX(a2[0], b2[0]) < MIN(a2[1], b2[1]) ||
|
||||
LINE_INTERSECTION(a1[0], a2[0], b1[0], b2[0]) ||
|
||||
LINE_INTERSECTION(a1[1], a2[1], b1[1], b2[1]);
|
||||
}
|
||||
|
||||
|
||||
#define MEANINGFUL_FACE(i, j) \
|
||||
(! ((a1[i] == INT_MIN) && (b1[j] == INT_MIN)) && \
|
||||
! ((a1[i+1] == INT_MAX) && (b1[j+1] == INT_MAX)))
|
||||
|
||||
|
||||
/* work should be pointer to 2n ints initialised to zero . */
|
||||
void findconnections(int n, int *pts[4],
|
||||
int *intersectionlist,
|
||||
int *work,
|
||||
struct processed_grid *out)
|
||||
{
|
||||
/* vectors of point numbers for faces a(b) on pillar 1(2) */
|
||||
int *a1 = pts[0];
|
||||
int *a2 = pts[1];
|
||||
int *b1 = pts[2];
|
||||
int *b2 = pts[3];
|
||||
|
||||
/* Intersection record for top line and bottomline of a */
|
||||
int *itop = work;
|
||||
int *ibottom = work + n;
|
||||
int *f = out->face_nodes + out->face_ptr[out->number_of_faces];
|
||||
int *c = out->face_neighbors + 2*out->number_of_faces;
|
||||
|
||||
int k1 = 0;
|
||||
int k2 = 0;
|
||||
|
||||
int i,j=0;
|
||||
int intersect[4];
|
||||
int *tmp;
|
||||
/* for (i=0; i<2*n; work[i++]=-1); */
|
||||
|
||||
for (i = 0; i < 4; i++) { intersect[i] = -1; }
|
||||
|
||||
for (i = 0; i < n - 1; ++i) {
|
||||
|
||||
/* pinched a-cell */
|
||||
if ((a1[i] == a1[i + 1]) &&
|
||||
(a2[i] == a2[i + 1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
while ((j < n-1) &&
|
||||
((b1[j] < a1[i + 1]) ||
|
||||
(b2[j] < a2[i + 1])))
|
||||
{
|
||||
/* pinched b-cell */
|
||||
if ((b1[j] == b1[j + 1]) &&
|
||||
(b2[j] == b2[j + 1])) {
|
||||
|
||||
itop[j+1] = itop[j];
|
||||
++j;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------- */
|
||||
/* face a(i,i+1) and face b(j,j+1) have nonzero intersection */
|
||||
/* --------------------------------------------------------- */
|
||||
if (faceintersection(a1+i, a2+i, b1+j, b2+j)){
|
||||
|
||||
|
||||
/* Completely matching faces */
|
||||
if (((a1[i] == b1[j]) && (a1[i+1] == b1[j+1])) &&
|
||||
((a2[i] == b2[j]) && (a2[i+1] == b2[j+1]))) {
|
||||
|
||||
if (MEANINGFUL_FACE(i, j)) {
|
||||
|
||||
int cell_a = i%2 != 0 ? (i-1)/2 : -1;
|
||||
int cell_b = j%2 != 0 ? (j-1)/2 : -1;
|
||||
|
||||
if (cell_a != -1 || cell_b != -1){
|
||||
*c++ = cell_a;
|
||||
*c++ = cell_b;
|
||||
|
||||
/* face */
|
||||
*f++ = a1[i];
|
||||
*f++ = a2[i];
|
||||
|
||||
/* avoid duplicating nodes in pinched faces */
|
||||
if (a2[i+1] != a2[i]) { *f++ = a2[i+1]; }
|
||||
if (a1[i+1] != a1[i]) { *f++ = a1[i+1]; }
|
||||
|
||||
out->face_ptr[++out->number_of_faces] = f - out->face_nodes;
|
||||
|
||||
}
|
||||
else{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Non-matching faces */
|
||||
else{
|
||||
|
||||
/* Find new intersection */
|
||||
if (LINE_INTERSECTION(a1[i+1], a2[i+1],
|
||||
b1[j+1], b2[j+1])) {
|
||||
itop[j+1] = out->number_of_nodes++;
|
||||
|
||||
/* store point numbers of intersecting lines */
|
||||
*intersectionlist++ = a1[i+1];
|
||||
*intersectionlist++ = a2[i+1];
|
||||
*intersectionlist++ = b1[j+1];
|
||||
*intersectionlist++ = b2[j+1];
|
||||
|
||||
|
||||
}else{
|
||||
itop[j+1] = -1;
|
||||
}
|
||||
|
||||
/* Update intersection record */
|
||||
intersect[0] = ibottom[j ]; /* i x j */
|
||||
intersect[1] = ibottom[j+1]; /* i x j+1 */
|
||||
intersect[2] = itop[j ]; /* i+1 x j */
|
||||
intersect[3] = itop[j+1]; /* i+1 x j+1 */
|
||||
|
||||
|
||||
/* Add face to list of faces if no INT_MIN or
|
||||
* INT_MAX appear in a or b. */
|
||||
if (MEANINGFUL_FACE(i,j)) {
|
||||
|
||||
/*
|
||||
Even indices refer to space between cells,
|
||||
odd indices refer to cells
|
||||
*/
|
||||
int cell_a = i%2 != 0 ? (i-1)/2 : -1;
|
||||
int cell_b = j%2 != 0 ? (j-1)/2 : -1;
|
||||
|
||||
|
||||
|
||||
if (cell_a != -1 || cell_b != -1){
|
||||
*c++ = cell_a;
|
||||
*c++ = cell_b;
|
||||
|
||||
f = computeFaceTopology(a1+i, a2+i, b1+j, b2+j, intersect, f);
|
||||
|
||||
out->face_ptr[++out->number_of_faces] = f - out->face_nodes;
|
||||
}
|
||||
else{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update candidates for restart of j for in next i-iteration */
|
||||
if (b1[j] < a1[i+1]) { k1 = j; }
|
||||
if (b2[j] < a2[i+1]) { k2 = j; }
|
||||
|
||||
j = j+1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Swap intersection records: top line of a[i,i+1] is bottom
|
||||
* line of a[i+1,i+2] */
|
||||
tmp = itop; itop = ibottom; ibottom = tmp;
|
||||
|
||||
/* Zero out the "new" itop */
|
||||
for(j=0;j<n; ++j) { itop[j]=-1; }
|
||||
|
||||
/* Set j to appropriate start position for next i */
|
||||
j = MIN(k1, k2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Local Variables: */
|
||||
/* c-basic-offset:4 */
|
||||
/* End: */
|
|
@ -1,48 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: facetopology.h
|
||||
//
|
||||
// Created: Fri Jun 19 08:47:10 2009
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// $Revision$
|
||||
//
|
||||
//===========================================================================*/
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_FACETOPOLOGY_HEADER
|
||||
#define OPM_FACETOPOLOGY_HEADER
|
||||
|
||||
|
||||
void findconnections(int n, int *pts[4],
|
||||
int *intersectionlist,
|
||||
int *work,
|
||||
struct processed_grid *out);
|
||||
|
||||
#endif /* OPM_FACETOPOLOGY_HEADER */
|
||||
|
||||
/* Local Variables: */
|
||||
/* c-basic-offset:4 */
|
||||
/* End: */
|
|
@ -1,460 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010 (c) SINTEF ICT, Applied Mathematics.
|
||||
* Jostein R. Natvig <Jostein.R.Natvig at sintef.no>
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "geometry.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void
|
||||
cross(const double u[3], const double v[3], double w[3])
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
w[0] = u[1]*v[2]-u[2]*v[1];
|
||||
w[1] = u[2]*v[0]-u[0]*v[2];
|
||||
w[2] = u[0]*v[1]-u[1]*v[0];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static double
|
||||
norm(const double w[3])
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
return sqrt(w[0]*w[0] + w[1]*w[1] + w[2]*w[2]);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void
|
||||
compute_face_geometry_3d(double *coords, int nfaces,
|
||||
int *nodepos, int *facenodes, double *fnormals,
|
||||
double *fcentroids, double *fareas)
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
|
||||
/* Assume 3D for now */
|
||||
const int ndims = 3;
|
||||
int f;
|
||||
double x[3];
|
||||
double u[3];
|
||||
double v[3];
|
||||
double w[3];
|
||||
|
||||
int i,k;
|
||||
int node;
|
||||
|
||||
double cface[3] = {0};
|
||||
double n[3] = {0};
|
||||
double twothirds = 0.666666666666666666666666666667;
|
||||
double a;
|
||||
int num_face_nodes;
|
||||
double area;
|
||||
/*#pragma omp parallel for */
|
||||
|
||||
/*#pragma omp parallel for shared(fnormals,fcentroids,fareas)*/
|
||||
#pragma omp parallel for default(none) \
|
||||
private(f,x,u,v,w,i,k,node,cface,n,a,num_face_nodes,area) \
|
||||
shared(fnormals,fcentroids,fareas \
|
||||
,coords, nfaces, nodepos, facenodes, twothirds)
|
||||
for (f=0; f<nfaces; ++f)
|
||||
{
|
||||
for(i=0; i<ndims; ++i) x[i] = 0.0;
|
||||
for(i=0; i<ndims; ++i) n[i] = 0.0;
|
||||
for(i=0; i<ndims; ++i) cface[i] = 0.0;
|
||||
|
||||
/* average node */
|
||||
for(k=nodepos[f]; k<nodepos[f+1]; ++k)
|
||||
{
|
||||
node = facenodes[k];
|
||||
for (i=0; i<ndims; ++i) x[i] += coords[3*node+i];
|
||||
}
|
||||
num_face_nodes = nodepos[f+1] - nodepos[f];
|
||||
for(i=0; i<ndims; ++i) x[i] /= num_face_nodes;
|
||||
|
||||
|
||||
|
||||
/* compute first vector u (to the last node in the face) */
|
||||
node = facenodes[nodepos[f+1]-1];
|
||||
for(i=0; i<ndims; ++i) u[i] = coords[3*node+i] - x[i];
|
||||
|
||||
area=0.0;
|
||||
/* Compute triangular contrib. to face normal and face centroid*/
|
||||
for(k=nodepos[f]; k<nodepos[f+1]; ++k)
|
||||
{
|
||||
|
||||
|
||||
node = facenodes[k];
|
||||
for (i=0; i<ndims; ++i) v[i] = coords[3*node+i] - x[i];
|
||||
|
||||
cross(u,v,w);
|
||||
a = 0.5*norm(w);
|
||||
area += a;
|
||||
/* if(!(a>0))
|
||||
{
|
||||
fprintf(stderr, "Internal error in compute_face_geometry.");
|
||||
}
|
||||
*/
|
||||
/* face normal */
|
||||
for (i=0; i<ndims; ++i) n[i] += w[i];
|
||||
|
||||
/* face centroid */
|
||||
for (i=0; i<ndims; ++i)
|
||||
cface[i] += a*(x[i]+twothirds*0.5*(u[i]+v[i]));
|
||||
|
||||
/* Store v in u for next iteration */
|
||||
for (i=0; i<ndims; ++i) u[i] = v[i];
|
||||
}
|
||||
|
||||
/* Store face normal and face centroid */
|
||||
for (i=0; i<ndims; ++i)
|
||||
{
|
||||
/* normal is scaled with face area */
|
||||
fnormals [3*f+i] = 0.5*n[i];
|
||||
fcentroids[3*f+i] = cface[i]/area;
|
||||
}
|
||||
fareas[f] = area;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void
|
||||
compute_edge_geometry_2d(
|
||||
/* in */ double *node_coords,
|
||||
/* in */ int num_edges,
|
||||
/* in */ int *edge_node_pos,
|
||||
/* in */ int *edge_nodes,
|
||||
/* out */ double *edge_normals,
|
||||
/* out */ double *edge_midpoints,
|
||||
/* out */ double *edge_lengths)
|
||||
{
|
||||
const int num_dims = 2;
|
||||
|
||||
/* offsets to each of the nodes in a compacted edge */
|
||||
const int a_ofs = 0;
|
||||
const int b_ofs = 1;
|
||||
|
||||
/* offsets to each dimension is a compacted point */
|
||||
const int x_ofs = 0;
|
||||
const int y_ofs = 1;
|
||||
|
||||
int edge; /* edge index */
|
||||
int a_nod, b_nod; /* node indices */
|
||||
double a_x, a_y, b_x, b_y; /* node coordinates */
|
||||
double v_x, v_y; /* vector elements */
|
||||
|
||||
/* decompose each edge into a tuple (a,b) between two points and
|
||||
* compute properties for that face. hopefully the host has enough
|
||||
* cache pages to keep both input and output at the same time, and
|
||||
* registers for all the local variables */
|
||||
for (edge = 0; edge < num_edges; ++edge)
|
||||
{
|
||||
/* an edge in 2D can only have starting and ending point
|
||||
* check that there are exactly two nodes till the next edge */
|
||||
assert (edge_node_pos[edge + 1] - edge_node_pos[edge] == num_dims);
|
||||
|
||||
/* get the first and last point on the edge */
|
||||
a_nod = edge_nodes[edge_node_pos[edge] + a_ofs];
|
||||
b_nod = edge_nodes[edge_node_pos[edge] + b_ofs];
|
||||
|
||||
/* extract individual coordinates for the points */
|
||||
a_x = node_coords[a_nod * num_dims + x_ofs];
|
||||
a_y = node_coords[a_nod * num_dims + y_ofs];
|
||||
b_x = node_coords[b_nod * num_dims + x_ofs];
|
||||
b_y = node_coords[b_nod * num_dims + y_ofs];
|
||||
|
||||
/* compute edge center -- average of node coordinates */
|
||||
edge_midpoints[edge * num_dims + x_ofs] = (a_x + b_x) * 0.5;
|
||||
edge_midpoints[edge * num_dims + y_ofs] = (a_y + b_y) * 0.5;
|
||||
|
||||
/* vector from first to last point */
|
||||
v_x = b_x - a_x;
|
||||
v_y = b_y - a_y;
|
||||
|
||||
/* two-dimensional (unary) cross product analog that makes the
|
||||
* "triple" (dot-cross) product zero, i.e. it's a normal; the
|
||||
* direction of this vector is such that it will be pointing
|
||||
* inwards when enumerating nodes clock-wise */
|
||||
edge_normals[edge * num_dims + x_ofs] = +v_y;
|
||||
edge_normals[edge * num_dims + y_ofs] = -v_x;
|
||||
|
||||
/* Euclidian norm in two dimensions is magnitude of edge */
|
||||
edge_lengths[edge] = sqrt(v_x*v_x + v_y*v_y);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
void
|
||||
compute_face_geometry(int ndims, double *coords, int nfaces,
|
||||
int *nodepos, int *facenodes, double *fnormals,
|
||||
double *fcentroids, double *fareas)
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
if (ndims == 3)
|
||||
{
|
||||
compute_face_geometry_3d(coords, nfaces, nodepos, facenodes,
|
||||
fnormals, fcentroids, fareas);
|
||||
}
|
||||
else if (ndims == 2)
|
||||
{
|
||||
/* two-dimensional interfaces are called 'edges' */
|
||||
compute_edge_geometry_2d(coords, nfaces, nodepos, facenodes,
|
||||
fnormals, fcentroids, fareas);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void
|
||||
compute_cell_geometry_3d(double *coords,
|
||||
int *nodepos, int *facenodes, int *neighbors,
|
||||
double *fnormals,
|
||||
double *fcentroids,
|
||||
int ncells, int *facepos, int *cellfaces,
|
||||
double *ccentroids, double *cvolumes)
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
const int ndims = 3;
|
||||
int i,k, f,c;
|
||||
int face,node;
|
||||
double x[3];
|
||||
double u[3];
|
||||
double v[3];
|
||||
double w[3];
|
||||
double xcell[3];
|
||||
double ccell[3];
|
||||
double cface[3] = {0};
|
||||
int num_faces;
|
||||
double volume;
|
||||
double tet_volume, subnormal_sign;
|
||||
double twothirds = 0.666666666666666666666666666667;
|
||||
#pragma omp parallel for default(none) \
|
||||
private(i,k,f,c,face,node,x,u,v,w,xcell \
|
||||
,ccell ,cface,num_faces,volume, tet_volume, subnormal_sign) \
|
||||
shared(coords,nodepos,facenodes,neighbors,twothirds, \
|
||||
fnormals,fcentroids,facepos,cellfaces,ccentroids,cvolumes) \
|
||||
firstprivate(ncells)
|
||||
for (c=0; c<ncells; ++c)
|
||||
{
|
||||
|
||||
|
||||
for(i=0; i<ndims; ++i) xcell[i] = 0.0;
|
||||
for(i=0; i<ndims; ++i) ccell[i] = 0.0;
|
||||
|
||||
|
||||
/*
|
||||
* Approximate cell center as average of face centroids
|
||||
*/
|
||||
for(f=facepos[c]; f<facepos[c+1]; ++f)
|
||||
{
|
||||
face = cellfaces[f];
|
||||
for (i=0; i<ndims; ++i) xcell[i] += fcentroids[3*face+i];
|
||||
}
|
||||
num_faces = facepos[c+1] - facepos[c];
|
||||
|
||||
for(i=0; i<ndims; ++i) xcell[i] /= num_faces;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* For all faces, add tetrahedron's volume and centroid to
|
||||
* 'cvolume' and 'ccentroid'.
|
||||
*/
|
||||
volume=0.0;
|
||||
for(f=facepos[c]; f<facepos[c+1]; ++f)
|
||||
{
|
||||
int num_face_nodes;
|
||||
|
||||
for(i=0; i<ndims; ++i) x[i] = 0.0;
|
||||
for(i=0; i<ndims; ++i) cface[i] = 0.0;
|
||||
|
||||
face = cellfaces[f];
|
||||
|
||||
/* average face node x */
|
||||
for(k=nodepos[face]; k<nodepos[face+1]; ++k)
|
||||
{
|
||||
node = facenodes[k];
|
||||
for (i=0; i<ndims; ++i) x[i] += coords[3*node+i];
|
||||
}
|
||||
num_face_nodes = nodepos[face+1] - nodepos[face];
|
||||
for(i=0; i<ndims; ++i) x[i] /= num_face_nodes;
|
||||
|
||||
|
||||
|
||||
/* compute first vector u (to the last node in the face) */
|
||||
node = facenodes[nodepos[face+1]-1];
|
||||
for(i=0; i<ndims; ++i) u[i] = coords[3*node+i] - x[i];
|
||||
|
||||
|
||||
/* Compute triangular contributions to face normal and face centroid */
|
||||
for(k=nodepos[face]; k<nodepos[face+1]; ++k)
|
||||
{
|
||||
|
||||
node = facenodes[k];
|
||||
for (i=0; i<ndims; ++i) v[i] = coords[3*node+i] - x[i];
|
||||
|
||||
cross(u,v,w);
|
||||
|
||||
|
||||
|
||||
tet_volume = 0.0;
|
||||
for(i=0; i<ndims; ++i){
|
||||
tet_volume += w[i]*(x[i]-xcell[i]);
|
||||
}
|
||||
tet_volume *= 0.5 / 3;
|
||||
|
||||
subnormal_sign=0.0;
|
||||
for(i=0; i<ndims; ++i){
|
||||
subnormal_sign += w[i]*fnormals[3*face+i];
|
||||
}
|
||||
|
||||
if(subnormal_sign < 0.0){
|
||||
tet_volume = -tet_volume;
|
||||
}
|
||||
if(!(neighbors[2*face+0]==c)){
|
||||
tet_volume = -tet_volume;
|
||||
}
|
||||
volume += tet_volume;
|
||||
/* face centroid of triangle */
|
||||
for (i=0; i<ndims; ++i) cface[i] = (x[i]+(twothirds)*0.5*(u[i]+v[i]));
|
||||
|
||||
/* Cell centroid */
|
||||
for (i=0; i<ndims; ++i) ccell[i] += tet_volume * 3/4.0*(cface[i] - xcell[i]);
|
||||
|
||||
|
||||
/* Store v in u for next iteration */
|
||||
for (i=0; i<ndims; ++i) u[i] = v[i];
|
||||
}
|
||||
}
|
||||
for (i=0; i<ndims; ++i) ccentroids[3*c+i] = xcell[i] + ccell[i]/volume;
|
||||
cvolumes[c] = volume;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void
|
||||
compute_cell_geometry_2d(
|
||||
/* in */ double *node_coords,
|
||||
/* in */ int *edge_node_pos,
|
||||
/* in */ int *edge_nodes,
|
||||
/* in */ double *edge_midpoints,
|
||||
/* in */ int num_cells,
|
||||
/* in */ int *cell_edge_pos,
|
||||
/* in */ int *cell_edges,
|
||||
/* out */ double *cell_centers,
|
||||
/* out */ double *cell_areas)
|
||||
{
|
||||
const int num_dims = 2;
|
||||
|
||||
/* offsets to each of the nodes in a compacted edge */
|
||||
const int a_ofs = 0;
|
||||
const int b_ofs = 1;
|
||||
|
||||
/* offsets to each dimension is a compacted point */
|
||||
const int x_ofs = 0;
|
||||
const int y_ofs = 1;
|
||||
|
||||
int cell; /* cell index */
|
||||
int num_nodes; /* number of vertices in current cell */
|
||||
int edge_ndx; /* relative edge index within cell */
|
||||
int edge; /* absolute cell index */
|
||||
double center_x; /* x-coordinate for cell barycenter */
|
||||
double center_y; /* y-coordinate for cell barycenter */
|
||||
double area; /* (accumulated) cell area */
|
||||
int a_nod, b_nod; /* node indices for edge start and end points */
|
||||
double a_x, a_y,
|
||||
b_x, b_y; /* vectors from center to edge points */
|
||||
|
||||
for (cell = 0; cell < num_cells; ++cell)
|
||||
{
|
||||
/* since the cell is a closed polygon, each point serves as the starting
|
||||
* point of one edge and the ending point of another; thus there is as
|
||||
* many vertices as there are edges */
|
||||
num_nodes = cell_edge_pos[cell + 1] - cell_edge_pos[cell];
|
||||
|
||||
/* to enumerate all vertices of a cell, we would have to expand the
|
||||
* edges and then remove duplicates. however, the centroid of each
|
||||
* edge contains half of the two vertices that are incident on it. if
|
||||
* we instead sum all the face centroids, we get the sum of all the
|
||||
* vertices */
|
||||
center_x = 0.;
|
||||
center_y = 0.;
|
||||
for (edge_ndx = cell_edge_pos[cell];
|
||||
edge_ndx < cell_edge_pos[cell + 1]; ++edge_ndx)
|
||||
{
|
||||
edge = cell_edges[edge_ndx];
|
||||
center_x += edge_midpoints[edge * num_dims + x_ofs];
|
||||
center_y += edge_midpoints[edge * num_dims + y_ofs];
|
||||
}
|
||||
center_x /= (double) num_nodes;
|
||||
center_y /= (double) num_nodes;
|
||||
cell_centers[cell * num_dims + x_ofs] = center_x;
|
||||
cell_centers[cell * num_dims + y_ofs] = center_y;
|
||||
|
||||
/* triangulate the polygon by introducing the cell center and then new
|
||||
* internal edges from this center to the vertices. the total area of
|
||||
* the cell is the sum of area of these sub-triangles */
|
||||
area = 0.;
|
||||
for (edge_ndx = cell_edge_pos[cell];
|
||||
edge_ndx < cell_edge_pos[cell + 1]; ++edge_ndx)
|
||||
{
|
||||
/* indirect lookup of edge index (from array that contains all the
|
||||
* edge indices for a certain cell) */
|
||||
edge = cell_edges[edge_ndx];
|
||||
|
||||
/* get the first and last point on the edge */
|
||||
a_nod = edge_nodes[edge_node_pos[edge] + a_ofs];
|
||||
b_nod = edge_nodes[edge_node_pos[edge] + b_ofs];
|
||||
|
||||
/* vector from center to each of the nodes */
|
||||
a_x = node_coords[a_nod * num_dims + x_ofs] - center_x;
|
||||
a_y = node_coords[a_nod * num_dims + y_ofs] - center_y;
|
||||
b_x = node_coords[b_nod * num_dims + x_ofs] - center_x;
|
||||
b_y = node_coords[b_nod * num_dims + y_ofs] - center_y;
|
||||
|
||||
/* two-dimensional (binary) cross product analog that has length
|
||||
* equal to the parallelogram spanned by the two vectors (but which
|
||||
* is a scalar). the sign tells us the orientation between the nodes
|
||||
* a and b, but we are not interested in that, just the area */
|
||||
area += fabs(a_x * b_y - a_y * b_x);
|
||||
}
|
||||
/* we summed parallelograms which are twice the size of the triangles
|
||||
* that make up the cell; divide out the half for all terms here */
|
||||
area *= 0.5;
|
||||
cell_areas[cell] = area;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
void
|
||||
compute_cell_geometry(int ndims, double *coords,
|
||||
int *nodepos, int *facenodes, int *neighbors,
|
||||
double *fnormals,
|
||||
double *fcentroids,
|
||||
int ncells, int *facepos, int *cellfaces,
|
||||
double *ccentroids, double *cvolumes)
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
if (ndims == 3)
|
||||
{
|
||||
compute_cell_geometry_3d(coords, nodepos, facenodes,
|
||||
neighbors, fnormals, fcentroids, ncells,
|
||||
facepos, cellfaces, ccentroids, cvolumes);
|
||||
}
|
||||
else if (ndims == 2)
|
||||
{
|
||||
compute_cell_geometry_2d(coords, nodepos, facenodes, fcentroids,
|
||||
ncells, facepos, cellfaces, ccentroids,
|
||||
cvolumes);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010 (c) SINTEF ICT, Applied Mathematics.
|
||||
* Jostein R. Natvig <Jostein.R.Natvig at sintef.no>
|
||||
*/
|
||||
#ifndef MRST_GEOMETRY_H_INCLUDED
|
||||
#define MRST_GEOMETRY_H_INCLUDED
|
||||
|
||||
void compute_face_geometry(int ndims, double *coords, int nfaces,
|
||||
int *nodepos, int *facenodes,
|
||||
double *fnormals, double *fcentroids,
|
||||
double *fareas);
|
||||
void compute_cell_geometry(int ndims, double *coords,
|
||||
int *nodepos, int *facenodes, int *neighbours,
|
||||
double *fnormals,
|
||||
double *fcentroids, int ncells,
|
||||
int *facepos, int *cellfaces,
|
||||
double *ccentroids, double *cvolumes);
|
||||
|
||||
#endif /* MRST_GEOMETRY_H_INCLUDED */
|
|
@ -1,956 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: preprocess.c
|
||||
//
|
||||
// Created: Fri Jun 19 08:42:39 2009
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// $Revision$
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010, 2011, 2012 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "preprocess.h"
|
||||
#include "uniquepoints.h"
|
||||
#include "facetopology.h"
|
||||
|
||||
|
||||
#define MIN(i,j) ((i)<(j) ? (i) : (j))
|
||||
#define MAX(i,j) ((i)>(j) ? (i) : (j))
|
||||
|
||||
static void
|
||||
compute_cell_index(const int dims[3], int i, int j, int *neighbors, int len);
|
||||
|
||||
static int
|
||||
checkmemory(int nz, struct processed_grid *out, int **intersections);
|
||||
|
||||
static void
|
||||
process_vertical_faces(int direction,
|
||||
int **intersections,
|
||||
int *plist, int *work,
|
||||
struct processed_grid *out);
|
||||
|
||||
static void
|
||||
process_horizontal_faces(int **intersections,
|
||||
int *plist,
|
||||
struct processed_grid *out);
|
||||
|
||||
static int
|
||||
linearindex(const int dims[3], int i, int j, int k)
|
||||
{
|
||||
assert (0 <= i);
|
||||
assert (0 <= j);
|
||||
assert (0 <= k);
|
||||
|
||||
assert (i < dims[0]);
|
||||
assert (j < dims[1]);
|
||||
assert (k < dims[2]);
|
||||
|
||||
return i + dims[0]*(j + dims[1]*k);
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Given a vector <field> with k index running faster than i running
|
||||
faster than j, and Cartesian dimensions <dims>, find pointers to the
|
||||
(i-1, j-1, 0), (i-1, j, 0), (i, j-1, 0) and (i, j, 0) elements of
|
||||
field. */
|
||||
static void
|
||||
igetvectors(int dims[3], int i, int j, int *field, int *v[])
|
||||
{
|
||||
int im = MAX(1, i ) - 1;
|
||||
int ip = MIN(dims[0], i+1) - 1;
|
||||
int jm = MAX(1, j ) - 1;
|
||||
int jp = MIN(dims[1], j+1) - 1;
|
||||
|
||||
v[0] = field + dims[2]*(im + dims[0]* jm);
|
||||
v[1] = field + dims[2]*(im + dims[0]* jp);
|
||||
v[2] = field + dims[2]*(ip + dims[0]* jm);
|
||||
v[3] = field + dims[2]*(ip + dims[0]* jp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Special purpose
|
||||
|
||||
Convert from k-index to Cartesian index i+nx*(j+ny*k) for every
|
||||
other element in neighbors.
|
||||
|
||||
*/
|
||||
static void
|
||||
compute_cell_index(const int dims[3], int i, int j,
|
||||
int *neighbors, int len)
|
||||
{
|
||||
int k;
|
||||
|
||||
if (((i < 0) || (i >= dims[0])) || /* 'i' outside [0, dims[0]) */
|
||||
((j < 0) || (j >= dims[1]))) { /* 'j' outside [0, dims[1]) */
|
||||
|
||||
for (k = 0; k < len; k += 2) {
|
||||
neighbors[k] = -1; /* Neighbour is outside domain */
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (k = 0; k < len; k += 2) {
|
||||
if (neighbors[k] != -1) {
|
||||
neighbors[k] = linearindex(dims, i, j, neighbors[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Ensure there's sufficient memory */
|
||||
static int
|
||||
checkmemory(int nz, struct processed_grid *out, int **intersections)
|
||||
{
|
||||
int r, m, n, ok;
|
||||
|
||||
/* Ensure there is enough space to manage the (pathological) case
|
||||
* of every single cell on one side of a fault connecting to all
|
||||
* cells on the other side of the fault (i.e., an all-to-all cell
|
||||
* connectivity pairing). */
|
||||
r = (2*nz + 2) * (2*nz + 2);
|
||||
m = out->m;
|
||||
n = out->n;
|
||||
|
||||
if (out->number_of_faces + r > m) {
|
||||
m += MAX(m / 2, 2 * r);
|
||||
}
|
||||
if (out->face_ptr[out->number_of_faces] + 6*r > n) {
|
||||
n += MAX(n / 2, 12 * r);
|
||||
}
|
||||
|
||||
ok = m == out->m;
|
||||
if (! ok) {
|
||||
void *p1, *p2, *p3, *p4;
|
||||
|
||||
p1 = realloc(*intersections , 4*m * sizeof **intersections);
|
||||
p2 = realloc(out->face_neighbors, 2*m * sizeof *out->face_neighbors);
|
||||
p3 = realloc(out->face_ptr , (m+1) * sizeof *out->face_ptr);
|
||||
p4 = realloc(out->face_tag , 1*m * sizeof *out->face_tag);
|
||||
|
||||
if (p1 != NULL) { *intersections = p1; }
|
||||
if (p2 != NULL) { out->face_neighbors = p2; }
|
||||
if (p3 != NULL) { out->face_ptr = p3; }
|
||||
if (p4 != NULL) { out->face_tag = p4; }
|
||||
|
||||
ok = (p1 != NULL) && (p2 != NULL) && (p3 != NULL) && (p4 != NULL);
|
||||
|
||||
if (ok) { out->m = m; }
|
||||
}
|
||||
|
||||
if (ok && (n != out->n)) {
|
||||
void *p1;
|
||||
|
||||
p1 = realloc(out->face_nodes, n * sizeof *out->face_nodes);
|
||||
|
||||
ok = p1 != NULL;
|
||||
|
||||
if (ok) {
|
||||
out->face_nodes = p1;
|
||||
out->n = n;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
For each vertical face (i.e. i or j constant),
|
||||
-find point numbers for the corners and
|
||||
-cell neighbors.
|
||||
-new points on faults defined by two intgersecting lines.
|
||||
|
||||
direction == 0 : constant-i faces.
|
||||
direction == 1 : constant-j faces.
|
||||
*/
|
||||
static void
|
||||
process_vertical_faces(int direction,
|
||||
int **intersections,
|
||||
int *plist, int *work,
|
||||
struct processed_grid *out)
|
||||
{
|
||||
int i,j;
|
||||
int *cornerpts[4];
|
||||
int d[3];
|
||||
int f;
|
||||
enum face_tag tag[] = { LEFT, BACK };
|
||||
int *tmp;
|
||||
int nx = out->dimensions[0];
|
||||
int ny = out->dimensions[1];
|
||||
int nz = out->dimensions[2];
|
||||
int startface;
|
||||
int num_intersections;
|
||||
int *ptr;
|
||||
int len;
|
||||
|
||||
assert ((direction == 0) || (direction == 1));
|
||||
|
||||
d[0] = 2 * (nx + 0);
|
||||
d[1] = 2 * (ny + 0);
|
||||
d[2] = 2 * (nz + 1);
|
||||
|
||||
for (j = 0; j < ny + direction; ++j) {
|
||||
for (i = 0; i < nx + (1 - direction); ++i) {
|
||||
|
||||
if (! checkmemory(nz, out, intersections)) {
|
||||
fprintf(stderr,
|
||||
"Could not allocate enough space in "
|
||||
"process_vertical_faces()\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Vectors of point numbers */
|
||||
igetvectors(d, 2*i + direction, 2*j + (1 - direction),
|
||||
plist, cornerpts);
|
||||
|
||||
if (direction == 1) {
|
||||
/* 1 3 0 1 */
|
||||
/* ---> */
|
||||
/* 0 2 2 3 */
|
||||
/* rotate clockwise */
|
||||
tmp = cornerpts[1];
|
||||
cornerpts[1] = cornerpts[0];
|
||||
cornerpts[0] = cornerpts[2];
|
||||
cornerpts[2] = cornerpts[3];
|
||||
cornerpts[3] = tmp;
|
||||
}
|
||||
|
||||
/* int startface = ftab->position; */
|
||||
startface = out->number_of_faces;
|
||||
/* int num_intersections = *npoints - npillarpoints; */
|
||||
num_intersections = out->number_of_nodes -
|
||||
out->number_of_nodes_on_pillars;
|
||||
|
||||
/* Establish new connections (faces) along pillar pair. */
|
||||
findconnections(2*nz + 2, cornerpts,
|
||||
*intersections + 4*num_intersections,
|
||||
work, out);
|
||||
|
||||
/* Start of ->face_neighbors[] for this set of connections. */
|
||||
ptr = out->face_neighbors + 2*startface;
|
||||
|
||||
/* Total number of cells (both sides) connected by this
|
||||
* set of connections (faces). */
|
||||
len = 2*out->number_of_faces - 2*startface;
|
||||
|
||||
/* Derive inter-cell connectivity (i.e. ->face_neighbors)
|
||||
* of global (uncompressed) cells for this set of
|
||||
* connections (faces). */
|
||||
compute_cell_index(out->dimensions, i-1+direction, j-direction, ptr , len);
|
||||
compute_cell_index(out->dimensions, i , j , ptr + 1, len);
|
||||
|
||||
/* Tag the new faces */
|
||||
f = startface;
|
||||
for (; f < out->number_of_faces; ++f) {
|
||||
out->face_tag[f] = tag[direction];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
For each horizontal face (i.e. k constant),
|
||||
-find point numbers for the corners and
|
||||
-cell neighbors.
|
||||
|
||||
Also define map from logically Cartesian
|
||||
cell index to local cell index 0, ..., #<active cells>. Exclude
|
||||
cells that are have collapsed coordinates. (This includes cells with
|
||||
ACTNUM==0)
|
||||
|
||||
*/
|
||||
static void
|
||||
process_horizontal_faces(int **intersections,
|
||||
int *plist,
|
||||
struct processed_grid *out)
|
||||
{
|
||||
int i,j,k;
|
||||
|
||||
int nx = out->dimensions[0];
|
||||
int ny = out->dimensions[1];
|
||||
int nz = out->dimensions[2];
|
||||
|
||||
int *cell = out->local_cell_index;
|
||||
int cellno = 0;
|
||||
int *f, *n, *c[4];
|
||||
int prevcell, thiscell;
|
||||
int idx;
|
||||
|
||||
/* dimensions of plist */
|
||||
int d[3];
|
||||
d[0] = 2*nx;
|
||||
d[1] = 2*ny;
|
||||
d[2] = 2+2*nz;
|
||||
|
||||
|
||||
for(j=0; j<ny; ++j) {
|
||||
for (i=0; i<nx; ++i) {
|
||||
|
||||
|
||||
if (! checkmemory(nz, out, intersections)) {
|
||||
fprintf(stderr,
|
||||
"Could not allocate enough space in "
|
||||
"process_horizontal_faces()\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
f = out->face_nodes + out->face_ptr[out->number_of_faces];
|
||||
n = out->face_neighbors + 2*out->number_of_faces;
|
||||
|
||||
|
||||
/* Vectors of point numbers */
|
||||
igetvectors(d, 2*i+1, 2*j+1, plist, c);
|
||||
|
||||
prevcell = -1;
|
||||
|
||||
|
||||
for (k = 1; k<nz*2+1; ++k){
|
||||
|
||||
/* Skip if space between face k and face k+1 is collapsed. */
|
||||
/* Note that inactive cells (with ACTNUM==0) have all been */
|
||||
/* collapsed in finduniquepoints. */
|
||||
if (c[0][k] == c[0][k+1] && c[1][k] == c[1][k+1] &&
|
||||
c[2][k] == c[2][k+1] && c[3][k] == c[3][k+1]){
|
||||
|
||||
/* If the pinch is a cell: */
|
||||
if (k%2){
|
||||
idx = linearindex(out->dimensions, i,j,(k-1)/2);
|
||||
cell[idx] = -1;
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
||||
if (k%2){
|
||||
/* Add face */
|
||||
*f++ = c[0][k];
|
||||
*f++ = c[2][k];
|
||||
*f++ = c[3][k];
|
||||
*f++ = c[1][k];
|
||||
|
||||
out->face_tag[ out->number_of_faces] = TOP;
|
||||
out->face_ptr[++out->number_of_faces] = f - out->face_nodes;
|
||||
|
||||
thiscell = linearindex(out->dimensions, i,j,(k-1)/2);
|
||||
*n++ = prevcell;
|
||||
*n++ = prevcell = thiscell;
|
||||
|
||||
cell[thiscell] = cellno++;
|
||||
|
||||
}
|
||||
else{
|
||||
if (prevcell != -1){
|
||||
/* Add face */
|
||||
*f++ = c[0][k];
|
||||
*f++ = c[2][k];
|
||||
*f++ = c[3][k];
|
||||
*f++ = c[1][k];
|
||||
|
||||
out->face_tag[ out->number_of_faces] = TOP;
|
||||
out->face_ptr[++out->number_of_faces] = f - out->face_nodes;
|
||||
|
||||
*n++ = prevcell;
|
||||
*n++ = prevcell = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out->number_of_cells = cellno;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
On input,
|
||||
L points to 4 ints that indirectly refers to points in c.
|
||||
c points to array of coordinates [x0,y0,z0,x1,y1,z1,...,xn,yn,zn].
|
||||
pt points to array of 3 doubles.
|
||||
|
||||
On output,
|
||||
pt holds coordinates to intersection between lines given by point
|
||||
numbers L[0]-L[1] and L[2]-L[3].
|
||||
*/
|
||||
static void approximate_intersection_pt(int *L, double *c, double *pt)
|
||||
{
|
||||
double a;
|
||||
double z0, z1, z2, z3;
|
||||
double b1, b2;
|
||||
double x1, y1;
|
||||
double x2, y2;
|
||||
double z;
|
||||
|
||||
/* no intersection on pillars expected here! */
|
||||
assert (L[0] != L[2]);
|
||||
assert (L[1] != L[3]);
|
||||
|
||||
z0 = c[3*L[0] + 2];
|
||||
z1 = c[3*L[1] + 2];
|
||||
z2 = c[3*L[2] + 2];
|
||||
z3 = c[3*L[3] + 2];
|
||||
|
||||
/* find parameter a where lines L0L1 and L2L3 have same
|
||||
* z-coordinate */
|
||||
if (fabs((z1 - z0) - (z3 - z2)) > 0.0) {
|
||||
|
||||
a = (z2 - z0) / ((z1 - z0) - (z3 - z2));
|
||||
|
||||
} else {
|
||||
|
||||
a = 0;
|
||||
|
||||
}
|
||||
|
||||
/* the corresponding z-coordinate is */
|
||||
z = z0*(1.0 - a) + z1*a;
|
||||
|
||||
|
||||
/* find point (x1, y1, z) on pillar 1 */
|
||||
b1 = (z2 - z) / (z2 - z0);
|
||||
b2 = (z - z0) / (z2 - z0);
|
||||
x1 = c[3*L[0] + 0]*b1 + c[3*L[2] + 0]*b2;
|
||||
y1 = c[3*L[0] + 1]*b1 + c[3*L[2] + 1]*b2;
|
||||
|
||||
/* find point (x2, y2, z) on pillar 2 */
|
||||
b1 = (z - z3) / (z1 - z3);
|
||||
b2 = (z1 - z) / (z1 - z3);
|
||||
x2 = c[3*L[1] + 0]*b1 + c[3*L[3] + 0]*b2;
|
||||
y2 = c[3*L[1] + 1]*b1 + c[3*L[3] + 1]*b2;
|
||||
|
||||
/* horizontal lines are by definition ON the bilinear surface
|
||||
spanned by L0, L1, L2 and L3. find point (x, y, z) on
|
||||
horizontal line between point (x1, y1, z) and (x2, y2, z).*/
|
||||
pt[0] = x1*(1.0 - a) + x2*a;
|
||||
pt[1] = y1*(1.0 - a) + y2*a;
|
||||
pt[2] = z;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Compute x,y and z coordinates for points on each pillar. Then,
|
||||
append x,y and z coordinates for extra points on faults. */
|
||||
static void
|
||||
compute_intersection_coordinates(int *intersections,
|
||||
struct processed_grid *out)
|
||||
{
|
||||
int n = out->number_of_nodes;
|
||||
int np = out->number_of_nodes_on_pillars;
|
||||
int k;
|
||||
double *pt;
|
||||
int *itsct = intersections;
|
||||
/* Make sure the space allocated for nodes match the number of
|
||||
* node. */
|
||||
void *p = realloc (out->node_coordinates, 3*n*sizeof(double));
|
||||
if (p) {
|
||||
out->node_coordinates = p;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Could not allocate extra space for intersections\n");
|
||||
}
|
||||
|
||||
|
||||
/* Append intersections */
|
||||
pt = out->node_coordinates + 3*np;
|
||||
|
||||
for (k=np; k<n; ++k){
|
||||
approximate_intersection_pt(itsct, out->node_coordinates, pt);
|
||||
pt += 3;
|
||||
itsct += 4;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static int*
|
||||
copy_and_permute_actnum(int nx, int ny, int nz, const int *in, int *out)
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
int i,j,k;
|
||||
int *ptr = out;
|
||||
|
||||
/* Permute actnum such that values of each vertical stack of cells
|
||||
* are adjacent in memory, i.e.,
|
||||
*
|
||||
* out = [in(0,0,:), in(1,0,:),..., in(nx-1, ny-1,:)]
|
||||
*
|
||||
* in MATLAB pseudo-code.
|
||||
*/
|
||||
if (in != NULL) {
|
||||
for (j = 0; j < ny; ++j) {
|
||||
for (i = 0; i < nx; ++i) {
|
||||
for (k = 0; k < nz; ++k) {
|
||||
*ptr++ = in[i + nx*(j + ny*k)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* No explicit ACTNUM. Assume all cells active. */
|
||||
for (i = 0; i < nx * ny * nz; i++) {
|
||||
out[ i ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static double*
|
||||
copy_and_permute_zcorn(int nx, int ny, int nz, const double *in,
|
||||
double sign, double *out)
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
int i,j,k;
|
||||
double *ptr = out;
|
||||
/* Permute zcorn such that values of each vertical stack of cells
|
||||
* are adjacent in memory, i.e.,
|
||||
|
||||
out = [in(0,0,:), in(1,0,:),..., in(2*nx-1, 2*ny-1,:)]
|
||||
|
||||
in Matlab pseudo-code.
|
||||
*/
|
||||
for (j=0; j<2*ny; ++j){
|
||||
for (i=0; i<2*nx; ++i){
|
||||
for (k=0; k<2*nz; ++k){
|
||||
*ptr++ = sign * in[i+2*nx*(j+2*ny*k)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
static int
|
||||
get_zcorn_sign(int nx, int ny, int nz, const int *actnum,
|
||||
const double *zcorn, int *error)
|
||||
/* ------------------------------------------------------------------ */
|
||||
{
|
||||
/* Ensure that zcorn (i.e., depth) is strictly nondecreasing in
|
||||
the k-direction. This is required by the processign algorithm.
|
||||
|
||||
1) if z(i,j,k) <= z(i,j,k+1) for all (i,j,k), return 1.0
|
||||
|
||||
2) if -z(i,j,k) <=-z(i,j,k+1) for all (i,j,k), return -1.0
|
||||
|
||||
3) if (1) and (2) fails, return -1.0, and set *error = 1.
|
||||
|
||||
*/
|
||||
int sign;
|
||||
int i, j, k;
|
||||
int c1, c2;
|
||||
double z1, z2;
|
||||
|
||||
for (sign = 1; sign>-2; sign = sign - 2)
|
||||
{
|
||||
*error = 0;
|
||||
|
||||
for (j=0; j<2*ny; ++j){
|
||||
for (i=0; i<2*nx; ++i){
|
||||
for (k=0; k<2*nz-1; ++k){
|
||||
z1 = sign*zcorn[i+2*nx*(j+2*ny*(k))];
|
||||
z2 = sign*zcorn[i+2*nx*(j+2*ny*(k+1))];
|
||||
|
||||
c1 = i/2 + nx*(j/2 + ny*(k/2));
|
||||
c2 = i/2 + nx*(j/2 + ny*((k+1)/2));
|
||||
|
||||
assert (c1 < (nx * ny * nz));
|
||||
assert (c2 < (nx * ny * nz));
|
||||
|
||||
if (((actnum == NULL) ||
|
||||
(actnum[c1] && actnum[c2]))
|
||||
&& (z2 < z1)) {
|
||||
|
||||
fprintf(stderr, "\nZCORN should be strictly "
|
||||
"nondecreasing along pillars!\n");
|
||||
*error = 1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (!*error){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*error){
|
||||
fprintf(stderr, "Attempt to reverse sign in ZCORN failed.\n"
|
||||
"Grid definition may be broken\n");
|
||||
}
|
||||
|
||||
return sign;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ind2sub(const size_t nx,
|
||||
const size_t ny,
|
||||
const size_t nz,
|
||||
size_t c ,
|
||||
size_t *i, size_t *j, size_t *k)
|
||||
{
|
||||
assert (c < (nx * ny * nz));
|
||||
|
||||
#if defined(NDEBUG)
|
||||
(void) nz;
|
||||
#endif
|
||||
|
||||
*i = c % nx; c /= nx;
|
||||
*j = c % ny;
|
||||
*k = c / ny;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static double
|
||||
vert_size(const struct grdecl *in,
|
||||
const size_t c ,
|
||||
const size_t off[8])
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t i, j, k, nx, ny, start;
|
||||
double dz;
|
||||
const double *zcorn;
|
||||
|
||||
nx = in->dims[ 0 ];
|
||||
ny = in->dims[ 1 ];
|
||||
|
||||
ind2sub(nx, ny, in->dims[ 2 ], c, &i, &j, &k);
|
||||
|
||||
zcorn = in->zcorn;
|
||||
start = (2 * i) + (2 * nx)*((2 * j) + (2 * ny)*(2 * k));
|
||||
|
||||
for (k = 0, dz = 0.0; (! (fabs(dz) > 0)) && (k < 4); k++) {
|
||||
dz = zcorn[start + off[k + 4]] - zcorn[start + off[k]];
|
||||
}
|
||||
|
||||
return dz;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
is_lefthanded(const struct grdecl *in)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int active, searching;
|
||||
size_t nx, ny, nz, c;
|
||||
size_t origin, imax, jmax;
|
||||
size_t off[8];
|
||||
double dx[2], dy[2], dz, triple;
|
||||
const double *pt_coord;
|
||||
|
||||
nx = in->dims[0];
|
||||
ny = in->dims[1];
|
||||
nz = in->dims[2];
|
||||
|
||||
off[0] = 0;
|
||||
off[1] = off[0] + 1;
|
||||
off[2] = off[0] + (2 * nx);
|
||||
off[3] = off[2] + 1;
|
||||
off[4] = off[0] + ((2 * nx) * (2 * ny));
|
||||
off[5] = off[4] + 1;
|
||||
off[6] = off[4] + (2 * nx);
|
||||
off[7] = off[6] + 1;
|
||||
|
||||
pt_coord = in->coord;
|
||||
|
||||
origin = 0;
|
||||
imax = (nx + 0) * 1 * (2 * 3);
|
||||
jmax = (nx + 1) * (ny + 0) * (2 * 3);
|
||||
|
||||
dx[0] = pt_coord[imax + 0] - pt_coord[origin + 0];
|
||||
dy[0] = pt_coord[imax + 1] - pt_coord[origin + 1];
|
||||
|
||||
dx[1] = pt_coord[jmax + 0] - pt_coord[origin + 0];
|
||||
dy[1] = pt_coord[jmax + 1] - pt_coord[origin + 1];
|
||||
|
||||
c = 0; dz = 0.0;
|
||||
do {
|
||||
active = (in->actnum == NULL) || (in->actnum[c] != 0);
|
||||
|
||||
if (active) {
|
||||
dz = vert_size(in, c, off);
|
||||
}
|
||||
|
||||
searching = ! (active && (fabs(dz) > 0.0));
|
||||
|
||||
c += 1;
|
||||
} while (searching && (c < (nx * ny * nz)));
|
||||
|
||||
assert (! searching); /* active && (fabs(dz) > 0) */
|
||||
|
||||
/* Compute vector triple product to distinguish left-handed (<0)
|
||||
* from right-handed (>0) coordinate systems. */
|
||||
triple = dz * (dx[0]*dy[1] - dx[1]*dy[0]);
|
||||
|
||||
assert (fabs(triple) > 0.0);
|
||||
|
||||
return triple < 0.0;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
reverse_face_nodes(struct processed_grid *out)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int f, t, *i, *j;
|
||||
|
||||
for (f = 0; f < out->number_of_faces; f++) {
|
||||
i = out->face_nodes + (out->face_ptr[f + 0] + 0);
|
||||
j = out->face_nodes + (out->face_ptr[f + 1] - 1);
|
||||
|
||||
assert (i <= j);
|
||||
|
||||
while (i < j) {
|
||||
t = *i;
|
||||
*i = *j;
|
||||
*j = t;
|
||||
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Public interface
|
||||
*/
|
||||
void process_grdecl(const struct grdecl *in,
|
||||
double tolerance,
|
||||
struct processed_grid *out)
|
||||
{
|
||||
struct grdecl g;
|
||||
|
||||
size_t i;
|
||||
int sign, error, left_handed;
|
||||
int cellnum;
|
||||
|
||||
int *actnum, *iptr;
|
||||
int *global_cell_index;
|
||||
|
||||
double *zcorn;
|
||||
|
||||
const size_t BIGNUM = 64;
|
||||
const int nx = in->dims[0];
|
||||
const int ny = in->dims[1];
|
||||
const int nz = in->dims[2];
|
||||
const size_t nc = ((size_t) nx) * ((size_t) ny) * ((size_t) nz);
|
||||
|
||||
/* internal work arrays */
|
||||
int *work;
|
||||
int *plist;
|
||||
int *intersections;
|
||||
|
||||
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------*/
|
||||
/* Initialize output structure:
|
||||
1) allocate space for grid topology (which may need to be
|
||||
increased)
|
||||
2) set Cartesian imensions
|
||||
*/
|
||||
out->m = (int) (BIGNUM / 3);
|
||||
out->n = (int) BIGNUM;
|
||||
|
||||
out->face_neighbors = malloc( BIGNUM * sizeof *out->face_neighbors);
|
||||
out->face_nodes = malloc( out->n * sizeof *out->face_nodes);
|
||||
out->face_ptr = malloc((out->m + 1) * sizeof *out->face_ptr);
|
||||
out->face_tag = malloc( out->m * sizeof *out->face_tag);
|
||||
out->face_ptr[0] = 0;
|
||||
|
||||
out->dimensions[0] = in->dims[0];
|
||||
out->dimensions[1] = in->dims[1];
|
||||
out->dimensions[2] = in->dims[2];
|
||||
out->number_of_faces = 0;
|
||||
out->number_of_nodes = 0;
|
||||
out->number_of_cells = 0;
|
||||
|
||||
out->node_coordinates = NULL;
|
||||
out->local_cell_index = malloc(nc * sizeof *out->local_cell_index);
|
||||
|
||||
|
||||
|
||||
/* Do actual work here:*/
|
||||
|
||||
/* -----------------------------------------------------------------*/
|
||||
/* For each pillar, compare zcorn values for adjacent cells to
|
||||
* find the unique node z-coordinates specified by the input.
|
||||
* While here, enumerate unique points and assign point numbers
|
||||
* (in plist) for each cornerpoint cell. In other words, plist has
|
||||
* 8 node numbers for each cornerpoint cell.*/
|
||||
|
||||
/* initialize grdecl structure "g" that will be processd by
|
||||
* "finduniquepoints" */
|
||||
g.dims[0] = in->dims[0];
|
||||
g.dims[1] = in->dims[1];
|
||||
g.dims[2] = in->dims[2];
|
||||
|
||||
actnum = malloc (nc * sizeof *actnum);
|
||||
g.actnum = copy_and_permute_actnum(nx, ny, nz, in->actnum, actnum);
|
||||
|
||||
zcorn = malloc (nc * 8 * sizeof *zcorn);
|
||||
sign = get_zcorn_sign(nx, ny, nz, in->actnum, in->zcorn, &error);
|
||||
g.zcorn = copy_and_permute_zcorn(nx, ny, nz, in->zcorn, sign, zcorn);
|
||||
|
||||
g.coord = in->coord;
|
||||
|
||||
|
||||
/* allocate space for cornerpoint numbers plus INT_MIN (INT_MAX)
|
||||
* padding */
|
||||
plist = malloc(8 * (nc + ((size_t)nx)*((size_t)ny)) * sizeof *plist);
|
||||
|
||||
finduniquepoints(&g, plist, tolerance, out);
|
||||
|
||||
free (zcorn);
|
||||
free (actnum);
|
||||
|
||||
/* Determine if coordinate system is left handed or not. */
|
||||
left_handed = is_lefthanded(in);
|
||||
if (left_handed) {
|
||||
/* Reflect Y coordinates about XZ plane to create right-handed
|
||||
* coordinate system whilst processing intersections. */
|
||||
for (i = 1; i < ((size_t) 3) * out->number_of_nodes; i += 3) {
|
||||
out->node_coordinates[i] = -out->node_coordinates[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------*/
|
||||
/* Find face topology and face-to-cell connections */
|
||||
|
||||
/* internal */
|
||||
work = malloc(2 * ((size_t) (2*nz + 2)) * sizeof *work);
|
||||
for(i = 0; i < ((size_t)4) * (nz + 1); ++i) { work[i] = -1; }
|
||||
|
||||
/* internal array to store intersections */
|
||||
intersections = malloc(BIGNUM* sizeof(*intersections));
|
||||
|
||||
|
||||
|
||||
process_vertical_faces (0, &intersections, plist, work, out);
|
||||
process_vertical_faces (1, &intersections, plist, work, out);
|
||||
process_horizontal_faces ( &intersections, plist, out);
|
||||
|
||||
free (plist);
|
||||
free (work);
|
||||
|
||||
/* -----------------------------------------------------------------*/
|
||||
/* (re)allocate space for and compute coordinates of nodes that
|
||||
* arise from intersecting cells (faults) */
|
||||
compute_intersection_coordinates(intersections, out);
|
||||
|
||||
free (intersections);
|
||||
|
||||
/* -----------------------------------------------------------------*/
|
||||
/* Enumerate compressed cells:
|
||||
-make array [0...#cells-1] of global cell numbers
|
||||
-make [0...nx*ny*nz-1] array of local cell numbers,
|
||||
lexicographically ordered, used to remap out->face_neighbors
|
||||
*/
|
||||
global_cell_index = malloc(nc * sizeof *global_cell_index);
|
||||
cellnum = 0;
|
||||
for (i = 0; i < nc; ++i) {
|
||||
if (out->local_cell_index[i] != -1) {
|
||||
global_cell_index[cellnum] = (int) i;
|
||||
out->local_cell_index[i] = cellnum;
|
||||
cellnum++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remap out->face_neighbors */
|
||||
iptr = out->face_neighbors;
|
||||
for (i = 0; i < ((size_t) 2) * out->number_of_faces; ++i, ++iptr) {
|
||||
if (*iptr != -1){
|
||||
*iptr = out->local_cell_index[*iptr];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
free(out->local_cell_index);
|
||||
out->local_cell_index = global_cell_index;
|
||||
|
||||
/* Reflect Y coordinate back to original position if left-handed
|
||||
* coordinate system was detected and handled earlier. */
|
||||
if (left_handed) {
|
||||
for (i = 1; i < ((size_t) 3) * out->number_of_nodes; i += 3) {
|
||||
out->node_coordinates[i] = -out->node_coordinates[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* if sign==-1 in ZCORN preprocessing, the sign of the
|
||||
* z-coordinate need to change before we finish */
|
||||
if (sign == -1)
|
||||
{
|
||||
for (i = 2; i < ((size_t) 3) * out->number_of_nodes; i += 3)
|
||||
out->node_coordinates[i] *= sign;
|
||||
}
|
||||
|
||||
/* If an odd number of coordinate reflections were applied, the
|
||||
* processing routines--especially facetopology()--will produce
|
||||
* node orderings that lead to normals pointing from 2 to 1.
|
||||
* Reverse nodes to reestablish expected normal direction (and
|
||||
* positive cell volumes). */
|
||||
if (left_handed ^ (sign == -1)) {
|
||||
reverse_face_nodes(out);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
void free_processed_grid(struct processed_grid *g)
|
||||
{
|
||||
if( g ){
|
||||
free ( g->face_nodes );
|
||||
free ( g->face_ptr );
|
||||
free ( g->face_tag );
|
||||
free ( g->face_neighbors );
|
||||
free ( g->node_coordinates );
|
||||
free ( g->local_cell_index );
|
||||
}
|
||||
}
|
||||
|
||||
/* Local Variables: */
|
||||
/* c-basic-offset:4 */
|
||||
/* End: */
|
|
@ -1,154 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: preprocess.h
|
||||
//
|
||||
// Created: Fri Jun 19 08:43:04 2009
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// $Revision$
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_PREPROCESS_HEADER
|
||||
#define OPM_PREPROCESS_HEADER
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Low-level corner-point processing routines and supporting data structures.
|
||||
*
|
||||
* User code should typically employ higher-level routines such as
|
||||
* create_grid_cornerpoint() in order to construct fully formed UnstructuredGrid
|
||||
* data structures from a corner-point specification. Incidentally, the routines
|
||||
* provided by this module are used to implement function
|
||||
* create_grid_cornerpoint().
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Raw corner-point specification of a particular geological model.
|
||||
*/
|
||||
struct grdecl {
|
||||
int dims[3]; /**< Cartesian box dimensions. */
|
||||
const double *coord; /**< Pillar end-points. */
|
||||
const double *zcorn; /**< Corner-point depths. */
|
||||
const int *actnum; /**< Explicit "active" map. May be NULL.*/
|
||||
const double *mapaxes; /**< 6 Element rotation vector - can be NULL. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection taxonomy.
|
||||
*/
|
||||
enum face_tag {
|
||||
LEFT, /**< Connection topologically parallel to J-K plane. */
|
||||
BACK, /**< Connection topologically parallel to I-K plane. */
|
||||
TOP /**< Connection topologically parallel to I-J plane. */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Result structure representing minimal derived topology and geometry of
|
||||
* a geological model in corner-point format.
|
||||
*/
|
||||
struct processed_grid {
|
||||
int m; /**< Upper bound on "number_of_faces". For internal use in
|
||||
function process_grid()'s memory management. */
|
||||
int n; /**< Upper bound on "number_of_nodes". For internal use in
|
||||
function process_grid()'s memory management. */
|
||||
|
||||
int dimensions[3]; /**< Cartesian box dimensions. */
|
||||
|
||||
int number_of_faces; /**< Total number of unique grid faces
|
||||
(i.e., connections). */
|
||||
int *face_nodes; /**< Node (vertex) numbers of each face,
|
||||
stored sequentially. */
|
||||
int *face_ptr; /**< Start position for each face's
|
||||
`face_nodes'. */
|
||||
int *face_neighbors; /**< Global cell numbers. Two elements per
|
||||
face, stored sequentially. */
|
||||
enum face_tag *face_tag; /**< Classification of grid's individual
|
||||
connections (faces). */
|
||||
|
||||
int number_of_nodes; /**< Number of unique grid vertices. */
|
||||
int number_of_nodes_on_pillars; /**< Total number of unique cell
|
||||
vertices that lie on pillars. */
|
||||
double *node_coordinates; /**< Vertex coordinates. Three doubles
|
||||
(\f$x\f$, \f$y\f$, \f$z\f$) per vertex,
|
||||
stored sequentially. */
|
||||
|
||||
int number_of_cells; /**< Number of active grid cells. */
|
||||
int *local_cell_index; /**< Deceptively named local-to-global cell
|
||||
index mapping. */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Construct a prototypical grid representation from a corner-point
|
||||
* specification.
|
||||
*
|
||||
* Pinched cells will be removed irrespective of any explicit "active" map
|
||||
* in the geological model input specification. On input, the result
|
||||
* structure "out" must point to a valid management structure. In other
|
||||
* words, the result structure must point to a region of memory that is
|
||||
* typically backed by automatic or allocated (dynamic) storage duration.
|
||||
*
|
||||
* @param[in] g Corner-point specification. If "actnum" is NULL, then
|
||||
* the specification is interpreted as if all cells are
|
||||
* initially active.
|
||||
* @param[in] tol Absolute tolerance of node-coincidence.
|
||||
* @param[in,out] out Minimal grid representation featuring face-to-cell
|
||||
* neighbourship definition, vertex geometry, face's
|
||||
* constituent vertices, and local-to-global cell
|
||||
* mapping.
|
||||
*/
|
||||
void process_grdecl(const struct grdecl *g ,
|
||||
double tol,
|
||||
struct processed_grid *out);
|
||||
|
||||
/**
|
||||
* Release memory resources acquired in previous grid processing using
|
||||
* function process_grdecl().
|
||||
*
|
||||
* Note: This function releases the resources associated to the individual
|
||||
* fields of the processed_grid, but does not free() the structure itself.
|
||||
*
|
||||
* @param[in,out] g Prototypical grid representation obtained in an earlier
|
||||
* call to function process_grdecl().
|
||||
*/
|
||||
void free_processed_grid(struct processed_grid *g);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* OPM_PREPROCESS_HEADER */
|
||||
|
||||
|
||||
/* Local Variables: */
|
||||
/* c-basic-offset:4 */
|
||||
/* End: */
|
|
@ -1,383 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: uniquepoints.c
|
||||
//
|
||||
// Created: Fri Jun 19 08:46:05 2009
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// $Revision$
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "preprocess.h"
|
||||
#include "uniquepoints.h"
|
||||
|
||||
#define MIN(i,j) (((i) < (j)) ? (i) : (j))
|
||||
#define MAX(i,j) (((i) > (j)) ? (i) : (j))
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Compare function passed to qsortx */
|
||||
static int compare(const void *a, const void *b)
|
||||
{
|
||||
const double a0 = *(const double*) a;
|
||||
const double b0 = *(const double*) b;
|
||||
|
||||
/* { -1, a < b
|
||||
* compare(a,b) = { 0, a = b
|
||||
* { 1, a > b */
|
||||
return (a0 > b0) - (a0 < b0);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Creat sorted list of z-values in zcorn with actnum==1x */
|
||||
static int createSortedList(double *list, int n, int m,
|
||||
const double *z[], const int *a[])
|
||||
{
|
||||
int i,j;
|
||||
double *ptr = list;
|
||||
for (i=0; i<n; ++i){
|
||||
for (j=0; j<m; ++j){
|
||||
if (a[j][i/2]) *ptr++ = z[j][i];
|
||||
/* else fprintf(stderr, "skipping point in inactive cell\n"); */
|
||||
}
|
||||
}
|
||||
|
||||
qsort(list, ptr-list, sizeof(double), compare);
|
||||
return ptr-list;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Remove points less than <tolerance> apart in <list> of increasing
|
||||
doubles. */
|
||||
static int uniquify(int n, double *list, double tolerance)
|
||||
{
|
||||
int i;
|
||||
int pos;
|
||||
double val;
|
||||
|
||||
assert (!(tolerance < 0.0));
|
||||
|
||||
if (n<1) return 0;
|
||||
pos = 0;
|
||||
val = list[pos++];/* Keep first value */
|
||||
|
||||
for (i=1; i<n; ++i){
|
||||
if (val + tolerance < list [i]){
|
||||
val = list[i];
|
||||
list[pos++] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Preserve outer z-boundary.
|
||||
|
||||
This operation is a no-op in the case
|
||||
|
||||
list[pos-2] + tolerance < list[n-1].
|
||||
|
||||
If, however, the second to last point is less than <tolerance>
|
||||
away from the last point (list[n-1]), we remove this
|
||||
second-to-last point as it cannot be distinguished from "final"
|
||||
point.
|
||||
*/
|
||||
list[pos-1] = list[n-1];
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Along single pillar: */
|
||||
static int assignPointNumbers(int begin,
|
||||
int end,
|
||||
const double *zlist,
|
||||
int n,
|
||||
const double *zcorn,
|
||||
const int *actnum,
|
||||
int *plist,
|
||||
double tolerance)
|
||||
{
|
||||
/* n - number of cells */
|
||||
/* zlist - list of len unique z-values */
|
||||
/* start - number of unique z-values processed before. */
|
||||
|
||||
int i, k;
|
||||
/* All points should now be within tolerance of a listed point. */
|
||||
|
||||
|
||||
const double *z = zcorn;
|
||||
const int *a = actnum;
|
||||
int *p = plist;
|
||||
|
||||
k = begin;
|
||||
*p++ = INT_MIN; /* Padding to ease processing of faults */
|
||||
for (i=0; i<n; ++i){
|
||||
|
||||
/* Skip inactive cells */
|
||||
if (!a[i/2]) {
|
||||
p[0] = p[-1]; /* Inactive cells are collapsed leaving
|
||||
* void space.*/
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find next k such that zlist[k] < z[i] < zlist[k+1] */
|
||||
while ((k < end) && (zlist[k] + tolerance < z[i])){
|
||||
k++;
|
||||
}
|
||||
|
||||
/* assert (k < len && z[i] - zlist[k] <= tolerance) */
|
||||
if ((k == end) || ( zlist[k] + tolerance < z[i])){
|
||||
fprintf(stderr, "Cannot associate zcorn values with given list\n");
|
||||
fprintf(stderr, "of z-coordinates to given tolerance\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*p++ = k;
|
||||
}
|
||||
*p++ = INT_MAX;/* Padding to ease processing of faults */
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
vector_positions(const int dims[3] ,
|
||||
const int i ,
|
||||
const int j ,
|
||||
size_t start[4])
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t im, ip, jm, jp;
|
||||
|
||||
im = MAX(1, i ) - 1;
|
||||
jm = MAX(1, j ) - 1;
|
||||
ip = MIN(dims[0], i+1) - 1;
|
||||
jp = MIN(dims[1], j+1) - 1;
|
||||
|
||||
start[ 0 ] = dims[2] * (im + dims[0]*jm);
|
||||
start[ 1 ] = dims[2] * (im + dims[0]*jp);
|
||||
start[ 2 ] = dims[2] * (ip + dims[0]*jm);
|
||||
start[ 3 ] = dims[2] * (ip + dims[0]*jp);
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Given a vector <field> with k index running faster than i running
|
||||
faster than j, and Cartesian dimensions <dims>, find pointers to the
|
||||
(i-1, j-1, 0), (i-1, j, 0), (i, j-1, 0) and (i, j, 0) elements of
|
||||
field. */
|
||||
static void igetvectors(const int dims[3], int i, int j,
|
||||
const int *field, const int *v[])
|
||||
{
|
||||
size_t p, start[4];
|
||||
|
||||
vector_positions(dims, i, j, start);
|
||||
|
||||
for (p = 0; p < 4; p++) {
|
||||
v[p] = field + start[p];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Given a vector <field> with k index running faster than i running
|
||||
faster than j, and Cartesian dimensions <dims>, find pointers to the
|
||||
(i-1, j-1, 0), (i-1, j, 0), (i, j-1, 0) and (i, j, 0) elements of
|
||||
field. */
|
||||
static void dgetvectors(const int dims[3], int i, int j,
|
||||
const double *field, const double *v[])
|
||||
{
|
||||
size_t p, start[4];
|
||||
|
||||
vector_positions(dims, i, j, start);
|
||||
|
||||
for (p = 0; p < 4; p++) {
|
||||
v[p] = field + start[p];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Given a z coordinate, find x and y coordinates on line defined by
|
||||
coord. Coord points to a vector of 6 doubles [x0,y0,z0,x1,y1,z1].
|
||||
*/
|
||||
static void interpolate_pillar(const double *coord, double *pt)
|
||||
{
|
||||
double a;
|
||||
|
||||
if (fabs(coord[5] - coord[2]) > 0) {
|
||||
|
||||
a = (pt[2] - coord[2]) / (coord[5] - coord[2]);
|
||||
|
||||
} else {
|
||||
|
||||
a = 0;
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
pt[0] = coord[0] + a*(coord[3]-coord[0]);
|
||||
pt[1] = coord[1] + a*(coord[4]-coord[1]);
|
||||
#else
|
||||
pt[0] = (1.0 - a)*coord[0] + a*coord[3];
|
||||
pt[1] = (1.0 - a)*coord[1] + a*coord[4];
|
||||
#endif
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
Assign point numbers p such that "zlist(p)==zcorn". Assume that
|
||||
coordinate number is arranged in a sequence such that the natural
|
||||
index is (k,i,j) */
|
||||
int finduniquepoints(const struct grdecl *g,
|
||||
/* return values: */
|
||||
int *plist, /* list of point numbers on
|
||||
* each pillar*/
|
||||
double tolerance,
|
||||
struct processed_grid *out)
|
||||
|
||||
{
|
||||
|
||||
const int nx = out->dimensions[0];
|
||||
const int ny = out->dimensions[1];
|
||||
const int nz = out->dimensions[2];
|
||||
const int nc = g->dims[0]*g->dims[1]*g->dims[2];
|
||||
|
||||
|
||||
/* zlist may need extra space temporarily due to simple boundary
|
||||
* treatement */
|
||||
int npillarpoints = 8*(nx+1)*(ny+1)*nz;
|
||||
int npillars = (nx+1)*(ny+1);
|
||||
|
||||
double *zlist = malloc(npillarpoints*sizeof *zlist);
|
||||
int *zptr = malloc((npillars+1)*sizeof *zptr);
|
||||
|
||||
|
||||
|
||||
|
||||
int i,j,k;
|
||||
|
||||
int d1[3];
|
||||
int len = 0;
|
||||
double *zout = zlist;
|
||||
int pos = 0;
|
||||
double *pt;
|
||||
const double *z[4];
|
||||
const int *a[4];
|
||||
int *p;
|
||||
int pix, cix;
|
||||
int zix;
|
||||
|
||||
const double *coord = g->coord;
|
||||
|
||||
d1[0] = 2*g->dims[0];
|
||||
d1[1] = 2*g->dims[1];
|
||||
d1[2] = 2*g->dims[2];
|
||||
|
||||
out->node_coordinates = malloc (3*8*nc*sizeof(*out->node_coordinates));
|
||||
|
||||
zptr[pos++] = zout - zlist;
|
||||
|
||||
pt = out->node_coordinates;
|
||||
|
||||
/* Loop over pillars, find unique points on each pillar */
|
||||
for (j=0; j < g->dims[1]+1; ++j){
|
||||
for (i=0; i < g->dims[0]+1; ++i){
|
||||
|
||||
/* Get positioned pointers for actnum and zcorn data */
|
||||
igetvectors(g->dims, i, j, g->actnum, a);
|
||||
dgetvectors(d1, 2*i, 2*j, g->zcorn, z);
|
||||
|
||||
len = createSortedList( zout, d1[2], 4, z, a);
|
||||
len = uniquify (len, zout, tolerance);
|
||||
|
||||
/* Assign unique points */
|
||||
for (k=0; k<len; ++k){
|
||||
pt[2] = zout[k];
|
||||
interpolate_pillar(coord, pt);
|
||||
pt += 3;
|
||||
}
|
||||
|
||||
/* Increment pointer to sparse table of unique zcorn
|
||||
* values */
|
||||
zout = zout + len;
|
||||
zptr[pos++] = zout - zlist;
|
||||
|
||||
coord += 6;
|
||||
}
|
||||
}
|
||||
out->number_of_nodes_on_pillars = zptr[pos-1];
|
||||
out->number_of_nodes = zptr[pos-1];
|
||||
|
||||
/* Loop over all vertical sets of zcorn values, assign point
|
||||
* numbers */
|
||||
p = plist;
|
||||
for (j=0; j < 2*g->dims[1]; ++j){
|
||||
for (i=0; i < 2*g->dims[0]; ++i){
|
||||
|
||||
/* pillar index */
|
||||
pix = (i+1)/2 + (g->dims[0]+1)*((j+1)/2);
|
||||
|
||||
/* cell column position */
|
||||
cix = g->dims[2]*((i/2) + (j/2)*g->dims[0]);
|
||||
|
||||
/* zcorn column position */
|
||||
zix = 2*g->dims[2]*(i+2*g->dims[0]*j);
|
||||
|
||||
if (!assignPointNumbers(zptr[pix], zptr[pix+1], zlist,
|
||||
2*g->dims[2],
|
||||
g->zcorn + zix, g->actnum + cix,
|
||||
p, tolerance)){
|
||||
fprintf(stderr, "Something went wrong in assignPointNumbers");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += 2 + 2*g->dims[2];
|
||||
}
|
||||
}
|
||||
|
||||
free(zptr);
|
||||
free(zlist);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Local Variables: */
|
||||
/* c-basic-offset:4 */
|
||||
/* End: */
|
|
@ -1,47 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: uniquepoints.h
|
||||
//
|
||||
// Created: Fri Jun 19 08:46:30 2009
|
||||
//
|
||||
// Author: Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// $Revision$
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_UNIQUEPOINTS_HEADER
|
||||
#define OPM_UNIQUEPOINTS_HEADER
|
||||
|
||||
int finduniquepoints(const struct grdecl *g, /* input */
|
||||
int *p, /* for each z0 in zcorn, z0 = z[p0] */
|
||||
double t, /* tolerance*/
|
||||
struct processed_grid *out);
|
||||
|
||||
#endif /* OPM_UNIQUEPOINTS_HEADER */
|
||||
|
||||
/* Local Variables: */
|
||||
/* c-basic-offset:4 */
|
||||
/* End: */
|
|
@ -1,609 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/utility/opm_memcmp_double.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
void
|
||||
destroy_grid(struct UnstructuredGrid *g)
|
||||
{
|
||||
if (g!=NULL)
|
||||
{
|
||||
free(g->face_nodes);
|
||||
free(g->face_nodepos);
|
||||
free(g->face_cells);
|
||||
free(g->cell_facepos);
|
||||
free(g->cell_faces);
|
||||
|
||||
free(g->node_coordinates);
|
||||
free(g->face_centroids);
|
||||
free(g->face_areas);
|
||||
free(g->face_normals);
|
||||
free(g->cell_centroids);
|
||||
free(g->cell_volumes);
|
||||
|
||||
free(g->global_cell);
|
||||
free(g->cell_facetag);
|
||||
}
|
||||
|
||||
free(g);
|
||||
}
|
||||
|
||||
|
||||
struct UnstructuredGrid *
|
||||
create_grid_empty(void)
|
||||
{
|
||||
struct UnstructuredGrid *G, g = { 0 };
|
||||
|
||||
G = malloc(1 * sizeof *G);
|
||||
|
||||
if (G != NULL) {
|
||||
*G = g;
|
||||
}
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
|
||||
struct UnstructuredGrid *
|
||||
allocate_grid(size_t ndims ,
|
||||
size_t ncells ,
|
||||
size_t nfaces ,
|
||||
size_t nfacenodes,
|
||||
size_t ncellfaces,
|
||||
size_t nnodes )
|
||||
{
|
||||
size_t nel;
|
||||
struct UnstructuredGrid *G;
|
||||
|
||||
G = create_grid_empty();
|
||||
|
||||
if (G != NULL) {
|
||||
/* Grid fields ---------------------------------------- */
|
||||
G->dimensions = ndims;
|
||||
G->number_of_cells = ncells;
|
||||
G->number_of_faces = nfaces;
|
||||
G->number_of_nodes = nnodes;
|
||||
|
||||
/* Node fields ---------------------------------------- */
|
||||
nel = nnodes * ndims;
|
||||
G->node_coordinates = malloc(nel * sizeof *G->node_coordinates);
|
||||
|
||||
/* Face fields ---------------------------------------- */
|
||||
nel = nfacenodes;
|
||||
G->face_nodes = malloc(nel * sizeof *G->face_nodes);
|
||||
|
||||
nel = nfaces + 1;
|
||||
G->face_nodepos = malloc(nel * sizeof *G->face_nodepos);
|
||||
|
||||
nel = 2 * nfaces;
|
||||
G->face_cells = malloc(nel * sizeof *G->face_cells);
|
||||
|
||||
nel = nfaces * ndims;
|
||||
G->face_centroids = malloc(nel * sizeof *G->face_centroids);
|
||||
|
||||
nel = nfaces * ndims;
|
||||
G->face_normals = malloc(nel * sizeof *G->face_normals);
|
||||
|
||||
nel = nfaces * 1;
|
||||
G->face_areas = malloc(nel * sizeof *G->face_areas);
|
||||
|
||||
|
||||
/* Cell fields ---------------------------------------- */
|
||||
nel = ncellfaces;
|
||||
G->cell_faces = malloc(nel * sizeof *G->cell_faces);
|
||||
|
||||
G->cell_facetag = malloc(nel * sizeof *G->cell_facetag);
|
||||
|
||||
nel = ncells + 1;
|
||||
G->cell_facepos = malloc(nel * sizeof *G->cell_facepos);
|
||||
|
||||
nel = ncells * ndims;
|
||||
G->cell_centroids = malloc(nel * sizeof *G->cell_centroids);
|
||||
|
||||
nel = ncells * 1;
|
||||
G->cell_volumes = malloc(nel * sizeof *G->cell_volumes);
|
||||
|
||||
if ((G->node_coordinates == NULL) ||
|
||||
(G->face_nodes == NULL) ||
|
||||
(G->face_nodepos == NULL) ||
|
||||
(G->face_cells == NULL) ||
|
||||
(G->face_centroids == NULL) ||
|
||||
(G->face_normals == NULL) ||
|
||||
(G->face_areas == NULL) ||
|
||||
(G->cell_faces == NULL) ||
|
||||
(G->cell_facetag == NULL) ||
|
||||
(G->cell_facepos == NULL) ||
|
||||
(G->cell_centroids == NULL) ||
|
||||
(G->cell_volumes == NULL) )
|
||||
{
|
||||
destroy_grid(G);
|
||||
G = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
|
||||
#define GRID_NMETA 6
|
||||
#define GRID_NDIMS 0
|
||||
#define GRID_NCELLS 1
|
||||
#define GRID_NFACES 2
|
||||
#define GRID_NNODES 3
|
||||
#define GRID_NFACENODES 4
|
||||
#define GRID_NCELLFACES 5
|
||||
|
||||
|
||||
static void
|
||||
input_error(FILE *fp, const char * const err)
|
||||
{
|
||||
int save_errno = errno;
|
||||
|
||||
if (ferror(fp)) {
|
||||
fprintf(stderr, "%s: %s\n", err, strerror(save_errno));
|
||||
clearerr(fp);
|
||||
}
|
||||
else if (feof(fp)) {
|
||||
fprintf(stderr, "%s: End-of-file\n", err);
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
|
||||
static struct UnstructuredGrid *
|
||||
allocate_grid_from_file(FILE *fp, int *has_tag, int *has_indexmap)
|
||||
{
|
||||
struct UnstructuredGrid *G;
|
||||
|
||||
int save_errno;
|
||||
unsigned long tmp;
|
||||
size_t dimens[GRID_NMETA], i;
|
||||
|
||||
save_errno = errno;
|
||||
|
||||
i = 0;
|
||||
while ((i < GRID_NMETA) && (fscanf(fp, " %lu", &tmp) == 1)) {
|
||||
dimens[i] = tmp;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i == GRID_NMETA) {
|
||||
if (fscanf(fp, "%d %d", has_tag, has_indexmap) == 2) {
|
||||
G = allocate_grid(dimens[GRID_NDIMS] ,
|
||||
dimens[GRID_NCELLS] ,
|
||||
dimens[GRID_NFACES] ,
|
||||
dimens[GRID_NFACENODES],
|
||||
dimens[GRID_NCELLFACES],
|
||||
dimens[GRID_NNODES] );
|
||||
|
||||
if (G != NULL) {
|
||||
if (! *has_tag) {
|
||||
free(G->cell_facetag);
|
||||
G->cell_facetag = NULL;
|
||||
}
|
||||
|
||||
if (*has_indexmap) {
|
||||
G->global_cell =
|
||||
malloc(dimens[GRID_NCELLS] * sizeof *G->global_cell);
|
||||
|
||||
/* Allocation failure checked elsewhere. */
|
||||
}
|
||||
|
||||
G->number_of_cells = (int) dimens[GRID_NCELLS];
|
||||
G->number_of_faces = (int) dimens[GRID_NFACES];
|
||||
G->number_of_nodes = (int) dimens[GRID_NNODES];
|
||||
G->dimensions = (int) dimens[GRID_NDIMS];
|
||||
|
||||
i = 0;
|
||||
while ((i < dimens[GRID_NDIMS]) &&
|
||||
(fscanf(fp, "%d", & G->cartdims[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i < dimens[GRID_NDIMS]) {
|
||||
input_error(fp, "Unable to read Cartesian dimensions");
|
||||
|
||||
destroy_grid(G);
|
||||
G = NULL;
|
||||
}
|
||||
else {
|
||||
/* Account for dimens[GRID_DIMS] < 3 */
|
||||
size_t n = (sizeof G->cartdims) / (sizeof G->cartdims[0]);
|
||||
for (; i < n; i++) { G->cartdims[ i ] = 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
input_error(fp, "Unable to read grid predicates");
|
||||
|
||||
G = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
input_error(fp, "Unable to read grid dimensions");
|
||||
|
||||
G = NULL;
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
read_grid_nodes(FILE *fp, struct UnstructuredGrid *G)
|
||||
{
|
||||
int save_errno;
|
||||
size_t i, n;
|
||||
|
||||
save_errno = errno;
|
||||
|
||||
n = G->dimensions;
|
||||
n *= G->number_of_nodes;
|
||||
|
||||
i = 0;
|
||||
while ((i < n) &&
|
||||
(fscanf(fp, " %lf", & G->node_coordinates[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i < n) {
|
||||
input_error(fp, "Unable to read node coordinates");
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
|
||||
return i == n;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
read_grid_faces(FILE *fp, struct UnstructuredGrid *G)
|
||||
{
|
||||
int save_errno, ok;
|
||||
size_t nf, nfn, i;
|
||||
|
||||
save_errno = errno;
|
||||
|
||||
nf = G->number_of_faces;
|
||||
|
||||
/* G->face_nodepos */
|
||||
i = 0;
|
||||
while ((i < nf + 1) &&
|
||||
(fscanf(fp, " %d", & G->face_nodepos[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
ok = i == nf + 1;
|
||||
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read node indirection array");
|
||||
}
|
||||
else {
|
||||
/* G->face_nodes */
|
||||
nfn = G->face_nodepos[ nf ];
|
||||
|
||||
i = 0;
|
||||
while ((i < nfn) && (fscanf(fp, " %d", & G->face_nodes[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ok = i == nfn;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read face-nodes");
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* G->face_cells */
|
||||
i = 0;
|
||||
while ((i < 2 * nf) && (fscanf(fp, " %d", & G->face_cells[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ok = i == 2 * nf;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read neighbourship");
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* G->face_areas */
|
||||
i = 0;
|
||||
while ((i < nf) && (fscanf(fp, " %lf", & G->face_areas[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ok = i == nf;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read face areas");
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* G->face_centroids */
|
||||
size_t n;
|
||||
|
||||
n = G->dimensions;
|
||||
n *= nf;
|
||||
|
||||
i = 0;
|
||||
while ((i < n) && (fscanf(fp, " %lf", & G->face_centroids[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ok = i == n;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read face centroids");
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* G->face_normals */
|
||||
size_t n;
|
||||
|
||||
n = G->dimensions;
|
||||
n *= nf;
|
||||
|
||||
i = 0;
|
||||
while ((i < n) && (fscanf(fp, " %lf", & G->face_normals[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ok = i == n;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read face normals");
|
||||
}
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
read_grid_cells(FILE *fp, int has_tag, int has_indexmap,
|
||||
struct UnstructuredGrid *G)
|
||||
{
|
||||
int save_errno, ok;
|
||||
size_t nc, ncf, i;
|
||||
|
||||
save_errno = errno;
|
||||
|
||||
nc = G->number_of_cells;
|
||||
|
||||
/* G->cell_facepos */
|
||||
i = 0;
|
||||
while ((i < nc + 1) && (fscanf(fp, " %d", & G->cell_facepos[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
ok = i == nc + 1;
|
||||
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read face indirection array");
|
||||
}
|
||||
else {
|
||||
/* G->cell_faces (and G->cell_facetag if applicable) */
|
||||
ncf = G->cell_facepos[ nc ];
|
||||
i = 0;
|
||||
|
||||
if (has_tag) {
|
||||
assert (G->cell_facetag != NULL);
|
||||
|
||||
while ((i < ncf) &&
|
||||
(fscanf(fp, " %d %d",
|
||||
& G->cell_faces [ i ],
|
||||
& G->cell_facetag[ i ]) == 2)) {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while ((i < ncf) &&
|
||||
(fscanf(fp, " %d", & G->cell_faces[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
ok = i == ncf;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read cell-faces");
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* G->global_cell if applicable */
|
||||
if (has_indexmap) {
|
||||
i = 0;
|
||||
|
||||
if (G->global_cell != NULL) {
|
||||
while ((i < nc) &&
|
||||
(fscanf(fp, " %d", & G->global_cell[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int discard;
|
||||
|
||||
while ((i < nc) && (fscanf(fp, " %d", & discard) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert (G->global_cell == NULL);
|
||||
i = nc;
|
||||
}
|
||||
|
||||
ok = i == nc;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read global cellmap");
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* G->cell_volumes */
|
||||
i = 0;
|
||||
while ((i < nc) && (fscanf(fp, " %lf", & G->cell_volumes[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ok = i == nc;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read cell volumes");
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* G->cell_centroids */
|
||||
size_t n;
|
||||
|
||||
n = G->dimensions;
|
||||
n *= nc;
|
||||
|
||||
i = 0;
|
||||
while ((i < n) && (fscanf(fp, " %lf", & G->cell_centroids[ i ]) == 1)) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ok = i == n;
|
||||
if (! ok) {
|
||||
input_error(fp, "Unable to read cell centroids");
|
||||
}
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
struct UnstructuredGrid *
|
||||
read_grid(const char *fname)
|
||||
{
|
||||
struct UnstructuredGrid *G;
|
||||
FILE *fp;
|
||||
|
||||
int save_errno;
|
||||
int has_tag, has_indexmap, ok;
|
||||
|
||||
save_errno = errno;
|
||||
|
||||
fp = fopen(fname, "rt");
|
||||
if (fp != NULL) {
|
||||
G = allocate_grid_from_file(fp, & has_tag, & has_indexmap);
|
||||
|
||||
ok = G != NULL;
|
||||
|
||||
if (ok) { ok = read_grid_nodes(fp, G); }
|
||||
if (ok) { ok = read_grid_faces(fp, G); }
|
||||
if (ok) { ok = read_grid_cells(fp, has_tag, has_indexmap, G); }
|
||||
|
||||
if (! ok) {
|
||||
destroy_grid(G);
|
||||
G = NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
else {
|
||||
G = NULL;
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
grid_equal(const struct UnstructuredGrid * grid1 , const struct UnstructuredGrid * grid2) {
|
||||
if ((grid1->dimensions == grid2->dimensions) &&
|
||||
(grid1->number_of_cells == grid2->number_of_cells) &&
|
||||
(grid1->number_of_faces == grid2->number_of_faces) &&
|
||||
(grid1->number_of_nodes == grid2->number_of_nodes)) {
|
||||
|
||||
// Exact integer comparisons
|
||||
{
|
||||
if (memcmp(grid1->face_nodepos , grid2->face_nodepos , (grid1->number_of_faces + 1) * sizeof * grid1->face_nodepos) != 0)
|
||||
return false;
|
||||
|
||||
if (memcmp(grid1->face_nodes , grid2->face_nodes , grid1->face_nodepos[grid1->number_of_faces] * sizeof * grid1->face_nodes) != 0)
|
||||
return false;
|
||||
|
||||
if (memcmp(grid1->face_cells , grid2->face_cells , 2 * grid1->number_of_faces * sizeof * grid1->face_cells) != 0)
|
||||
return false;
|
||||
|
||||
if (memcmp(grid1->cell_faces , grid2->cell_faces , grid1->cell_facepos[grid1->number_of_cells] * sizeof * grid1->cell_faces) != 0)
|
||||
return false;
|
||||
|
||||
if (memcmp(grid1->cell_facepos , grid2->cell_facepos , (grid1->number_of_cells + 1) * sizeof * grid1->cell_facepos) != 0)
|
||||
return false;
|
||||
|
||||
if (grid1->global_cell && grid2->global_cell) {
|
||||
if (memcmp(grid1->global_cell , grid2->global_cell , grid1->number_of_cells * sizeof * grid1->global_cell) != 0)
|
||||
return false;
|
||||
} else if (grid1->global_cell != grid2->global_cell)
|
||||
return false;
|
||||
|
||||
if (grid1->cell_facetag && grid2->cell_facetag) {
|
||||
if (memcmp(grid1->cell_facetag , grid2->cell_facetag , grid1->cell_facepos[grid1->number_of_cells] * sizeof * grid1->cell_facetag) != 0)
|
||||
return false;
|
||||
} else if (grid1->cell_facetag != grid2->cell_facetag)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Floating point comparisons.
|
||||
{
|
||||
if (opm_memcmp_double( grid1->node_coordinates , grid2->node_coordinates , grid1->dimensions * grid1->number_of_nodes) != 0)
|
||||
return false;
|
||||
|
||||
if (opm_memcmp_double( grid1->face_centroids , grid2->face_centroids , grid1->dimensions * grid1->number_of_faces) != 0)
|
||||
return false;
|
||||
|
||||
if (opm_memcmp_double( grid1->face_areas , grid2->face_areas , grid1->number_of_faces) != 0)
|
||||
return false;
|
||||
|
||||
if (opm_memcmp_double( grid1->face_normals , grid2->face_normals , grid1->dimensions * grid1->number_of_faces) != 0)
|
||||
return false;
|
||||
|
||||
if (opm_memcmp_double( grid1->cell_centroids , grid2->cell_centroids , grid1->dimensions * grid1->number_of_cells) != 0)
|
||||
return false;
|
||||
|
||||
if (opm_memcmp_double( grid1->cell_volumes , grid2->cell_volumes , grid1->number_of_cells) != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
#include "OutputWriter.hpp"
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/io/eclipse/EclipseWriter.hpp>
|
||||
#include <opm/core/utility/parameters/Parameter.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <forward_list>
|
||||
#include <map>
|
||||
#include <memory> // unique_ptr
|
||||
|
||||
using namespace std;
|
||||
using namespace Opm;
|
||||
using namespace Opm::parameter;
|
||||
|
||||
namespace {
|
||||
|
||||
/// Multiplexer over a list of output writers
|
||||
struct MultiWriter : public OutputWriter {
|
||||
/// Shorthand for a list of owned output writers
|
||||
typedef forward_list <unique_ptr <OutputWriter> > writers_t;
|
||||
typedef writers_t::iterator it_t;
|
||||
typedef unique_ptr <writers_t> ptr_t;
|
||||
|
||||
/// Adopt a list of writers
|
||||
MultiWriter (ptr_t writers) : writers_ (std::move (writers)) { }
|
||||
|
||||
/// Forward the call to all writers
|
||||
virtual void writeInit(const SimulatorTimerInterface &timer) {
|
||||
for (it_t it = writers_->begin (); it != writers_->end (); ++it) {
|
||||
(*it)->writeInit (timer);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void writeTimeStep(const SimulatorTimerInterface& timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState,
|
||||
bool isSubstep) {
|
||||
for (it_t it = writers_->begin (); it != writers_->end(); ++it) {
|
||||
(*it)->writeTimeStep (timer, reservoirState, wellState, isSubstep);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ptr_t writers_;
|
||||
};
|
||||
|
||||
/// Psuedo-constructor, can appear in template
|
||||
template <typename Format> unique_ptr <OutputWriter>
|
||||
create (const ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseState> eclipseState,
|
||||
const Opm::PhaseUsage &phaseUsage,
|
||||
std::shared_ptr <const UnstructuredGrid> grid) {
|
||||
return unique_ptr <OutputWriter> (new Format (params,
|
||||
eclipseState,
|
||||
phaseUsage,
|
||||
grid->number_of_cells,
|
||||
grid->global_cell));
|
||||
}
|
||||
|
||||
/// Map between keyword in configuration and the corresponding
|
||||
/// constructor function (type) that should be called when detected.
|
||||
/// The writer must have a constructor which takes params and parser.
|
||||
///
|
||||
/// If you want to add more possible writer formats, just add them
|
||||
/// to the list below!
|
||||
typedef map <const char*, unique_ptr <OutputWriter> (*)(
|
||||
const ParameterGroup&,
|
||||
std::shared_ptr <const EclipseState> eclipseState,
|
||||
const Opm::PhaseUsage &phaseUsage,
|
||||
std::shared_ptr <const UnstructuredGrid>)> map_t;
|
||||
map_t FORMATS = {
|
||||
{ "output_ecl", &create <EclipseWriter> },
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
unique_ptr <OutputWriter>
|
||||
OutputWriter::create (const ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseState> eclipseState,
|
||||
const Opm::PhaseUsage &phaseUsage,
|
||||
std::shared_ptr <const UnstructuredGrid> grid) {
|
||||
// allocate a list which will be filled with writers. this list
|
||||
// is initially empty (no output).
|
||||
MultiWriter::ptr_t list (new MultiWriter::writers_t ());
|
||||
|
||||
// loop through the map and see if we can find the key that is
|
||||
// specified there
|
||||
typedef map_t::iterator map_it_t;
|
||||
for (map_it_t it = FORMATS.begin (); it != FORMATS.end(); ++it) {
|
||||
// keyword which would indicate that this format should be used
|
||||
const std::string name (it->first);
|
||||
|
||||
// invoke the constructor for the type if we found the keyword
|
||||
// and put the pointer to this writer onto the list
|
||||
if (params.getDefault <bool> (name, false)) {
|
||||
list->push_front (it->second (params, eclipseState, phaseUsage, grid));
|
||||
}
|
||||
}
|
||||
|
||||
// create a multiplexer from the list of formats we found
|
||||
return unique_ptr <OutputWriter> (new MultiWriter (std::move (list)));
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2013 Uni Research AS
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_OUTPUT_WRITER_HPP
|
||||
#define OPM_OUTPUT_WRITER_HPP
|
||||
|
||||
#include <memory> // unique_ptr, shared_ptr
|
||||
#include <opm/core/simulator/SimulatorTimerInterface.hpp>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
// forward declaration
|
||||
class EclipseState;
|
||||
namespace parameter { class ParameterGroup; }
|
||||
class SimulatorState;
|
||||
class WellState;
|
||||
struct PhaseUsage;
|
||||
|
||||
/*!
|
||||
* Interface for writing non-compositional (blackoil, two-phase) simulation
|
||||
* state to files.
|
||||
*
|
||||
* Use the create() function to setup a chain of writer based on the
|
||||
* configuration values, e.g.
|
||||
*
|
||||
* \example
|
||||
* \code{.cpp}
|
||||
* ParameterGroup params (argc, argv, false);
|
||||
* auto parser = std::make_shared <const Deck> (
|
||||
* params.get <string> ("deck_filename"));
|
||||
*
|
||||
* std::unique_ptr <OutputWriter> writer =
|
||||
* OutputWriter::create (params, parser);
|
||||
*
|
||||
* // before the first timestep
|
||||
* writer->writeInit (timer);
|
||||
*
|
||||
* // after each timestep
|
||||
* writer->writeTimeStep (timer, state, wellState);
|
||||
*
|
||||
* \endcode
|
||||
*/
|
||||
class OutputWriter {
|
||||
public:
|
||||
/// Allow derived classes to be used in the unique_ptr that is returned
|
||||
/// from the create() method. (Every class that should be delete'd should
|
||||
/// have a proper constructor, and if the base class isn't virtual then
|
||||
/// the compiler won't call the right one when the unique_ptr goes out of
|
||||
/// scope).
|
||||
virtual ~OutputWriter () { }
|
||||
|
||||
/**
|
||||
* Write the static data (grid, PVT curves, etc) to disk.
|
||||
*
|
||||
* This routine should be called before the first timestep (i.e. when
|
||||
* timer.currentStepNum () == 0)
|
||||
*/
|
||||
virtual void writeInit(const SimulatorTimerInterface &timer) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Write a blackoil reservoir state to disk for later inspection with
|
||||
* visualization tools like ResInsight
|
||||
*
|
||||
* \param[in] timer The timer providing time, time step, etc. information
|
||||
* \param[in] reservoirState The thermodynamic state of the reservoir
|
||||
* \param[in] wellState The production/injection data for all wells
|
||||
*
|
||||
* This routine should be called after the timestep has been advanced,
|
||||
* i.e. timer.currentStepNum () > 0.
|
||||
*/
|
||||
virtual void writeTimeStep(const SimulatorTimerInterface& timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState,
|
||||
bool isSubstep) = 0;
|
||||
|
||||
/*!
|
||||
* Create a suitable set of output formats based on configuration.
|
||||
*
|
||||
* @param params Configuration properties. This function will setup a
|
||||
* multiplexer of applicable output formats based on the
|
||||
* desired configuration values.
|
||||
*
|
||||
* @param deck Input deck used to set up the simulation.
|
||||
*
|
||||
* @param eclipseState The internalized input deck.
|
||||
*
|
||||
* @return Pointer to a multiplexer to all applicable output formats.
|
||||
*
|
||||
* @see Opm::share_obj
|
||||
*/
|
||||
static std::unique_ptr <OutputWriter>
|
||||
create (const parameter::ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseState> eclipseState,
|
||||
const Opm::PhaseUsage &phaseUsage,
|
||||
std::shared_ptr <const UnstructuredGrid> grid);
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif /* OPM_OUTPUT_WRITER_HPP */
|
|
@ -1,459 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_CORNERPOINTCHOPPER_HEADER_INCLUDED
|
||||
#define OPM_CORNERPOINTCHOPPER_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseMode.hpp>
|
||||
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckDoubleItem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckIntItem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckStringItem.hpp>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class CornerPointChopper
|
||||
{
|
||||
public:
|
||||
CornerPointChopper(const std::string& file)
|
||||
{
|
||||
Opm::ParseMode parseMode;
|
||||
Opm::ParserPtr parser(new Opm::Parser());
|
||||
deck_ = parser->parseFile(file , parseMode);
|
||||
|
||||
metricUnits_.reset(Opm::UnitSystem::newMETRIC());
|
||||
|
||||
Opm::DeckRecordConstPtr specgridRecord = deck_->getKeyword("SPECGRID")->getRecord(0);
|
||||
dims_[0] = specgridRecord->getItem("NX")->getInt(0);
|
||||
dims_[1] = specgridRecord->getItem("NY")->getInt(0);
|
||||
dims_[2] = specgridRecord->getItem("NZ")->getInt(0);
|
||||
|
||||
int layersz = 8*dims_[0]*dims_[1];
|
||||
const std::vector<double>& ZCORN = deck_->getKeyword("ZCORN")->getRawDoubleData();
|
||||
botmax_ = *std::max_element(ZCORN.begin(), ZCORN.begin() + layersz/2);
|
||||
topmin_ = *std::min_element(ZCORN.begin() + dims_[2]*layersz - layersz/2,
|
||||
ZCORN.begin() + dims_[2]*layersz);
|
||||
|
||||
abszmax_ = *std::max_element(ZCORN.begin(), ZCORN.end());
|
||||
abszmin_ = *std::min_element(ZCORN.begin(), ZCORN.end());
|
||||
|
||||
std::cout << "Parsed grdecl file with dimensions ("
|
||||
<< dims_[0] << ", " << dims_[1] << ", " << dims_[2] << ")" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const int* dimensions() const
|
||||
{
|
||||
return dims_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const int* newDimensions() const
|
||||
{
|
||||
return new_dims_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const std::pair<double, double> zLimits() const
|
||||
{
|
||||
return std::make_pair(botmax_, topmin_);
|
||||
}
|
||||
|
||||
const std::pair<double, double> abszLimits() const
|
||||
{
|
||||
return std::make_pair(abszmin_, abszmax_);
|
||||
}
|
||||
|
||||
|
||||
void verifyInscribedShoebox(int imin, int ilen, int imax,
|
||||
int jmin, int jlen, int jmax,
|
||||
double zmin, double zlen, double zmax)
|
||||
{
|
||||
if (imin < 0) {
|
||||
std::cerr << "Error! imin < 0 (imin = " << imin << ")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (ilen > dims_[0]) {
|
||||
std::cerr << "Error! ilen larger than grid (ilen = " << ilen <<")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (imax > dims_[0]) {
|
||||
std::cerr << "Error! imax larger than input grid (imax = " << imax << ")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (jmin < 0) {
|
||||
std::cerr << "Error! jmin < 0 (jmin = " << jmin << ")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (jlen > dims_[1]) {
|
||||
std::cerr << "Error! jlen larger than grid (jlen = " << jlen <<")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (jmax > dims_[1]) {
|
||||
std::cerr << "Error! jmax larger than input grid (jmax = " << jmax << ")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (zmin < abszmin_) {
|
||||
std::cerr << "Error! zmin ("<< zmin << ") less than minimum ZCORN value ("<< abszmin_ << ")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (zmax > abszmax_) {
|
||||
std::cerr << "Error! zmax ("<< zmax << ") larger than maximal ZCORN value ("<< abszmax_ << ")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
if (zlen > (abszmax_ - abszmin_)) {
|
||||
std::cerr << "Error! zlen ("<< zlen <<") larger than maximal ZCORN (" << abszmax_ << ") minus minimal ZCORN ("<< abszmin_ <<")\n";
|
||||
throw std::runtime_error("Inconsistent user input.");
|
||||
}
|
||||
}
|
||||
|
||||
void chop(int imin, int imax, int jmin, int jmax, double zmin, double zmax, bool resettoorigin=true)
|
||||
{
|
||||
new_dims_[0] = imax - imin;
|
||||
new_dims_[1] = jmax - jmin;
|
||||
|
||||
// Filter the coord field
|
||||
const std::vector<double>& COORD = deck_->getKeyword("COORD")->getRawDoubleData();
|
||||
int num_coord = COORD.size();
|
||||
if (num_coord != 6*(dims_[0] + 1)*(dims_[1] + 1)) {
|
||||
std::cerr << "Error! COORD size (" << COORD.size() << ") not consistent with SPECGRID\n";
|
||||
throw std::runtime_error("Inconsistent COORD and SPECGRID.");
|
||||
}
|
||||
int num_new_coord = 6*(new_dims_[0] + 1)*(new_dims_[1] + 1);
|
||||
double x_correction = COORD[6*((dims_[0] + 1)*jmin + imin)];
|
||||
double y_correction = COORD[6*((dims_[0] + 1)*jmin + imin) + 1];
|
||||
new_COORD_.resize(num_new_coord, 1e100);
|
||||
for (int j = jmin; j < jmax + 1; ++j) {
|
||||
for (int i = imin; i < imax + 1; ++i) {
|
||||
int pos = (dims_[0] + 1)*j + i;
|
||||
int new_pos = (new_dims_[0] + 1)*(j-jmin) + (i-imin);
|
||||
// Copy all 6 coordinates for a pillar.
|
||||
std::copy(COORD.begin() + 6*pos, COORD.begin() + 6*(pos + 1), new_COORD_.begin() + 6*new_pos);
|
||||
if (resettoorigin) {
|
||||
// Substract lowest x value from all X-coords, similarly for y, and truncate in z-direction
|
||||
new_COORD_[6*new_pos] -= x_correction;
|
||||
new_COORD_[6*new_pos + 1] -= y_correction;
|
||||
new_COORD_[6*new_pos + 2] = 0;
|
||||
new_COORD_[6*new_pos + 3] -= x_correction;
|
||||
new_COORD_[6*new_pos + 4] -= y_correction;
|
||||
new_COORD_[6*new_pos + 5] = zmax-zmin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the z limits, check if they must be changed to make a shoe-box.
|
||||
// This means that zmin must be greater than or equal to the highest
|
||||
// coordinate of the bottom surface, while zmax must be less than or
|
||||
// equal to the lowest coordinate of the top surface.
|
||||
int layersz = 8*dims_[0]*dims_[1];
|
||||
const std::vector<double>& ZCORN = deck_->getKeyword("ZCORN")->getRawDoubleData();
|
||||
int num_zcorn = ZCORN.size();
|
||||
if (num_zcorn != layersz*dims_[2]) {
|
||||
std::cerr << "Error! ZCORN size (" << ZCORN.size() << ") not consistent with SPECGRID\n";
|
||||
throw std::runtime_error("Inconsistent ZCORN and SPECGRID.");
|
||||
}
|
||||
|
||||
zmin = std::max(zmin, botmax_);
|
||||
zmax = std::min(zmax, topmin_);
|
||||
if (zmin >= zmax) {
|
||||
std::cerr << "Error: zmin >= zmax (zmin = " << zmin << ", zmax = " << zmax << ")\n";
|
||||
throw std::runtime_error("zmin >= zmax");
|
||||
}
|
||||
std::cout << "Chopping subsample, i: (" << imin << "--" << imax << ") j: (" << jmin << "--" << jmax << ") z: (" << zmin << "--" << zmax << ")" << std::endl;
|
||||
|
||||
// We must find the maximum and minimum k value for the given z limits.
|
||||
// First, find the first layer with a z-coordinate strictly above zmin.
|
||||
int kmin = -1;
|
||||
for (int k = 0; k < dims_[2]; ++k) {
|
||||
double layer_max = *std::max_element(ZCORN.begin() + k*layersz, ZCORN.begin() + (k + 1)*layersz);
|
||||
if (layer_max > zmin) {
|
||||
kmin = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Then, find the last layer with a z-coordinate strictly below zmax.
|
||||
int kmax = -1;
|
||||
for (int k = dims_[2]; k > 0; --k) {
|
||||
double layer_min = *std::min_element(ZCORN.begin() + (k - 1)*layersz, ZCORN.begin() + k*layersz);
|
||||
if (layer_min < zmax) {
|
||||
kmax = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_dims_[2] = kmax - kmin;
|
||||
|
||||
// Filter the ZCORN field, build mapping from new to old cells.
|
||||
double z_origin_correction = 0.0;
|
||||
if (resettoorigin) {
|
||||
z_origin_correction = zmin;
|
||||
}
|
||||
new_ZCORN_.resize(8*new_dims_[0]*new_dims_[1]*new_dims_[2], 1e100);
|
||||
new_to_old_cell_.resize(new_dims_[0]*new_dims_[1]*new_dims_[2], -1);
|
||||
int cellcount = 0;
|
||||
int delta[3] = { 1, 2*dims_[0], 4*dims_[0]*dims_[1] };
|
||||
int new_delta[3] = { 1, 2*new_dims_[0], 4*new_dims_[0]*new_dims_[1] };
|
||||
for (int k = kmin; k < kmax; ++k) {
|
||||
for (int j = jmin; j < jmax; ++j) {
|
||||
for (int i = imin; i < imax; ++i) {
|
||||
new_to_old_cell_[cellcount++] = dims_[0]*dims_[1]*k + dims_[0]*j + i;
|
||||
int old_ix = 2*(i*delta[0] + j*delta[1] + k*delta[2]);
|
||||
int new_ix = 2*((i-imin)*new_delta[0] + (j-jmin)*new_delta[1] + (k-kmin)*new_delta[2]);
|
||||
int old_indices[8] = { old_ix, old_ix + delta[0],
|
||||
old_ix + delta[1], old_ix + delta[1] + delta[0],
|
||||
old_ix + delta[2], old_ix + delta[2] + delta[0],
|
||||
old_ix + delta[2] + delta[1], old_ix + delta[2] + delta[1] + delta[0] };
|
||||
int new_indices[8] = { new_ix, new_ix + new_delta[0],
|
||||
new_ix + new_delta[1], new_ix + new_delta[1] + new_delta[0],
|
||||
new_ix + new_delta[2], new_ix + new_delta[2] + new_delta[0],
|
||||
new_ix + new_delta[2] + new_delta[1], new_ix + new_delta[2] + new_delta[1] + new_delta[0] };
|
||||
for (int cc = 0; cc < 8; ++cc) {
|
||||
new_ZCORN_[new_indices[cc]] = std::min(zmax, std::max(zmin, ZCORN[old_indices[cc]])) - z_origin_correction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterIntegerField("ACTNUM", new_ACTNUM_);
|
||||
filterDoubleField("PORO", new_PORO_);
|
||||
filterDoubleField("NTG", new_NTG_);
|
||||
filterDoubleField("SWCR", new_SWCR_);
|
||||
filterDoubleField("SOWCR", new_SOWCR_);
|
||||
filterDoubleField("PERMX", new_PERMX_);
|
||||
filterDoubleField("PERMY", new_PERMY_);
|
||||
filterDoubleField("PERMZ", new_PERMZ_);
|
||||
filterIntegerField("SATNUM", new_SATNUM_);
|
||||
}
|
||||
|
||||
/// Return a sub-deck with fields corresponding to the selected subset.
|
||||
Opm::DeckConstPtr subDeck()
|
||||
{
|
||||
Opm::DeckPtr subDeck(new Opm::Deck);
|
||||
|
||||
Opm::DeckKeywordPtr specGridKw(new Opm::DeckKeyword("SPECGRID"));
|
||||
Opm::DeckRecordPtr specGridRecord(new Opm::DeckRecord());
|
||||
|
||||
Opm::DeckIntItemPtr nxItem(new Opm::DeckIntItem("NX"));
|
||||
Opm::DeckIntItemPtr nyItem(new Opm::DeckIntItem("NY"));
|
||||
Opm::DeckIntItemPtr nzItem(new Opm::DeckIntItem("NZ"));
|
||||
Opm::DeckIntItemPtr numresItem(new Opm::DeckIntItem("NUMRES"));
|
||||
Opm::DeckStringItemPtr coordTypeItem(new Opm::DeckStringItem("COORD_TYPE"));
|
||||
|
||||
nxItem->push_back(new_dims_[0]);
|
||||
nyItem->push_back(new_dims_[1]);
|
||||
nzItem->push_back(new_dims_[2]);
|
||||
numresItem->push_back(1);
|
||||
coordTypeItem->push_back("F");
|
||||
|
||||
specGridRecord->addItem(nxItem);
|
||||
specGridRecord->addItem(nyItem);
|
||||
specGridRecord->addItem(nzItem);
|
||||
specGridRecord->addItem(numresItem);
|
||||
specGridRecord->addItem(coordTypeItem);
|
||||
|
||||
specGridKw->addRecord(specGridRecord);
|
||||
|
||||
subDeck->addKeyword(specGridKw);
|
||||
addDoubleKeyword_(subDeck, "COORD", /*dimension=*/"Length", new_COORD_);
|
||||
addDoubleKeyword_(subDeck, "ZCORN", /*dimension=*/"Length", new_ZCORN_);
|
||||
addIntKeyword_(subDeck, "ACTNUM", new_ACTNUM_);
|
||||
addDoubleKeyword_(subDeck, "PORO", /*dimension=*/"1", new_PORO_);
|
||||
addDoubleKeyword_(subDeck, "NTG", /*dimension=*/"1", new_NTG_);
|
||||
addDoubleKeyword_(subDeck, "SWCR", /*dimension=*/"1", new_SWCR_);
|
||||
addDoubleKeyword_(subDeck, "SOWCR", /*dimension=*/"1", new_SOWCR_);
|
||||
addDoubleKeyword_(subDeck, "PERMX", /*dimension=*/"Permeability", new_PERMX_);
|
||||
addDoubleKeyword_(subDeck, "PERMY", /*dimension=*/"Permeability", new_PERMY_);
|
||||
addDoubleKeyword_(subDeck, "PERMZ", /*dimension=*/"Permeability", new_PERMZ_);
|
||||
addIntKeyword_(subDeck, "SATNUM", new_SATNUM_);
|
||||
return subDeck;
|
||||
}
|
||||
void writeGrdecl(const std::string& filename)
|
||||
{
|
||||
// Output new versions of SPECGRID, COORD, ZCORN, ACTNUM, PERMX, PORO, SATNUM.
|
||||
std::ofstream out(filename.c_str());
|
||||
if (!out) {
|
||||
std::cerr << "Could not open file " << filename << "\n";
|
||||
throw std::runtime_error("Could not open output file.");
|
||||
}
|
||||
out << "SPECGRID\n" << new_dims_[0] << ' ' << new_dims_[1] << ' ' << new_dims_[2]
|
||||
<< " 1 F\n/\n\n";
|
||||
|
||||
out.precision(15);
|
||||
out.setf(std::ios::scientific);
|
||||
|
||||
outputField(out, new_COORD_, "COORD", /* nl = */ 3);
|
||||
outputField(out, new_ZCORN_, "ZCORN", /* nl = */ 4);
|
||||
outputField(out, new_ACTNUM_, "ACTNUM");
|
||||
outputField(out, new_PORO_, "PORO", 4);
|
||||
if (hasNTG()) {outputField(out, new_NTG_, "NTG", 4);}
|
||||
if (hasSWCR()) {outputField(out, new_SWCR_, "SWCR", 4);}
|
||||
if (hasSOWCR()) {outputField(out, new_SOWCR_, "SOWCR", 4);}
|
||||
outputField(out, new_PERMX_, "PERMX", 4);
|
||||
outputField(out, new_PERMY_, "PERMY", 4);
|
||||
outputField(out, new_PERMZ_, "PERMZ", 4);
|
||||
outputField(out, new_SATNUM_, "SATNUM");
|
||||
}
|
||||
bool hasNTG() const {return !new_NTG_.empty(); }
|
||||
bool hasSWCR() const {return !new_SWCR_.empty(); }
|
||||
bool hasSOWCR() const {return !new_SOWCR_.empty(); }
|
||||
|
||||
private:
|
||||
Opm::DeckConstPtr deck_;
|
||||
std::shared_ptr<Opm::UnitSystem> metricUnits_;
|
||||
|
||||
double botmax_;
|
||||
double topmin_;
|
||||
double abszmin_;
|
||||
double abszmax_;
|
||||
std::vector<double> new_COORD_;
|
||||
std::vector<double> new_ZCORN_;
|
||||
std::vector<int> new_ACTNUM_;
|
||||
std::vector<double> new_PORO_;
|
||||
std::vector<double> new_NTG_;
|
||||
std::vector<double> new_SWCR_;
|
||||
std::vector<double> new_SOWCR_;
|
||||
std::vector<double> new_PERMX_;
|
||||
std::vector<double> new_PERMY_;
|
||||
std::vector<double> new_PERMZ_;
|
||||
std::vector<int> new_SATNUM_;
|
||||
int dims_[3];
|
||||
int new_dims_[3];
|
||||
std::vector<int> new_to_old_cell_;
|
||||
|
||||
void addDoubleKeyword_(Opm::DeckPtr subDeck,
|
||||
const std::string& keywordName,
|
||||
const std::string& dimensionString,
|
||||
const std::vector<double>& data)
|
||||
{
|
||||
if (data.empty())
|
||||
return;
|
||||
|
||||
Opm::DeckKeywordPtr dataKw(new Opm::DeckKeyword(keywordName));
|
||||
Opm::DeckRecordPtr dataRecord(new Opm::DeckRecord());
|
||||
Opm::DeckDoubleItemPtr dataItem(new Opm::DeckDoubleItem("DATA"));
|
||||
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
dataItem->push_back(data[i]);
|
||||
}
|
||||
|
||||
std::shared_ptr<const Dimension> dimension = metricUnits_->parse(dimensionString);
|
||||
dataItem->push_backDimension(/*active=*/dimension, /*default=*/dimension);
|
||||
|
||||
dataRecord->addItem(dataItem);
|
||||
dataKw->addRecord(dataRecord);
|
||||
subDeck->addKeyword(dataKw);
|
||||
}
|
||||
|
||||
void addIntKeyword_(Opm::DeckPtr subDeck,
|
||||
const std::string& keywordName,
|
||||
const std::vector<int>& data)
|
||||
{
|
||||
if (data.empty())
|
||||
return;
|
||||
|
||||
Opm::DeckKeywordPtr dataKw(new Opm::DeckKeyword(keywordName));
|
||||
Opm::DeckRecordPtr dataRecord(new Opm::DeckRecord());
|
||||
Opm::DeckIntItemPtr dataItem(new Opm::DeckIntItem("DATA"));
|
||||
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
dataItem->push_back(data[i]);
|
||||
}
|
||||
|
||||
dataRecord->addItem(dataItem);
|
||||
dataKw->addRecord(dataRecord);
|
||||
subDeck->addKeyword(dataKw);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void outputField(std::ostream& os,
|
||||
const std::vector<T>& field,
|
||||
const std::string& keyword,
|
||||
const typename std::vector<T>::size_type nl = 20)
|
||||
{
|
||||
if (field.empty()) return;
|
||||
|
||||
os << keyword << '\n';
|
||||
|
||||
typedef typename std::vector<T>::size_type sz_t;
|
||||
|
||||
const sz_t n = field.size();
|
||||
for (sz_t i = 0; i < n; ++i) {
|
||||
os << field[i]
|
||||
<< (((i + 1) % nl == 0) ? '\n' : ' ');
|
||||
}
|
||||
if (n % nl != 0) {
|
||||
os << '\n';
|
||||
}
|
||||
os << "/\n\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
void filterField(const std::vector<T>& field,
|
||||
std::vector<T>& output_field)
|
||||
{
|
||||
int sz = new_to_old_cell_.size();
|
||||
output_field.resize(sz);
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
output_field[i] = field[new_to_old_cell_[i]];
|
||||
}
|
||||
}
|
||||
|
||||
void filterDoubleField(const std::string& keyword, std::vector<double>& output_field)
|
||||
{
|
||||
if (deck_->hasKeyword(keyword)) {
|
||||
const std::vector<double>& field = deck_->getKeyword(keyword)->getRawDoubleData();
|
||||
filterField(field, output_field);
|
||||
}
|
||||
}
|
||||
|
||||
void filterIntegerField(const std::string& keyword, std::vector<int>& output_field)
|
||||
{
|
||||
if (deck_->hasKeyword(keyword)) {
|
||||
const std::vector<int>& field = deck_->getKeyword(keyword)->getIntData();
|
||||
filterField(field, output_field);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // OPM_CORNERPOINTCHOPPER_HEADER_INCLUDED
|
|
@ -1,347 +0,0 @@
|
|||
//===========================================================================
|
||||
//
|
||||
// File: EclipseGridInspector.C
|
||||
//
|
||||
// Created: Mon Jun 2 12:17:51 2008
|
||||
//
|
||||
// Author: Atgeirr F Rasmussen <atgeirr@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// $Revision$
|
||||
//
|
||||
// Revision: $Id: EclipseGridInspector.C,v 1.2 2008/08/18 14:16:13 atgeirr Exp $
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <opm/core/io/eclipse/EclipseGridInspector.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <stdexcept>
|
||||
#include <numeric>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
EclipseGridInspector::EclipseGridInspector(Opm::DeckConstPtr deck)
|
||||
: deck_(deck)
|
||||
{
|
||||
init_();
|
||||
}
|
||||
|
||||
void EclipseGridInspector::init_()
|
||||
{
|
||||
if (!deck_->hasKeyword("COORD")) {
|
||||
OPM_THROW(std::runtime_error, "Needed field \"COORD\" is missing in file");
|
||||
}
|
||||
if (!deck_->hasKeyword("ZCORN")) {
|
||||
OPM_THROW(std::runtime_error, "Needed field \"ZCORN\" is missing in file");
|
||||
}
|
||||
|
||||
if (deck_->hasKeyword("SPECGRID")) {
|
||||
Opm::DeckRecordConstPtr specgridRecord =
|
||||
deck_->getKeyword("SPECGRID")->getRecord(0);
|
||||
logical_gridsize_[0] = specgridRecord->getItem("NX")->getInt(0);
|
||||
logical_gridsize_[1] = specgridRecord->getItem("NY")->getInt(0);
|
||||
logical_gridsize_[2] = specgridRecord->getItem("NZ")->getInt(0);
|
||||
} else if (deck_->hasKeyword("DIMENS")) {
|
||||
Opm::DeckRecordConstPtr dimensRecord =
|
||||
deck_->getKeyword("DIMENS")->getRecord(0);
|
||||
logical_gridsize_[0] = dimensRecord->getItem("NX")->getInt(0);
|
||||
logical_gridsize_[1] = dimensRecord->getItem("NY")->getInt(0);
|
||||
logical_gridsize_[2] = dimensRecord->getItem("NZ")->getInt(0);
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Found neither SPECGRID nor DIMENS in file. At least one is needed.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Return the dip slopes for the cell relative to xy-plane in x- and y- direction.
|
||||
Dip slope is average rise in positive x-direction over cell length in x-direction.
|
||||
Similarly for y.
|
||||
|
||||
Current implementation is for vertical pillars, but is not difficult to fix.
|
||||
|
||||
@returns a std::pair<double,double> with x-dip in first component and y-dip in second.
|
||||
*/
|
||||
std::pair<double,double> EclipseGridInspector::cellDips(int i, int j, int k) const
|
||||
{
|
||||
checkLogicalCoords(i, j, k);
|
||||
const std::vector<double>& pillc =
|
||||
deck_->getKeyword("COORD")->getSIDoubleData();
|
||||
int num_pillars = (logical_gridsize_[0] + 1)*(logical_gridsize_[1] + 1);
|
||||
if (6*num_pillars != int(pillc.size())) {
|
||||
throw std::runtime_error("Wrong size of COORD field.");
|
||||
}
|
||||
const std::vector<double>& z =
|
||||
deck_->getKeyword("ZCORN")->getSIDoubleData();
|
||||
int num_cells = logical_gridsize_[0]*logical_gridsize_[1]*logical_gridsize_[2];
|
||||
if (8*num_cells != int(z.size())) {
|
||||
throw std::runtime_error("Wrong size of ZCORN field");
|
||||
}
|
||||
|
||||
// Pick ZCORN-value for all 8 corners of the given cell
|
||||
std::array<double, 8> cellz = cellZvals(i, j, k);
|
||||
|
||||
// Compute rise in positive x-direction for all four edges (and then find mean)
|
||||
// Current implementation is for regularly placed and vertical pillars!
|
||||
int numxpill = logical_gridsize_[0] + 1;
|
||||
int pix = i + j*numxpill;
|
||||
double cell_xlength = pillc[6*(pix + 1)] - pillc[6*pix];
|
||||
flush(std::cout);
|
||||
double xrise[4] = { (cellz[1] - cellz[0])/cell_xlength, // LLL -> HLL
|
||||
(cellz[3] - cellz[2])/cell_xlength, // LHL -> HHL
|
||||
(cellz[5] - cellz[4])/cell_xlength, // LLH -> HLH
|
||||
(cellz[7] - cellz[6])/cell_xlength}; // LHH -> HHH
|
||||
|
||||
double cell_ylength = pillc[6*(pix + numxpill) + 1] - pillc[6*pix + 1];
|
||||
double yrise[4] = { (cellz[2] - cellz[0])/cell_ylength, // LLL -> LHL
|
||||
(cellz[3] - cellz[1])/cell_ylength, // HLL -> HHL
|
||||
(cellz[6] - cellz[4])/cell_ylength, // LLH -> LHH
|
||||
(cellz[7] - cellz[5])/cell_ylength}; // HLH -> HHH
|
||||
|
||||
|
||||
// Now ignore those edges that touch the global top or bottom surface
|
||||
// of the entire grdecl model. This is to avoid bias, as these edges probably
|
||||
// don't follow an overall dip for the model if it exists.
|
||||
int x_edges = 4;
|
||||
int y_edges = 4;
|
||||
std::array<double, 6> gridlimits = getGridLimits();
|
||||
double zmin = gridlimits[4];
|
||||
double zmax = gridlimits[5];
|
||||
// LLL -> HLL
|
||||
if ((cellz[1] == zmin) || (cellz[0] == zmin)) {
|
||||
xrise[0] = 0; x_edges--;
|
||||
}
|
||||
// LHL -> HHL
|
||||
if ((cellz[2] == zmin) || (cellz[3] == zmin)) {
|
||||
xrise[1] = 0; x_edges--;
|
||||
}
|
||||
// LLH -> HLH
|
||||
if ((cellz[4] == zmax) || (cellz[5] == zmax)) {
|
||||
xrise[2] = 0; x_edges--;
|
||||
}
|
||||
// LHH -> HHH
|
||||
if ((cellz[6] == zmax) || (cellz[7] == zmax)) {
|
||||
xrise[3] = 0; x_edges--;
|
||||
}
|
||||
// LLL -> LHL
|
||||
if ((cellz[0] == zmin) || (cellz[2] == zmin)) {
|
||||
yrise[0] = 0; y_edges--;
|
||||
}
|
||||
// HLL -> HHL
|
||||
if ((cellz[1] == zmin) || (cellz[3] == zmin)) {
|
||||
yrise[1] = 0; y_edges--;
|
||||
}
|
||||
// LLH -> LHH
|
||||
if ((cellz[6] == zmax) || (cellz[4] == zmax)) {
|
||||
yrise[2] = 0; y_edges--;
|
||||
}
|
||||
// HLH -> HHH
|
||||
if ((cellz[7] == zmax) || (cellz[5] == zmax)) {
|
||||
yrise[3] = 0; y_edges--;
|
||||
}
|
||||
|
||||
return std::make_pair( (xrise[0] + xrise[1] + xrise[2] + xrise[3])/x_edges,
|
||||
(yrise[0] + yrise[1] + yrise[2] + yrise[3])/y_edges);
|
||||
}
|
||||
/**
|
||||
Wrapper for cellDips(i, j, k).
|
||||
*/
|
||||
std::pair<double,double> EclipseGridInspector::cellDips(int cell_idx) const
|
||||
{
|
||||
std::array<int, 3> idxs = cellIdxToLogicalCoords(cell_idx);
|
||||
return cellDips(idxs[0], idxs[1], idxs[2]);
|
||||
}
|
||||
|
||||
std::array<int, 3> EclipseGridInspector::cellIdxToLogicalCoords(int cell_idx) const
|
||||
{
|
||||
|
||||
int i,j,k; // Position of cell in cell hierarchy
|
||||
int horIdx = (cell_idx+1) - int(std::floor(((double)(cell_idx+1))/((double)(logical_gridsize_[0]*logical_gridsize_[1]))))*logical_gridsize_[0]*logical_gridsize_[1]; // index in the corresponding horizon
|
||||
if (horIdx == 0) {
|
||||
horIdx = logical_gridsize_[0]*logical_gridsize_[1];
|
||||
}
|
||||
i = horIdx - int(std::floor(((double)horIdx)/((double)logical_gridsize_[0])))*logical_gridsize_[0];
|
||||
if (i == 0) {
|
||||
i = logical_gridsize_[0];
|
||||
}
|
||||
j = (horIdx-i)/logical_gridsize_[0]+1;
|
||||
k = ((cell_idx+1)-logical_gridsize_[0]*(j-1)-1)/(logical_gridsize_[0]*logical_gridsize_[1])+1;
|
||||
|
||||
std::array<int, 3> a = {{i-1, j-1, k-1}};
|
||||
return a; //std::array<int, 3> {{i-1, j-1, k-1}};
|
||||
}
|
||||
|
||||
double EclipseGridInspector::cellVolumeVerticalPillars(int i, int j, int k) const
|
||||
{
|
||||
// Checking parameters and obtaining values from parser.
|
||||
checkLogicalCoords(i, j, k);
|
||||
const std::vector<double>& pillc =
|
||||
deck_->getKeyword("COORD")->getSIDoubleData();
|
||||
int num_pillars = (logical_gridsize_[0] + 1)*(logical_gridsize_[1] + 1);
|
||||
if (6*num_pillars != int(pillc.size())) {
|
||||
throw std::runtime_error("Wrong size of COORD field.");
|
||||
}
|
||||
const std::vector<double>& z =
|
||||
deck_->getKeyword("ZCORN")->getSIDoubleData();
|
||||
int num_cells = logical_gridsize_[0]*logical_gridsize_[1]*logical_gridsize_[2];
|
||||
if (8*num_cells != int(z.size())) {
|
||||
throw std::runtime_error("Wrong size of ZCORN field");
|
||||
}
|
||||
|
||||
// Computing the base area as half the 2d cross product of the diagonals.
|
||||
int numxpill = logical_gridsize_[0] + 1;
|
||||
int pix = i + j*numxpill;
|
||||
double px[4] = { pillc[6*pix],
|
||||
pillc[6*(pix + 1)],
|
||||
pillc[6*(pix + numxpill)],
|
||||
pillc[6*(pix + numxpill + 1)] };
|
||||
double py[4] = { pillc[6*pix + 1],
|
||||
pillc[6*(pix + 1) + 1],
|
||||
pillc[6*(pix + numxpill) + 1],
|
||||
pillc[6*(pix + numxpill + 1) + 1] };
|
||||
double diag1[2] = { px[3] - px[0], py[3] - py[0] };
|
||||
double diag2[2] = { px[2] - px[1], py[2] - py[1] };
|
||||
double area = 0.5*(diag1[0]*diag2[1] - diag1[1]*diag2[0]);
|
||||
|
||||
// Computing the average of the z-differences along each pillar.
|
||||
int delta[3] = { 1,
|
||||
2*logical_gridsize_[0],
|
||||
4*logical_gridsize_[0]*logical_gridsize_[1] };
|
||||
int ix = 2*(i*delta[0] + j*delta[1] + k*delta[2]);
|
||||
double cellz[8] = { z[ix], z[ix + delta[0]],
|
||||
z[ix + delta[1]], z[ix + delta[1] + delta[0]],
|
||||
z[ix + delta[2]], z[ix + delta[2] + delta[0]],
|
||||
z[ix + delta[2] + delta[1]], z[ix + delta[2] + delta[1] + delta[0]] };
|
||||
double diffz[4] = { cellz[4] - cellz[0],
|
||||
cellz[5] - cellz[1],
|
||||
cellz[6] - cellz[2],
|
||||
cellz[7] - cellz[3] };
|
||||
double averzdiff = 0.25*std::accumulate(diffz, diffz + 4, 0.0);
|
||||
return averzdiff*area;
|
||||
}
|
||||
|
||||
|
||||
double EclipseGridInspector::cellVolumeVerticalPillars(int cell_idx) const
|
||||
{
|
||||
std::array<int, 3> idxs = cellIdxToLogicalCoords(cell_idx);
|
||||
return cellVolumeVerticalPillars(idxs[0], idxs[1], idxs[2]);
|
||||
}
|
||||
|
||||
void EclipseGridInspector::checkLogicalCoords(int i, int j, int k) const
|
||||
{
|
||||
if (i < 0 || i >= logical_gridsize_[0])
|
||||
throw std::runtime_error("First coordinate out of bounds");
|
||||
if (j < 0 || j >= logical_gridsize_[1])
|
||||
throw std::runtime_error("Second coordinate out of bounds");
|
||||
if (k < 0 || k >= logical_gridsize_[2])
|
||||
throw std::runtime_error("Third coordinate out of bounds");
|
||||
}
|
||||
|
||||
|
||||
std::array<double, 6> EclipseGridInspector::getGridLimits() const
|
||||
{
|
||||
if (! (deck_->hasKeyword("COORD") && deck_->hasKeyword("ZCORN") && deck_->hasKeyword("SPECGRID")) ) {
|
||||
throw std::runtime_error("EclipseGridInspector: Grid does not have SPECGRID, COORD, and ZCORN, can't find dimensions.");
|
||||
}
|
||||
|
||||
std::vector<double> coord = deck_->getKeyword("COORD")->getSIDoubleData();
|
||||
std::vector<double> zcorn = deck_->getKeyword("ZCORN")->getSIDoubleData();
|
||||
|
||||
double xmin = +DBL_MAX;
|
||||
double xmax = -DBL_MAX;
|
||||
double ymin = +DBL_MAX;
|
||||
double ymax = -DBL_MAX;
|
||||
|
||||
|
||||
int pillars = (logical_gridsize_[0]+1) * (logical_gridsize_[1]+1);
|
||||
|
||||
for (int pillarindex = 0; pillarindex < pillars; ++pillarindex) {
|
||||
if (coord[pillarindex * 6 + 0] > xmax)
|
||||
xmax = coord[pillarindex * 6 + 0];
|
||||
if (coord[pillarindex * 6 + 0] < xmin)
|
||||
xmin = coord[pillarindex * 6 + 0];
|
||||
if (coord[pillarindex * 6 + 1] > ymax)
|
||||
ymax = coord[pillarindex * 6 + 1];
|
||||
if (coord[pillarindex * 6 + 1] < ymin)
|
||||
ymin = coord[pillarindex * 6 + 1];
|
||||
if (coord[pillarindex * 6 + 3] > xmax)
|
||||
xmax = coord[pillarindex * 6 + 3];
|
||||
if (coord[pillarindex * 6 + 3] < xmin)
|
||||
xmin = coord[pillarindex * 6 + 3];
|
||||
if (coord[pillarindex * 6 + 4] > ymax)
|
||||
ymax = coord[pillarindex * 6 + 4];
|
||||
if (coord[pillarindex * 6 + 4] < ymin)
|
||||
ymin = coord[pillarindex * 6 + 4];
|
||||
}
|
||||
|
||||
std::array<double, 6> gridlimits = {{ xmin, xmax, ymin, ymax,
|
||||
*min_element(zcorn.begin(), zcorn.end()),
|
||||
*max_element(zcorn.begin(), zcorn.end()) }};
|
||||
return gridlimits;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::array<int, 3> EclipseGridInspector::gridSize() const
|
||||
{
|
||||
std::array<int, 3> retval = {{ logical_gridsize_[0],
|
||||
logical_gridsize_[1],
|
||||
logical_gridsize_[2] }};
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
std::array<double, 8> EclipseGridInspector::cellZvals(int i, int j, int k) const
|
||||
{
|
||||
// Get the zcorn field.
|
||||
const std::vector<double>& z = deck_->getKeyword("ZCORN")->getSIDoubleData();
|
||||
int num_cells = logical_gridsize_[0]*logical_gridsize_[1]*logical_gridsize_[2];
|
||||
if (8*num_cells != int(z.size())) {
|
||||
throw std::runtime_error("Wrong size of ZCORN field");
|
||||
}
|
||||
|
||||
// Make the coordinate array.
|
||||
int delta[3] = { 1,
|
||||
2*logical_gridsize_[0],
|
||||
4*logical_gridsize_[0]*logical_gridsize_[1] };
|
||||
int ix = 2*(i*delta[0] + j*delta[1] + k*delta[2]);
|
||||
std::array<double, 8> cellz = {{ z[ix], z[ix + delta[0]],
|
||||
z[ix + delta[1]], z[ix + delta[1] + delta[0]],
|
||||
z[ix + delta[2]], z[ix + delta[2] + delta[0]],
|
||||
z[ix + delta[2] + delta[1]], z[ix + delta[2] + delta[1] + delta[0]] }};
|
||||
return cellz;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Opm
|
|
@ -1,103 +0,0 @@
|
|||
//===========================================================================
|
||||
//
|
||||
// File: EclipseGridInspector.h
|
||||
//
|
||||
// Created: Mon Jun 2 09:46:08 2008
|
||||
//
|
||||
// Author: Atgeirr F Rasmussen <atgeirr@sintef.no>
|
||||
//
|
||||
// $Date$
|
||||
//
|
||||
// Revision: $Id: EclipseGridInspector.h,v 1.2 2008/08/18 14:16:12 atgeirr Exp $
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
/*
|
||||
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2009, 2010 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLIPSEGRIDINSPECTOR_HEADER
|
||||
#define OPM_ECLIPSEGRIDINSPECTOR_HEADER
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/**
|
||||
@brief A class for inspecting the contents of an eclipse file.
|
||||
|
||||
Given an Eclipse deck, this class may be used to answer certain
|
||||
queries about its contents.
|
||||
|
||||
@author Atgeirr F. Rasmussen <atgeirr@sintef.no>
|
||||
@date 2008/06/02 09:46:08
|
||||
*/
|
||||
class EclipseGridInspector
|
||||
{
|
||||
public:
|
||||
/// Constructor taking a parser as argument.
|
||||
/// The parser must already have read an Eclipse file.
|
||||
EclipseGridInspector(Opm::DeckConstPtr deck);
|
||||
|
||||
/// Assuming that the pillars are vertical, compute the
|
||||
/// volume of the cell given by logical coordinates (i, j, k).
|
||||
double cellVolumeVerticalPillars(int i, int j, int k) const;
|
||||
|
||||
/// Assuming that the pillars are vertical, compute the
|
||||
/// volume of the cell given by the cell index
|
||||
double cellVolumeVerticalPillars(int cell_idx) const;
|
||||
|
||||
/// Compute the average dip in x- and y-direction of the
|
||||
/// cell tops and bottoms relative to the xy-plane
|
||||
std::pair<double,double> cellDips(int i, int j, int k) const;
|
||||
std::pair<double,double> cellDips(int cell_idx) const;
|
||||
|
||||
// Convert global cell index to logical ijk-coordinates
|
||||
std::array<int, 3> cellIdxToLogicalCoords(int cell_idx) const;
|
||||
|
||||
/// Returns a vector with the outer limits of grid (in the grid's unit).
|
||||
/// The vector contains [xmin, xmax, ymin, ymax, zmin, zmax], as
|
||||
/// read from COORDS and ZCORN
|
||||
std::array<double, 6> getGridLimits() const;
|
||||
|
||||
/// Returns the extent of the logical cartesian grid
|
||||
/// as number of cells in the (i, j, k) directions.
|
||||
std::array<int, 3> gridSize() const;
|
||||
|
||||
/// Returns the eight z-values associated with a given cell.
|
||||
/// The ordering is such that i runs fastest. That is, with
|
||||
/// L = low and H = high:
|
||||
/// {LLL, HLL, LHL, HHL, LLH, HLH, LHH, HHH }.
|
||||
std::array<double, 8> cellZvals(int i, int j, int k) const;
|
||||
|
||||
private:
|
||||
Opm::DeckConstPtr deck_;
|
||||
int logical_gridsize_[3];
|
||||
void init_();
|
||||
void checkLogicalCoords(int i, int j, int k) const;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_ECLIPSEGRIDINSPECTOR_HEADER
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef ECLIPSE_IO_UTIL_HPP
|
||||
#define ECLIPSE_IO_UTIL_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
namespace EclipseIOUtil
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
void addToStripedData(const std::vector<T>& data, std::vector<T>& result, size_t offset, size_t stride) {
|
||||
int dataindex = 0;
|
||||
for (size_t index = offset; index < result.size(); index += stride) {
|
||||
result[index] = data[dataindex];
|
||||
++dataindex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void extractFromStripedData(const std::vector<T>& data, std::vector<T>& result, size_t offset, size_t stride) {
|
||||
for (size_t index = offset; index < data.size(); index += stride) {
|
||||
result.push_back(data[index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} //namespace EclipseIOUtil
|
||||
} //namespace Opm
|
||||
|
||||
#endif //ECLIPSE_IO_UTIL_HPP
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "EclipseReader.hpp"
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/simulator/SimulatorState.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/core/grid/GridHelpers.hpp>
|
||||
#include <opm/core/io/eclipse/EclipseIOUtil.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <ert/ecl/ecl_file.h>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
void restoreOPM_XWELKeyword(const std::string& restart_filename, int reportstep, WellState& wellstate)
|
||||
{
|
||||
const char * keyword = "OPM_XWEL";
|
||||
const char* filename = restart_filename.c_str();
|
||||
ecl_file_type* file_type = ecl_file_open(filename, 0);
|
||||
|
||||
if (file_type != NULL) {
|
||||
bool block_selected = ecl_file_select_rstblock_report_step(file_type , reportstep);
|
||||
|
||||
if (block_selected) {
|
||||
ecl_kw_type* xwel = ecl_file_iget_named_kw(file_type , keyword, 0);
|
||||
const double* xwel_data = ecl_kw_get_double_ptr(xwel);
|
||||
|
||||
std::copy_n(xwel_data + wellstate.getRestartTemperatureOffset(), wellstate.temperature().size(), wellstate.temperature().begin());
|
||||
std::copy_n(xwel_data + wellstate.getRestartBhpOffset(), wellstate.bhp().size(), wellstate.bhp().begin());
|
||||
std::copy_n(xwel_data + wellstate.getRestartPerfPressOffset(), wellstate.perfPress().size(), wellstate.perfPress().begin());
|
||||
std::copy_n(xwel_data + wellstate.getRestartPerfRatesOffset(), wellstate.perfRates().size(), wellstate.perfRates().begin());
|
||||
std::copy_n(xwel_data + wellstate.getRestartWellRatesOffset(), wellstate.wellRates().size(), wellstate.wellRates().begin());
|
||||
}
|
||||
|
||||
ecl_file_close(file_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
void restoreTemperatureData(const ecl_file_type* file,
|
||||
const EclipseState& eclipse_state,
|
||||
const UnstructuredGrid& grid,
|
||||
SimulatorState& simulator_state) {
|
||||
const char* temperature_keyword = "TEMP";
|
||||
|
||||
if (ecl_file_has_kw(file , temperature_keyword)) {
|
||||
ecl_kw_type* temperature_kw = ecl_file_iget_named_kw(file, temperature_keyword, 0);
|
||||
|
||||
if (ecl_kw_get_size(temperature_kw) != Opm::UgGridHelpers::numCells(grid)) {
|
||||
throw std::runtime_error("Read of restart file: Could not restore temperature data, length of data from file not equal number of cells");
|
||||
}
|
||||
|
||||
float* temperature_data = ecl_kw_get_float_ptr(temperature_kw);
|
||||
|
||||
// factor and offset from the temperature values given in the deck to Kelvin
|
||||
double scaling = eclipse_state.getDeckUnitSystem()->parse("Temperature")->getSIScaling();
|
||||
double offset = eclipse_state.getDeckUnitSystem()->parse("Temperature")->getSIOffset();
|
||||
|
||||
for (size_t index = 0; index < simulator_state.temperature().size(); ++index) {
|
||||
simulator_state.temperature()[index] = unit::convert::from((double)temperature_data[index] - offset, scaling);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void restorePressureData(const ecl_file_type* file,
|
||||
const EclipseState& eclipse_state,
|
||||
const UnstructuredGrid& grid,
|
||||
SimulatorState& simulator_state) {
|
||||
const char* pressure_keyword = "PRESSURE";
|
||||
|
||||
if (ecl_file_has_kw(file , pressure_keyword)) {
|
||||
|
||||
ecl_kw_type* pressure_kw = ecl_file_iget_named_kw(file, pressure_keyword, 0);
|
||||
|
||||
if (ecl_kw_get_size(pressure_kw) != Opm::UgGridHelpers::numCells(grid)) {
|
||||
throw std::runtime_error("Read of restart file: Could not restore pressure data, length of data from file not equal number of cells");
|
||||
}
|
||||
|
||||
float* pressure_data = ecl_kw_get_float_ptr(pressure_kw);
|
||||
const double deck_pressure_unit = (eclipse_state.getDeckUnitSystem()->getType() == UnitSystem::UNIT_TYPE_METRIC) ? Opm::unit::barsa : Opm::unit::psia;
|
||||
for (size_t index = 0; index < simulator_state.pressure().size(); ++index) {
|
||||
simulator_state.pressure()[index] = unit::convert::from((double)pressure_data[index], deck_pressure_unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void restoreSOLUTIONData(const std::string& restart_filename,
|
||||
int reportstep,
|
||||
const EclipseState& eclipseState,
|
||||
const UnstructuredGrid& grid,
|
||||
const PhaseUsage& phaseUsage,
|
||||
SimulatorState& simulator_state)
|
||||
{
|
||||
const char* filename = restart_filename.c_str();
|
||||
ecl_file_type* file_type = ecl_file_open(filename, 0);
|
||||
|
||||
if (file_type != NULL) {
|
||||
bool block_selected = ecl_file_select_rstblock_report_step(file_type , reportstep);
|
||||
|
||||
if (block_selected) {
|
||||
|
||||
restorePressureData(file_type, eclipseState, grid, simulator_state);
|
||||
restoreTemperatureData(file_type, eclipseState, grid, simulator_state);
|
||||
|
||||
int numcells = Opm::UgGridHelpers::numCells(grid);
|
||||
|
||||
float* sgas_data = NULL;
|
||||
float* swat_data = NULL;
|
||||
|
||||
if (phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
||||
ecl_kw_type* swat_kw = ecl_file_iget_named_kw(file_type , "SWAT", 0);
|
||||
swat_data = ecl_kw_get_float_ptr(swat_kw);
|
||||
std::vector<double> swat_datavec(&swat_data[0], &swat_data[numcells]);
|
||||
EclipseIOUtil::addToStripedData(swat_datavec, simulator_state.saturation(), phaseUsage.phase_pos[BlackoilPhases::Aqua], phaseUsage.num_phases);
|
||||
}
|
||||
|
||||
if (phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
||||
ecl_kw_type* sgas_kw = ecl_file_iget_named_kw(file_type , "SGAS", 0);
|
||||
sgas_data = ecl_kw_get_float_ptr(sgas_kw);
|
||||
std::vector<double> sgas_datavec(&sgas_data[0], &sgas_data[numcells]);
|
||||
EclipseIOUtil::addToStripedData(sgas_datavec, simulator_state.saturation(), phaseUsage.phase_pos[BlackoilPhases::Vapour], phaseUsage.num_phases);
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Warning: Could not load solution data for report step " << reportstep << ", data for reportstep not found on file " << filename << std::endl;
|
||||
}
|
||||
|
||||
ecl_file_close(file_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Opm
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef ECLIPSEREADER_HPP
|
||||
#define ECLIPSEREADER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/core/simulator/SimulatorState.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
///
|
||||
/// \brief restoreOPM_XWELKeyword
|
||||
/// Reading from the restart file, information stored under the OPM_XWEL keyword is in this method filled into
|
||||
/// an instance of a wellstate object.
|
||||
/// \param restart_filename
|
||||
/// The filename of the restart file.
|
||||
/// \param reportstep
|
||||
/// The report step to restart from.
|
||||
/// \param wellstate
|
||||
/// An instance of a WellState object, with correct size for each of the 5 contained std::vector<double> objects.
|
||||
///
|
||||
void restoreOPM_XWELKeyword(const std::string& restart_filename, int report_step, WellState& wellState);
|
||||
void restoreSOLUTIONData(const std::string& restart_filename,
|
||||
int report_step,
|
||||
const EclipseState &eclipseState,
|
||||
const UnstructuredGrid& grid,
|
||||
const PhaseUsage& phaseUsage,
|
||||
SimulatorState& simulator_state);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // ECLIPSEREADER_HPP
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLIPSEUNITS_HEADER_INCLUDED
|
||||
#define OPM_ECLIPSEUNITS_HEADER_INCLUDED
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
struct EclipseUnits
|
||||
{
|
||||
double length;
|
||||
double time;
|
||||
double density;
|
||||
double polymer_density;
|
||||
double pressure;
|
||||
double compressibility;
|
||||
double viscosity;
|
||||
double permeability;
|
||||
double liqvol_s;
|
||||
double liqvol_r;
|
||||
double gasvol_s;
|
||||
double gasvol_r;
|
||||
double transmissibility;
|
||||
|
||||
void setToOne()
|
||||
{
|
||||
length = 1.0;
|
||||
time = 1.0;
|
||||
density = 1.0;
|
||||
polymer_density = 1.0;
|
||||
pressure = 1.0;
|
||||
compressibility = 1.0;
|
||||
viscosity = 1.0;
|
||||
permeability = 1.0;
|
||||
liqvol_s = 1.0;
|
||||
liqvol_r = 1.0;
|
||||
gasvol_s = 1.0;
|
||||
gasvol_r = 1.0;
|
||||
transmissibility = 1.0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_ECLIPSEUNITS_HEADER_INCLUDED
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <opm/core/io/eclipse/EclipseWriteRFTHandler.hpp>
|
||||
#include <opm/core/simulator/SimulatorState.hpp>
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/WellSet.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
|
||||
|
||||
|
||||
#include <ert/ecl/ecl_rft_node.h>
|
||||
#include <ert/ecl/ecl_rft_file.h>
|
||||
|
||||
|
||||
|
||||
namespace Opm {
|
||||
namespace EclipseWriterDetails {
|
||||
|
||||
EclipseWriteRFTHandler::EclipseWriteRFTHandler(const int * compressedToCartesianCellIdx, size_t numCells, size_t cartesianSize) {
|
||||
initGlobalToActiveIndex(compressedToCartesianCellIdx, numCells, cartesianSize);
|
||||
}
|
||||
|
||||
void EclipseWriteRFTHandler::writeTimeStep(const std::string& filename,
|
||||
const ert_ecl_unit_enum ecl_unit,
|
||||
const SimulatorTimerInterface& simulatorTimer,
|
||||
std::vector<WellConstPtr>& wells,
|
||||
EclipseGridConstPtr eclipseGrid,
|
||||
std::vector<double>& pressure,
|
||||
std::vector<double>& swat,
|
||||
std::vector<double>& sgas) {
|
||||
|
||||
|
||||
|
||||
std::vector<ecl_rft_node_type *> rft_nodes;
|
||||
for (std::vector<WellConstPtr>::const_iterator ci = wells.begin(); ci != wells.end(); ++ci) {
|
||||
WellConstPtr well = *ci;
|
||||
if ((well->getRFTActive(simulatorTimer.reportStepNum())) || (well->getPLTActive(simulatorTimer.reportStepNum()))) {
|
||||
ecl_rft_node_type * ecl_node = createEclRFTNode(well,
|
||||
simulatorTimer,
|
||||
eclipseGrid,
|
||||
pressure,
|
||||
swat,
|
||||
sgas);
|
||||
|
||||
// TODO: replace this silenced warning with an appropriate
|
||||
// use of the OpmLog facilities.
|
||||
// if (well->getPLTActive(simulatorTimer.reportStepNum())) {
|
||||
// std::cerr << "PLT not supported, writing RFT data" << std::endl;
|
||||
// }
|
||||
|
||||
rft_nodes.push_back(ecl_node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (rft_nodes.size() > 0) {
|
||||
ecl_rft_file_update(filename.c_str(), rft_nodes.data(), rft_nodes.size(), ecl_unit);
|
||||
}
|
||||
|
||||
//Cleanup: The ecl_rft_file_update method takes care of freeing the ecl_rft_nodes that it receives.
|
||||
// Each ecl_rft_node is again responsible for freeing it's cells.
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ecl_rft_node_type * EclipseWriteRFTHandler::createEclRFTNode(WellConstPtr well,
|
||||
const SimulatorTimerInterface& simulatorTimer,
|
||||
EclipseGridConstPtr eclipseGrid,
|
||||
const std::vector<double>& pressure,
|
||||
const std::vector<double>& swat,
|
||||
const std::vector<double>& sgas) {
|
||||
|
||||
|
||||
const std::string& well_name = well->name();
|
||||
size_t timestep = (size_t)simulatorTimer.reportStepNum();
|
||||
time_t recording_date = simulatorTimer.currentPosixTime();
|
||||
double days = Opm::unit::convert::to(simulatorTimer.simulationTimeElapsed(), Opm::unit::day);
|
||||
|
||||
std::string type = "RFT";
|
||||
ecl_rft_node_type * ecl_rft_node = ecl_rft_node_alloc_new(well_name.c_str(), type.c_str(), recording_date, days);
|
||||
|
||||
CompletionSetConstPtr completionsSet = well->getCompletions(timestep);
|
||||
for (size_t index = 0; index < completionsSet->size(); ++index) {
|
||||
CompletionConstPtr completion = completionsSet->get(index);
|
||||
size_t i = (size_t)completion->getI();
|
||||
size_t j = (size_t)completion->getJ();
|
||||
size_t k = (size_t)completion->getK();
|
||||
|
||||
size_t global_index = eclipseGrid->getGlobalIndex(i,j,k);
|
||||
int active_index = globalToActiveIndex_[global_index];
|
||||
|
||||
if (active_index > -1) {
|
||||
double depth = eclipseGrid->getCellDepth(i,j,k);
|
||||
double completion_pressure = pressure.size() > 0 ? pressure[active_index] : 0.0;
|
||||
double saturation_water = swat.size() > 0 ? swat[active_index] : 0.0;
|
||||
double saturation_gas = sgas.size() > 0 ? sgas[active_index] : 0.0;
|
||||
|
||||
ecl_rft_cell_type * ecl_rft_cell = ecl_rft_cell_alloc_RFT( i ,j, k , depth, completion_pressure, saturation_water, saturation_gas);
|
||||
ecl_rft_node_append_cell( ecl_rft_node , ecl_rft_cell);
|
||||
}
|
||||
}
|
||||
|
||||
return ecl_rft_node;
|
||||
}
|
||||
|
||||
|
||||
void EclipseWriteRFTHandler::initGlobalToActiveIndex(const int * compressedToCartesianCellIdx, size_t numCells, size_t cartesianSize) {
|
||||
globalToActiveIndex_.resize(cartesianSize, -1);
|
||||
for (size_t active_index = 0; active_index < numCells; ++active_index) {
|
||||
//If compressedToCartesianCellIdx is NULL, assume no compressed to cartesian mapping, set global equal to active index
|
||||
int global_index = (NULL != compressedToCartesianCellIdx) ? compressedToCartesianCellIdx[active_index] : active_index;
|
||||
globalToActiveIndex_[global_index] = active_index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace EclipseWriterDetails
|
||||
}//namespace Opm
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLIPSE_WRITE_RFT_HANDLER_HPP
|
||||
#define OPM_ECLIPSE_WRITE_RFT_HANDLER_HPP
|
||||
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/simulator/SimulatorState.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <ert/ecl/ecl_rft_node.h>
|
||||
#include <ert/ecl/ecl_util.h>
|
||||
|
||||
|
||||
namespace Opm {
|
||||
namespace EclipseWriterDetails {
|
||||
|
||||
|
||||
class EclipseWriteRFTHandler {
|
||||
|
||||
public:
|
||||
EclipseWriteRFTHandler(const int * compressedToCartesianCellIdx, size_t numCells, size_t cartesianSize);
|
||||
|
||||
|
||||
void writeTimeStep(const std::string& filename,
|
||||
const ert_ecl_unit_enum ecl_unit,
|
||||
const SimulatorTimerInterface& simulatorTimer,
|
||||
std::vector<WellConstPtr>& wells,
|
||||
EclipseGridConstPtr eclipseGrid,
|
||||
std::vector<double>& pressure,
|
||||
std::vector<double>& swat,
|
||||
std::vector<double>& sgas);
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
ecl_rft_node_type * createEclRFTNode(WellConstPtr well,
|
||||
const SimulatorTimerInterface& simulatorTimer,
|
||||
EclipseGridConstPtr eclipseGrid,
|
||||
const std::vector<double>& pressure,
|
||||
const std::vector<double>& swat,
|
||||
const std::vector<double>& sgas);
|
||||
|
||||
void initGlobalToActiveIndex(const int * compressedToCartesianCellIdx, size_t numCells, size_t cartesianSize);
|
||||
|
||||
std::vector<int> globalToActiveIndex_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}//namespace EclipseWriterDetails
|
||||
}//namespace Opm
|
||||
|
||||
|
||||
#endif //OPM_ECLIPSE_WRITE_RFT_HANDLER_HPP
|
File diff suppressed because it is too large
Load Diff
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2013 Andreas Lauser
|
||||
Copyright (c) 2013 Uni Research AS
|
||||
Copyright (c) 2014 IRIS AS
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLIPSE_WRITER_HPP
|
||||
#define OPM_ECLIPSE_WRITER_HPP
|
||||
|
||||
#include <opm/core/io/OutputWriter.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/wells.h> // WellType
|
||||
#include <opm/core/simulator/SimulatorTimerInterface.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
// forward declarations
|
||||
namespace EclipseWriterDetails {
|
||||
class Summary;
|
||||
}
|
||||
|
||||
class SimulatorState;
|
||||
class WellState;
|
||||
|
||||
namespace parameter { class ParameterGroup; }
|
||||
|
||||
/*!
|
||||
* \brief A class to write the reservoir state and the well state of a
|
||||
* blackoil simulation to disk using the Eclipse binary format.
|
||||
*
|
||||
* This class only writes files if the 'write_output' parameter is set
|
||||
* to 1. It needs the ERT libraries to write to disk, so if the
|
||||
* 'write_output' parameter is set but ERT is not available, all
|
||||
* methods throw a std::runtime_error.
|
||||
*/
|
||||
class EclipseWriter : public OutputWriter
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* \brief Sets the common attributes required to write eclipse
|
||||
* binary files using ERT.
|
||||
*/
|
||||
EclipseWriter(const parameter::ParameterGroup& params,
|
||||
Opm::EclipseStateConstPtr eclipseState,
|
||||
const Opm::PhaseUsage &phaseUsage,
|
||||
int numCells,
|
||||
const int* compressedToCartesianCellIdx);
|
||||
|
||||
/**
|
||||
* We need a destructor in the compilation unit to avoid the
|
||||
* EclipseSummary being a complete type here.
|
||||
*/
|
||||
virtual ~EclipseWriter ();
|
||||
|
||||
/**
|
||||
* Write the static eclipse data (grid, PVT curves, etc) to disk.
|
||||
*/
|
||||
virtual void writeInit(const SimulatorTimerInterface &timer);
|
||||
|
||||
/*!
|
||||
* \brief Write a reservoir state and summary information to disk.
|
||||
*
|
||||
*
|
||||
* The reservoir state can be inspected with visualization tools like
|
||||
* ResInsight.
|
||||
*
|
||||
* The summary information can then be visualized using tools from
|
||||
* ERT or ECLIPSE. Note that calling this method is only
|
||||
* meaningful after the first time step has been completed.
|
||||
*
|
||||
* \param[in] timer The timer providing time step and time information
|
||||
* \param[in] reservoirState The thermodynamic state of the reservoir
|
||||
* \param[in] wellState The production/injection data for all wells
|
||||
*/
|
||||
virtual void writeTimeStep(const SimulatorTimerInterface& timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState, bool isSubstep );
|
||||
|
||||
|
||||
static int eclipseWellTypeMask(WellType wellType, WellInjector::TypeEnum injectorType);
|
||||
static int eclipseWellStatusMask(WellCommon::StatusEnum wellStatus);
|
||||
static ert_ecl_unit_enum convertUnitTypeErtEclUnitEnum(UnitSystem::UnitType unit);
|
||||
|
||||
private:
|
||||
Opm::EclipseStateConstPtr eclipseState_;
|
||||
int numCells_;
|
||||
std::array<int, 3> cartesianSize_;
|
||||
const int* compressedToCartesianCellIdx_;
|
||||
std::vector< int > gridToEclipseIdx_;
|
||||
double deckToSiPressure_;
|
||||
double deckToSiTemperatureFactor_;
|
||||
double deckToSiTemperatureOffset_;
|
||||
bool enableOutput_;
|
||||
int writeStepIdx_;
|
||||
int reportStepIdx_;
|
||||
std::string outputDir_;
|
||||
std::string baseName_;
|
||||
PhaseUsage phaseUsage_; // active phases in the input deck
|
||||
std::shared_ptr<EclipseWriterDetails::Summary> summary_;
|
||||
|
||||
void init(const parameter::ParameterGroup& params);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<EclipseWriter> EclipseWriterPtr;
|
||||
typedef std::shared_ptr<const EclipseWriter> EclipseWriterConstPtr;
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_ECLIPSE_WRITER_HPP
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/io/eclipse/writeECLData.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef HAVE_ERT // This one goes almost to the bottom of the file
|
||||
|
||||
#include <ert/ecl/ecl_grid.h>
|
||||
#include <ert/ecl/ecl_util.h>
|
||||
#include <ert/ecl/ecl_rst_file.h>
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
static ecl_kw_type * ecl_kw_wrapper( const UnstructuredGrid& grid,
|
||||
const std::string& kw_name ,
|
||||
const std::vector<double> * data ,
|
||||
int offset ,
|
||||
int stride ) {
|
||||
|
||||
if (stride <= 0)
|
||||
OPM_THROW(std::runtime_error, "Vector strides must be positive. Got stride = " << stride);
|
||||
if ((stride * std::vector<double>::size_type(grid.number_of_cells)) != data->size())
|
||||
OPM_THROW(std::runtime_error, "Internal mismatch grid.number_of_cells: " << grid.number_of_cells << " data size: " << data->size() / stride);
|
||||
{
|
||||
ecl_kw_type * ecl_kw = ecl_kw_alloc( kw_name.c_str() , grid.number_of_cells , ECL_FLOAT_TYPE );
|
||||
for (int i=0; i < grid.number_of_cells; i++)
|
||||
ecl_kw_iset_float( ecl_kw , i , (*data)[i*stride + offset]);
|
||||
return ecl_kw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This function will write the data solution data in the DataMap
|
||||
@data as an ECLIPSE restart file, in addition to the solution
|
||||
fields the ECLIPSE restart file will have a minimum (hopefully
|
||||
sufficient) amount of header information.
|
||||
|
||||
The ECLIPSE restart files come in two varietes; unified restart
|
||||
files which have all the report steps lumped together in one large
|
||||
chunk and non-unified restart files which are one file for each
|
||||
report step. In addition the files can be either formatted
|
||||
(i.e. ASCII) or unformatted (i.e. binary).
|
||||
|
||||
The writeECLData() function has two hardcoded settings:
|
||||
'file_type' and 'fmt_file' which regulate which type of files the
|
||||
should be created. The extension of the files follow a convention:
|
||||
|
||||
Unified, formatted : .FUNRST
|
||||
Unified, unformatted : .UNRST
|
||||
Multiple, formatted : .Fnnnn
|
||||
Multiple, unformatted : .Xnnnn
|
||||
|
||||
For the multiple files the 'nnnn' part is the report number,
|
||||
formatted with '%04d' format specifier. The writeECLData()
|
||||
function will use the ecl_util_alloc_filename() function to create
|
||||
an ECLIPSE filename according to this conventions.
|
||||
*/
|
||||
|
||||
void writeECLData(const UnstructuredGrid& grid,
|
||||
const DataMap& data,
|
||||
const int current_step,
|
||||
const double current_time,
|
||||
const boost::posix_time::ptime& current_date_time,
|
||||
const std::string& output_dir,
|
||||
const std::string& base_name) {
|
||||
|
||||
ecl_file_enum file_type = ECL_UNIFIED_RESTART_FILE; // Alternatively ECL_RESTART_FILE for multiple restart files.
|
||||
bool fmt_file = false;
|
||||
|
||||
char * filename = ecl_util_alloc_filename(output_dir.c_str() , base_name.c_str() , file_type , fmt_file , current_step );
|
||||
int phases = ECL_OIL_PHASE + ECL_WATER_PHASE;
|
||||
time_t date = 0;
|
||||
int nx = grid.cartdims[0];
|
||||
int ny = grid.cartdims[1];
|
||||
int nz = grid.cartdims[2];
|
||||
int nactive = grid.number_of_cells;
|
||||
ecl_rst_file_type * rst_file;
|
||||
|
||||
{
|
||||
using namespace boost::posix_time;
|
||||
ptime t0( boost::gregorian::date(1970 , 1 ,1) );
|
||||
time_duration::sec_type seconds = (current_date_time - t0).total_seconds();
|
||||
|
||||
date = time_t( seconds );
|
||||
}
|
||||
|
||||
if (current_step > 0 && file_type == ECL_UNIFIED_RESTART_FILE)
|
||||
rst_file = ecl_rst_file_open_append( filename );
|
||||
else
|
||||
rst_file = ecl_rst_file_open_write( filename );
|
||||
|
||||
{
|
||||
ecl_rsthead_type rsthead_data = {};
|
||||
|
||||
const int num_wells = 0;
|
||||
const int niwelz = 0;
|
||||
const int nzwelz = 0;
|
||||
const int niconz = 0;
|
||||
const int ncwmax = 0;
|
||||
|
||||
rsthead_data.nx = nx;
|
||||
rsthead_data.ny = ny;
|
||||
rsthead_data.nz = nz;
|
||||
rsthead_data.nwells = num_wells;
|
||||
rsthead_data.niwelz = niwelz;
|
||||
rsthead_data.nzwelz = nzwelz;
|
||||
rsthead_data.niconz = niconz;
|
||||
rsthead_data.ncwmax = ncwmax;
|
||||
rsthead_data.nactive = nactive;
|
||||
rsthead_data.phase_sum = phases;
|
||||
rsthead_data.sim_time = date;
|
||||
|
||||
rsthead_data.sim_days = Opm::unit::convert::to(current_time, Opm::unit::day); //Data for doubhead
|
||||
|
||||
ecl_rst_file_fwrite_header( rst_file , current_step , &rsthead_data);
|
||||
}
|
||||
|
||||
ecl_rst_file_start_solution( rst_file );
|
||||
|
||||
{
|
||||
DataMap::const_iterator i = data.find("pressure");
|
||||
if (i != data.end()) {
|
||||
ecl_kw_type * pressure_kw = ecl_kw_wrapper( grid , "PRESSURE" , i->second , 0 , 1);
|
||||
ecl_rst_file_add_kw( rst_file , pressure_kw );
|
||||
ecl_kw_free( pressure_kw );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
DataMap::const_iterator i = data.find("saturation");
|
||||
if (i != data.end()) {
|
||||
if (int(i->second->size()) != 2 * grid.number_of_cells) {
|
||||
OPM_THROW(std::runtime_error, "writeECLData() requires saturation field to have two phases.");
|
||||
}
|
||||
ecl_kw_type * swat_kw = ecl_kw_wrapper( grid , "SWAT" , i->second , 0 , 2);
|
||||
ecl_rst_file_add_kw( rst_file , swat_kw );
|
||||
ecl_kw_free( swat_kw );
|
||||
}
|
||||
}
|
||||
|
||||
ecl_rst_file_end_solution( rst_file );
|
||||
ecl_rst_file_close( rst_file );
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
|
||||
#else // that is, we have not defined HAVE_ERT
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
void writeECLData(const UnstructuredGrid&,
|
||||
const DataMap&,
|
||||
const int,
|
||||
const double,
|
||||
const boost::posix_time::ptime&,
|
||||
const std::string&,
|
||||
const std::string&)
|
||||
{
|
||||
OPM_THROW(std::runtime_error, "Cannot call writeECLData() without ERT library support. Reconfigure opm-core with ERT support and recompile.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WRITEECLDATA_HEADER_INCLUDED
|
||||
#define OPM_WRITEECLDATA_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/utility/DataMap.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
// ECLIPSE output for general grids.
|
||||
void writeECLData(const UnstructuredGrid& grid,
|
||||
const DataMap& data,
|
||||
const int current_step,
|
||||
const double current_time,
|
||||
const boost::posix_time::ptime& current_date_time,
|
||||
const std::string& output_dir,
|
||||
const std::string& base_name);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,408 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: vag.cpp
|
||||
//
|
||||
// Created: 2012-06-08 15:45:53+0200
|
||||
//
|
||||
// Authors: Knut-Andreas Lie <Knut-Andreas.Lie@sintef.no>
|
||||
// Halvor M. Nilsen <HalvorMoll.Nilsen@sintef.no>
|
||||
// Atgeirr F. Rasmussen <atgeirr@sintef.no>
|
||||
// Xavier Raynaud <Xavier.Raynaud@sintef.no>
|
||||
// Bård Skaflestad <Bard.Skaflestad@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <opm/core/io/vag/vag.hpp>
|
||||
#include <opm/core/grid/cornerpoint_grid.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
namespace Opm
|
||||
{
|
||||
void readPosStruct(std::istream& is,int n,PosStruct& pos_struct){
|
||||
using namespace std;
|
||||
//PosStruct pos_struct;
|
||||
pos_struct.pos.resize(n+1);
|
||||
pos_struct.pos[0]=0;
|
||||
for(int i=0;i< n;++i){
|
||||
int number;
|
||||
is >> number ;
|
||||
//cout <<number << endl;
|
||||
pos_struct.pos[i+1]=pos_struct.pos[i]+number;
|
||||
for(int j=0;j< number;++j){
|
||||
int value;
|
||||
is >> value;
|
||||
// cout << value << " ";
|
||||
pos_struct.value.push_back(value);
|
||||
}
|
||||
//cout << endl;
|
||||
}
|
||||
if(int(pos_struct.value.size()) != pos_struct.pos[n]){
|
||||
cerr << "Failed to read pos structure" << endl;
|
||||
cerr << "pos_struct.value.size()" << pos_struct.value.size() << endl;
|
||||
cerr << "pos_struct.pos[n+1]" << pos_struct.pos[n] << endl;
|
||||
}
|
||||
}
|
||||
void writePosStruct(std::ostream& os,PosStruct& pos_struct){
|
||||
using namespace std;
|
||||
//PosStruct pos_struct;
|
||||
if(pos_struct.pos.size()==0){
|
||||
return;
|
||||
}
|
||||
int n=pos_struct.pos.size()-1;
|
||||
pos_struct.pos.resize(n+1);
|
||||
pos_struct.pos[0]=0;
|
||||
for(int i=0;i< n;++i){
|
||||
int number=pos_struct.pos[i+1]-pos_struct.pos[i];
|
||||
os << number << " ";
|
||||
for(int j=0;j< number;++j){
|
||||
os << pos_struct.value[pos_struct.pos[i]+j] << " ";
|
||||
}
|
||||
os << endl;
|
||||
}
|
||||
}
|
||||
void readVagGrid(std::istream& is,Opm::VAG& vag_grid){
|
||||
using namespace std;
|
||||
using namespace Opm;
|
||||
while (!is.eof()) {
|
||||
string keyword;
|
||||
is >> keyword;
|
||||
//cout << keyword<< endl;
|
||||
if(keyword == "Number"){
|
||||
string stmp;
|
||||
is >> stmp;
|
||||
if(stmp == "of"){
|
||||
string entity;
|
||||
is >> entity;
|
||||
getline(is,stmp);
|
||||
int number;
|
||||
is >> number;
|
||||
if(entity=="vertices"){
|
||||
vag_grid.number_of_vertices=number;
|
||||
}else if((entity=="volumes") || (entity=="control")){
|
||||
vag_grid.number_of_volumes=number;
|
||||
}else if(entity=="faces"){
|
||||
vag_grid.number_of_faces=number;
|
||||
}else if(entity=="edges"){
|
||||
vag_grid.number_of_edges=number;
|
||||
}
|
||||
cout << "Found Number of: " << entity <<" " << number << endl;
|
||||
} else {
|
||||
cerr << "Wrong format: Not of after Number" << endl;
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
// read geometry defined by vertices
|
||||
if(keyword=="Vertices"){
|
||||
int number;
|
||||
is >> number;
|
||||
vag_grid.vertices.resize(3*number);// assume 3d data
|
||||
readVector(is,vag_grid.vertices);
|
||||
}
|
||||
// here starts the reding of all pos structures
|
||||
else if(keyword=="Volumes->Faces" || keyword=="Volumes->faces"){
|
||||
//vag_grid.volumes_to_faces=
|
||||
int number;
|
||||
is >> number;
|
||||
readPosStruct(is,number,vag_grid.volumes_to_faces);
|
||||
cout << "Volumes->Faces: Number of " << number << endl;
|
||||
}else if(keyword=="Faces->edges" || keyword=="Faces->Edges" || keyword=="Faces->Edgess"){
|
||||
int number;
|
||||
is >> number;
|
||||
//vag_grid.volumes_to_faces=
|
||||
readPosStruct(is,number,vag_grid.faces_to_edges);
|
||||
cout << "Faces->edges: Number of " << number << endl;
|
||||
}else if(keyword=="Faces->Vertices" || keyword=="Faces->vertices"){
|
||||
int number;
|
||||
is >> number;
|
||||
//vag_grid.volumes_to_faces=
|
||||
readPosStruct(is,number,vag_grid.faces_to_vertices);
|
||||
cout << "Faces->Vertices: Number of " << number << endl;
|
||||
}else if(keyword=="Volumes->Vertices" || keyword=="Volumes->Verticess"){
|
||||
int number;
|
||||
is >> number;
|
||||
//vag_grid.volumes_to_faces=
|
||||
readPosStruct(is,number,vag_grid.volumes_to_vertices);
|
||||
cout << "Volumes->Vertices: Number of " << number << endl;
|
||||
}
|
||||
|
||||
// read simple mappings
|
||||
else if(keyword=="Edge" || keyword=="Edges"){
|
||||
int number;
|
||||
is >> number;
|
||||
vag_grid.edges.resize(2*number);
|
||||
readVector(is,vag_grid.edges);
|
||||
cout << "Edges: Number of " << number << endl;
|
||||
}else if(keyword=="Faces->Volumes" || keyword=="Faces->Control"){
|
||||
int number;
|
||||
if(keyword=="Faces->Control"){
|
||||
string vol;
|
||||
is >> vol;
|
||||
}
|
||||
is >> number;
|
||||
vag_grid.faces_to_volumes.resize(2*number);
|
||||
readVector(is,vag_grid.faces_to_volumes);
|
||||
cout << "Faces->Volumes: Number of " << number << endl;
|
||||
}
|
||||
// read material
|
||||
else if(keyword=="Material"){
|
||||
string snum;
|
||||
is >> snum;
|
||||
int number;
|
||||
is >> number;
|
||||
cout << "Material number " << number << endl;
|
||||
// we read all the rest into doubles
|
||||
while(!is.eof()){
|
||||
double value;
|
||||
is >> value;
|
||||
//cout << value << endl;
|
||||
vag_grid.material.push_back(value);
|
||||
}
|
||||
}else{
|
||||
//cout << "keyword;
|
||||
}
|
||||
//cout << "Found" << keyword << "Number of " << number << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
void vagToUnstructuredGrid(Opm::VAG& vag_grid,UnstructuredGrid& grid){
|
||||
using namespace std;
|
||||
using namespace Opm;
|
||||
cout << "Converting grid" << endl;
|
||||
cout << "Warning:: orignial grid may not be edge confomal" << endl;
|
||||
cout << " inverse mappings from edges will be wrong" << endl;
|
||||
grid.dimensions=3;
|
||||
grid.number_of_cells=vag_grid.number_of_volumes;
|
||||
grid.number_of_faces=vag_grid.number_of_faces;
|
||||
grid.number_of_nodes=vag_grid.number_of_vertices;
|
||||
|
||||
// fill face_nodes
|
||||
for(int i=0;i< int(vag_grid.faces_to_vertices.pos.size());++i){
|
||||
grid.face_nodepos[i] = vag_grid.faces_to_vertices.pos[i];
|
||||
}
|
||||
for(int i=0;i< int(vag_grid.faces_to_vertices.value.size());++i){
|
||||
grid.face_nodes[i] = vag_grid.faces_to_vertices.value[i]-1;
|
||||
}
|
||||
// fill cell_face
|
||||
for(int i=0;i< int(vag_grid.volumes_to_faces.pos.size());++i){
|
||||
grid.cell_facepos[i] = vag_grid.volumes_to_faces.pos[i];
|
||||
}
|
||||
for(int i=0;i< int(vag_grid.volumes_to_faces.value.size());++i){
|
||||
grid.cell_faces[i] = vag_grid.volumes_to_faces.value[i]-1;
|
||||
}
|
||||
// fill face_cells
|
||||
for(int i=0;i< int(vag_grid.faces_to_volumes.size());++i){
|
||||
grid.face_cells[i] = vag_grid.faces_to_volumes[i]-1;
|
||||
}
|
||||
|
||||
// fill node_cordinates. This is the only geometry given in the vag
|
||||
for(int i=0;i< int(vag_grid.vertices.size());++i){
|
||||
grid.node_coordinates[i] = vag_grid.vertices[i];
|
||||
}
|
||||
// informations in edges, faces_to_eges, faces_to_vertices, volume_to_vertices and materials
|
||||
// is not used
|
||||
cout << "Computing geometry" << endl;
|
||||
compute_geometry(&grid);
|
||||
|
||||
}
|
||||
|
||||
void unstructuredGridToVag(UnstructuredGrid& grid,Opm::VAG& vag_grid){
|
||||
using namespace std;
|
||||
using namespace Opm;
|
||||
cout << "Converting grid" << endl;
|
||||
// grid.dimensions=3;
|
||||
vag_grid.number_of_volumes=grid.number_of_cells;
|
||||
vag_grid.number_of_faces=grid.number_of_faces;
|
||||
vag_grid.number_of_vertices=grid.number_of_nodes;
|
||||
|
||||
// resizing vectors
|
||||
vag_grid.vertices.resize(grid.number_of_nodes*3);
|
||||
vag_grid.faces_to_vertices.pos.resize(grid.number_of_faces+1);
|
||||
vag_grid.faces_to_vertices.value.resize(grid.face_nodepos[grid.number_of_faces]);
|
||||
vag_grid.faces_to_volumes.resize(2*grid.number_of_faces);
|
||||
vag_grid.volumes_to_faces.pos.resize(grid.number_of_cells+1);
|
||||
vag_grid.volumes_to_faces.value.resize(grid.cell_facepos[grid.number_of_cells]);//not known
|
||||
|
||||
|
||||
|
||||
|
||||
// fill face_nodes
|
||||
for(int i=0;i< int(vag_grid.faces_to_vertices.pos.size());++i){
|
||||
vag_grid.faces_to_vertices.pos[i] = grid.face_nodepos[i];
|
||||
}
|
||||
|
||||
for(int i=0;i< int(vag_grid.faces_to_vertices.value.size());++i){
|
||||
vag_grid.faces_to_vertices.value[i] = grid.face_nodes[i] +1;
|
||||
}
|
||||
|
||||
// fill cell_face
|
||||
for(int i=0;i< int(vag_grid.volumes_to_faces.pos.size());++i){
|
||||
vag_grid.volumes_to_faces.pos[i] = grid.cell_facepos[i];
|
||||
}
|
||||
for(int i=0;i< int(vag_grid.volumes_to_faces.value.size());++i){
|
||||
vag_grid.volumes_to_faces.value[i] = grid.cell_faces[i] +1;
|
||||
}
|
||||
// fill face_cells
|
||||
for(int i=0;i< int(vag_grid.faces_to_volumes.size());++i){
|
||||
vag_grid.faces_to_volumes[i] = grid.face_cells[i] +1;
|
||||
}
|
||||
|
||||
// fill node_cordinates. This is the only geometry given in the vag
|
||||
for(int i=0;i< int(vag_grid.vertices.size());++i){
|
||||
vag_grid.vertices[i] = grid.node_coordinates[i];
|
||||
}
|
||||
|
||||
|
||||
// The missing field need to be constructed
|
||||
// gennerate volume to vertice mapping
|
||||
std::vector< std::set<int> > volumes_to_vertices(grid.number_of_cells);
|
||||
for(int i=0;i < grid.number_of_cells; ++i){
|
||||
int nlf=grid.cell_facepos[i+1]-grid.cell_facepos[i];
|
||||
std::set<int> nodes;
|
||||
for(int j=0; j < nlf; ++j){
|
||||
int face = grid.cell_faces[grid.cell_facepos[i]+j];
|
||||
int nlv = grid.face_nodepos[face+1]-grid.face_nodepos[face];
|
||||
for(int k=0; k< nlv; ++k){
|
||||
int node = grid.face_nodes[grid.face_nodepos[face]+k]+1;
|
||||
nodes.insert(node);
|
||||
}
|
||||
}
|
||||
volumes_to_vertices[i]=nodes;
|
||||
}
|
||||
|
||||
// fill volume to vertice map
|
||||
vag_grid.volumes_to_vertices.pos.resize(grid.number_of_cells+1);
|
||||
vag_grid.volumes_to_vertices.value.resize(0);
|
||||
vag_grid.volumes_to_vertices.pos[0]=0;
|
||||
for(int i=0;i < grid.number_of_cells;++i){
|
||||
int nv=volumes_to_vertices[i].size();
|
||||
vag_grid.volumes_to_vertices.pos[i+1]=vag_grid.volumes_to_vertices.pos[i]+nv;
|
||||
std::set<int>::iterator it;
|
||||
for(it=volumes_to_vertices[i].begin();it!=volumes_to_vertices[i].end();++it){
|
||||
vag_grid.volumes_to_vertices.value.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
std::set< std::set<int> > edges;
|
||||
std::vector< std::vector< std::set<int> > > faces_spares;
|
||||
int nfe=0;
|
||||
faces_spares.resize(grid.number_of_faces);
|
||||
for(int i=0;i < grid.number_of_faces;++i){
|
||||
int ne=grid.face_nodepos[i+1]-grid.face_nodepos[i];
|
||||
nfe=nfe+ne;
|
||||
|
||||
for(int j=0; j < ne-1;++j){
|
||||
int node1=grid.face_nodes[grid.face_nodepos[i]+j]+1;
|
||||
int node2=grid.face_nodes[grid.face_nodepos[i]+j+1]+1;
|
||||
std::set<int> spair;
|
||||
spair.insert(node1);
|
||||
spair.insert(node2);
|
||||
edges.insert(spair);
|
||||
faces_spares[i].push_back(spair);
|
||||
}
|
||||
// add end segment
|
||||
{
|
||||
std::set<int> spair;
|
||||
int node1=grid.face_nodes[grid.face_nodepos[i]+ne-1]+1;
|
||||
int node2=grid.face_nodes[grid.face_nodepos[i]]+1;
|
||||
spair.insert(node1);
|
||||
spair.insert(node2);
|
||||
edges.insert(spair);
|
||||
faces_spares[i].push_back(spair);
|
||||
}
|
||||
}
|
||||
|
||||
// make edge numbering and fill edges
|
||||
std::map<std::set<int>, int> edge_map;
|
||||
std::set< std::set<int> >::iterator it;
|
||||
vag_grid.edges.resize(0);
|
||||
int k=0;
|
||||
for(it=edges.begin(); it!=edges.end();++it){
|
||||
edge_map.insert(std::pair< std::set<int> , int >(*it,k));
|
||||
k=k+1;
|
||||
std::set<int>::iterator sit;
|
||||
for(sit=(*it).begin();sit!=(*it).end();++sit){
|
||||
vag_grid.edges.push_back(*sit);
|
||||
}
|
||||
}
|
||||
// fill face_to_egdes
|
||||
vag_grid.number_of_edges=edges.size();
|
||||
vag_grid.faces_to_edges.pos.resize(vag_grid.number_of_faces+1);
|
||||
for(int i=0;i < grid.number_of_faces;++i){
|
||||
int ne=grid.face_nodepos[i+1]-grid.face_nodepos[i];
|
||||
vag_grid.faces_to_edges.pos[i+1]=vag_grid.faces_to_edges.pos[i]+ne;
|
||||
for(int j=0;j<int(faces_spares[i].size());++j){
|
||||
int edge_num=edge_map[faces_spares[i][j]];
|
||||
vag_grid.faces_to_edges.value.push_back(edge_num+1);
|
||||
}
|
||||
}
|
||||
// vag_grid.edges(0);//not known
|
||||
// vag_grid.faces_to_edges// not known
|
||||
|
||||
// material // can not be extracted from the grid
|
||||
}
|
||||
|
||||
void writeVagFormat(std::ostream& os,Opm::VAG& vag_grid){
|
||||
using namespace std;
|
||||
os << "File in the Vag grid format\n";
|
||||
os << "Number of vertices " ;
|
||||
os << vag_grid.number_of_vertices << endl;;
|
||||
os <<"Number of control volume ";
|
||||
os << vag_grid.number_of_volumes << endl;
|
||||
os <<"Number of faces " ;
|
||||
os << vag_grid.number_of_faces << endl;
|
||||
os <<"Number of edges " ;
|
||||
os << vag_grid.number_of_edges << endl;
|
||||
os <<"Vertices " << vag_grid.vertices.size() << endl;
|
||||
writeVector(os, vag_grid.vertices,3);
|
||||
os << "Volumes->faces " << vag_grid.volumes_to_faces.pos.size()-1 << endl;
|
||||
writePosStruct(os, vag_grid.volumes_to_faces);
|
||||
os << "Volumes->Vertices " << vag_grid.volumes_to_vertices.pos.size()-1 << endl;
|
||||
writePosStruct(os, vag_grid.volumes_to_vertices);
|
||||
os << "Faces->edges " << vag_grid.faces_to_edges.pos.size()-1 << endl;
|
||||
writePosStruct(os, vag_grid.faces_to_edges);
|
||||
os << "Faces->vertices " << vag_grid.faces_to_vertices.pos.size()-1 << endl;
|
||||
writePosStruct(os, vag_grid.faces_to_vertices);
|
||||
os << "Faces->Control volumes " << floor(vag_grid.faces_to_volumes.size()/2) << endl;
|
||||
writeVector(os,vag_grid.faces_to_volumes,2);
|
||||
os << "Edges " << floor(vag_grid.edges.size()/2) << endl;
|
||||
writeVector(os,vag_grid.edges,2);
|
||||
/*
|
||||
assert(vag_grid.material.size()%vag_grid.number_of_volumes==0);
|
||||
int lines= floor(vag_grid.material.size()/vag_grid.number_of_volumes);
|
||||
os << "Material number " << 1 << endl;
|
||||
writeVector(os,vag_grid.material,lines);
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
/*===========================================================================
|
||||
//
|
||||
// File: vag.hpp
|
||||
//
|
||||
// Created: 2012-06-08 15:46:23+0200
|
||||
//
|
||||
// Authors: Knut-Andreas Lie <Knut-Andreas.Lie@sintef.no>
|
||||
// Halvor M. Nilsen <HalvorMoll.Nilsen@sintef.no>
|
||||
// Atgeirr F. Rasmussen <atgeirr@sintef.no>
|
||||
// Xavier Raynaud <Xavier.Raynaud@sintef.no>
|
||||
// Bård Skaflestad <Bard.Skaflestad@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_VAG_HPP_HEADER
|
||||
#define OPM_VAG_HPP_HEADER
|
||||
|
||||
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
/**
|
||||
Struct to hold mapping from the natural numbers less than pos.size()-1 to
|
||||
a set of integers. value(pos(i):pos(i+1)-1) hold the integers corresponding to i.
|
||||
pos(end)-1==value.size();
|
||||
*/
|
||||
struct PosStruct{
|
||||
std::vector<int> pos;
|
||||
std::vector<int> value;
|
||||
};
|
||||
/**
|
||||
Structure to represent the unstructured vag grid format. The format is only for
|
||||
3D grids.
|
||||
*/
|
||||
struct VAG{
|
||||
int number_of_vertices;
|
||||
int number_of_volumes;
|
||||
int number_of_faces;
|
||||
int number_of_edges;
|
||||
/** Vertices. The coordinates of vertice i is [vetices[3*i:3*i+2]*/
|
||||
std::vector<double> vertices;
|
||||
/** Mapping from volumes to faces */
|
||||
PosStruct volumes_to_faces;
|
||||
/** Mapping from volumes to vertices */
|
||||
PosStruct volumes_to_vertices;
|
||||
/** Mapping from faces to edges */
|
||||
PosStruct faces_to_edges;
|
||||
/** Mapping from faces to vertices */
|
||||
PosStruct faces_to_vertices;
|
||||
/** The edge i is given by the nodes edges[2*i:2*i+1] */
|
||||
std::vector<int> edges;
|
||||
/** The two neigbours of the face i is faces_to_volumes[2*i:2*i+1] */
|
||||
std::vector<int> faces_to_volumes;
|
||||
/** A vector containing information of each volume. The size is n*number_of_volumes.
|
||||
For each i this is the information:
|
||||
material[n*i] is the volume number and should be transformed to integer
|
||||
material[n*i+1] is a tag and should be transformed to integer
|
||||
material[n*i+2:n*(i+1)-1] represent propertices.
|
||||
*/
|
||||
std::vector<double> material;
|
||||
};
|
||||
/**
|
||||
Function the vag grid format and make a vag_grid struct. This structure
|
||||
is intended to be converted to a grid.
|
||||
\param[in] is is is stream of the file.
|
||||
\param[out] vag_grid is a reference to a vag_grid struct.
|
||||
*/
|
||||
void readVagGrid(std::istream& is,Opm::VAG& vag_grid);
|
||||
/**
|
||||
Function to write vag format.
|
||||
\param[out] is is is stream of the file.
|
||||
\param[in] vag_grid is a reference to a vag_grid struct.
|
||||
|
||||
*/
|
||||
void writeVagFormat(std::ostream& os,Opm::VAG& vag_grid);
|
||||
/**
|
||||
Function to read a vector of some type from a stream.
|
||||
\param[in] os is is stream of the file.
|
||||
\param[out] vag_grid is a resized and filled vector containing the quantiy read.
|
||||
*/
|
||||
template <typename T>
|
||||
void readVector(std::istream& is,std::vector<T>& vec){
|
||||
using namespace std;
|
||||
for(int i=0;i< int(vec.size());++i){
|
||||
is >> vec[i];
|
||||
}
|
||||
}
|
||||
/**
|
||||
Function to write a vector of some type from a stream.
|
||||
\param[in] os is is stream of the file.
|
||||
\param[out] vag_grid is a resized and filled vector containing the quantiy read.
|
||||
\param[in] n number of doubles on each line.
|
||||
*/
|
||||
template <typename T>
|
||||
void writeVector(std::ostream& os,std::vector<T>& vec,int n){
|
||||
typedef typename std::vector<T>::size_type sz_t;
|
||||
|
||||
const sz_t nn = n;
|
||||
|
||||
for (sz_t i = 0; i < vec.size(); ++i) {
|
||||
os << vec[i] << (((i % nn) == 0) ? '\n' : ' ');
|
||||
}
|
||||
|
||||
if ((vec.size() % nn) != 0) {
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Read pos struct type mapping from a stream
|
||||
\param[in] is is stream
|
||||
\param[in] n number of lines to read
|
||||
\param[out] pos_struct reference to PosStruct
|
||||
*/
|
||||
void readPosStruct(std::istream& is,int n,PosStruct& pos_struct);
|
||||
/**
|
||||
Read pos struct type mapping from a stream
|
||||
\param[in] os is stream to write to
|
||||
\param[in] pos_struct to write
|
||||
*/
|
||||
void writePosStruct(std::ostream& os,PosStruct& pos_struct);
|
||||
|
||||
/**
|
||||
Fill a UnstructuredGrid from a vag_grid.
|
||||
\param[out] vag_grid s is a valid vag_grid struct.
|
||||
\param[in] grid is a grid with have allocated correct size to each pointer.
|
||||
*/
|
||||
void vagToUnstructuredGrid(Opm::VAG& vag_grid,UnstructuredGrid& grid);
|
||||
|
||||
/**
|
||||
Fill a vag_grid from UnstructuredGrid
|
||||
\param[out] vag_grid s is a valid vag_grid struct.
|
||||
\param[in] grid is a grid with have allocated correct size to each pointer.
|
||||
*/
|
||||
void unstructuredGridToVag(UnstructuredGrid& grid, Opm::VAG& vag_grid);
|
||||
}
|
||||
#endif /* OPM_VAG_HPP_HEADER */
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <opm/core/io/vtk/writeVtkData.hpp>
|
||||
#include <opm/core/utility/DataMap.hpp>
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <set>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
void writeVtkData(const std::array<int, 3>& dims,
|
||||
const std::array<double, 3>& cell_size,
|
||||
const DataMap& data,
|
||||
std::ostream& os)
|
||||
{
|
||||
// Dimension is hardcoded in the prototype and the next two lines,
|
||||
// but the rest is flexible (allows dimension == 2 or 3).
|
||||
int dimension = 3;
|
||||
int num_cells = dims[0]*dims[1]*dims[2];
|
||||
|
||||
assert(dimension == 2 || dimension == 3);
|
||||
assert(num_cells == dims[0]*dims[1]* (dimension == 2 ? 1 : dims[2]));
|
||||
|
||||
os << "# vtk DataFile Version 2.0\n";
|
||||
os << "Structured Grid\n \n";
|
||||
os << "ASCII \n";
|
||||
os << "DATASET STRUCTURED_POINTS\n";
|
||||
|
||||
os << "DIMENSIONS "
|
||||
<< dims[0] + 1 << " "
|
||||
<< dims[1] + 1 << " ";
|
||||
if (dimension == 3) {
|
||||
os << dims[2] + 1;
|
||||
} else {
|
||||
os << 1;
|
||||
}
|
||||
os << "\n";
|
||||
|
||||
os << "ORIGIN " << 0.0 << " " << 0.0 << " " << 0.0 << "\n";
|
||||
|
||||
os << "SPACING " << cell_size[0] << " " << cell_size[1];
|
||||
if (dimension == 3) {
|
||||
os << " " << cell_size[2];
|
||||
} else {
|
||||
os << " " << 0.0;
|
||||
}
|
||||
os << "\n";
|
||||
|
||||
os << "\nCELL_DATA " << num_cells << '\n';
|
||||
for (DataMap::const_iterator dit = data.begin(); dit != data.end(); ++dit) {
|
||||
std::string name = dit->first;
|
||||
os << "SCALARS " << name << " float" << '\n';
|
||||
os << "LOOKUP_TABLE " << name << "_table " << '\n';
|
||||
const std::vector<double>& field = *(dit->second);
|
||||
// We always print only the first data item for every
|
||||
// cell, using 'stride'.
|
||||
// This is a hack to get water saturation nicely.
|
||||
// \TODO: Extend to properly printing vector data.
|
||||
const int stride = field.size()/num_cells;
|
||||
const int num_per_line = 5;
|
||||
for (int c = 0; c < num_cells; ++c) {
|
||||
os << field[stride*c] << ' ';
|
||||
if (c % num_per_line == num_per_line - 1
|
||||
|| c == num_cells - 1) {
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::map<std::string, std::string> PMap;
|
||||
|
||||
|
||||
struct Tag
|
||||
{
|
||||
Tag(const std::string& tag, const PMap& props, std::ostream& os)
|
||||
: name_(tag), os_(os)
|
||||
{
|
||||
indent(os);
|
||||
os << "<" << tag;
|
||||
for (PMap::const_iterator it = props.begin(); it != props.end(); ++it) {
|
||||
os << " " << it->first << "=\"" << it->second << "\"";
|
||||
}
|
||||
os << ">\n";
|
||||
++indent_;
|
||||
}
|
||||
Tag(const std::string& tag, std::ostream& os)
|
||||
: name_(tag), os_(os)
|
||||
{
|
||||
indent(os);
|
||||
os << "<" << tag << ">\n";
|
||||
++indent_;
|
||||
}
|
||||
~Tag()
|
||||
{
|
||||
--indent_;
|
||||
indent(os_);
|
||||
os_ << "</" << name_ << ">\n";
|
||||
}
|
||||
static void indent(std::ostream& os)
|
||||
{
|
||||
for (int i = 0; i < indent_; ++i) {
|
||||
os << " ";
|
||||
}
|
||||
}
|
||||
private:
|
||||
static int indent_;
|
||||
std::string name_;
|
||||
std::ostream& os_;
|
||||
};
|
||||
|
||||
int Tag::indent_ = 0;
|
||||
|
||||
|
||||
void writeVtkData(const UnstructuredGrid& grid,
|
||||
const DataMap& data,
|
||||
std::ostream& os)
|
||||
{
|
||||
if (grid.dimensions != 3) {
|
||||
OPM_THROW(std::runtime_error, "Vtk output for 3d grids only");
|
||||
}
|
||||
os.precision(12);
|
||||
os << "<?xml version=\"1.0\"?>\n";
|
||||
PMap pm;
|
||||
pm["type"] = "UnstructuredGrid";
|
||||
Tag vtkfiletag("VTKFile", pm, os);
|
||||
Tag ugtag("UnstructuredGrid", os);
|
||||
int num_pts = grid.number_of_nodes;
|
||||
int num_cells = grid.number_of_cells;
|
||||
pm.clear();
|
||||
pm["NumberOfPoints"] = boost::lexical_cast<std::string>(num_pts);
|
||||
pm["NumberOfCells"] = boost::lexical_cast<std::string>(num_cells);
|
||||
Tag piecetag("Piece", pm, os);
|
||||
{
|
||||
Tag pointstag("Points", os);
|
||||
pm.clear();
|
||||
pm["type"] = "Float64";
|
||||
pm["Name"] = "Coordinates";
|
||||
pm["NumberOfComponents"] = "3";
|
||||
pm["format"] = "ascii";
|
||||
Tag datag("DataArray", pm, os);
|
||||
for (int i = 0; i < num_pts; ++i) {
|
||||
Tag::indent(os);
|
||||
os << grid.node_coordinates[3*i + 0] << ' '
|
||||
<< grid.node_coordinates[3*i + 1] << ' '
|
||||
<< grid.node_coordinates[3*i + 2] << '\n';
|
||||
}
|
||||
}
|
||||
{
|
||||
Tag cellstag("Cells", os);
|
||||
pm.clear();
|
||||
pm["type"] = "Int32";
|
||||
pm["NumberOfComponents"] = "1";
|
||||
pm["format"] = "ascii";
|
||||
std::vector<int> cell_numpts;
|
||||
cell_numpts.reserve(num_cells);
|
||||
{
|
||||
pm["Name"] = "connectivity";
|
||||
Tag t("DataArray", pm, os);
|
||||
int hf = 0;
|
||||
for (int c = 0; c < num_cells; ++c) {
|
||||
std::set<int> cell_pts;
|
||||
for (; hf < grid.cell_facepos[c+1]; ++hf) {
|
||||
int f = grid.cell_faces[hf];
|
||||
const int* fnbeg = grid.face_nodes + grid.face_nodepos[f];
|
||||
const int* fnend = grid.face_nodes + grid.face_nodepos[f+1];
|
||||
cell_pts.insert(fnbeg, fnend);
|
||||
}
|
||||
cell_numpts.push_back(cell_pts.size());
|
||||
Tag::indent(os);
|
||||
std::copy(cell_pts.begin(), cell_pts.end(),
|
||||
std::ostream_iterator<int>(os, " "));
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
{
|
||||
pm["Name"] = "offsets";
|
||||
Tag t("DataArray", pm, os);
|
||||
int offset = 0;
|
||||
const int num_per_line = 10;
|
||||
for (int c = 0; c < num_cells; ++c) {
|
||||
if (c % num_per_line == 0) {
|
||||
Tag::indent(os);
|
||||
}
|
||||
offset += cell_numpts[c];
|
||||
os << offset << ' ';
|
||||
if (c % num_per_line == num_per_line - 1
|
||||
|| c == num_cells - 1) {
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<int> cell_foffsets;
|
||||
cell_foffsets.reserve(num_cells);
|
||||
{
|
||||
pm["Name"] = "faces";
|
||||
Tag t("DataArray", pm, os);
|
||||
const int* fp = grid.cell_facepos;
|
||||
int offset = 0;
|
||||
for (int c = 0; c < num_cells; ++c) {
|
||||
Tag::indent(os);
|
||||
os << fp[c+1] - fp[c] << '\n';
|
||||
++offset;
|
||||
for (int hf = fp[c]; hf < fp[c+1]; ++hf) {
|
||||
int f = grid.cell_faces[hf];
|
||||
const int* np = grid.face_nodepos;
|
||||
int f_num_pts = np[f+1] - np[f];
|
||||
Tag::indent(os);
|
||||
os << f_num_pts << ' ';
|
||||
++offset;
|
||||
std::copy(grid.face_nodes + np[f],
|
||||
grid.face_nodes + np[f+1],
|
||||
std::ostream_iterator<int>(os, " "));
|
||||
os << '\n';
|
||||
offset += f_num_pts;
|
||||
}
|
||||
cell_foffsets.push_back(offset);
|
||||
}
|
||||
}
|
||||
{
|
||||
pm["Name"] = "faceoffsets";
|
||||
Tag t("DataArray", pm, os);
|
||||
const int num_per_line = 10;
|
||||
for (int c = 0; c < num_cells; ++c) {
|
||||
if (c % num_per_line == 0) {
|
||||
Tag::indent(os);
|
||||
}
|
||||
os << cell_foffsets[c] << ' ';
|
||||
if (c % num_per_line == num_per_line - 1
|
||||
|| c == num_cells - 1) {
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
pm["type"] = "UInt8";
|
||||
pm["Name"] = "types";
|
||||
Tag t("DataArray", pm, os);
|
||||
const int num_per_line = 10;
|
||||
for (int c = 0; c < num_cells; ++c) {
|
||||
if (c % num_per_line == 0) {
|
||||
Tag::indent(os);
|
||||
}
|
||||
os << "42 ";
|
||||
if (c % num_per_line == num_per_line - 1
|
||||
|| c == num_cells - 1) {
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
pm.clear();
|
||||
if (data.find("saturation") != data.end()) {
|
||||
pm["Scalars"] = "saturation";
|
||||
} else if (data.find("pressure") != data.end()) {
|
||||
pm["Scalars"] = "pressure";
|
||||
}
|
||||
Tag celldatatag("CellData", pm, os);
|
||||
pm.clear();
|
||||
pm["NumberOfComponents"] = "1";
|
||||
pm["format"] = "ascii";
|
||||
pm["type"] = "Float64";
|
||||
for (DataMap::const_iterator dit = data.begin(); dit != data.end(); ++dit) {
|
||||
pm["Name"] = dit->first;
|
||||
const std::vector<double>& field = *(dit->second);
|
||||
const int num_comps = field.size()/grid.number_of_cells;
|
||||
pm["NumberOfComponents"] = boost::lexical_cast<std::string>(num_comps);
|
||||
Tag ptag("DataArray", pm, os);
|
||||
const int num_per_line = num_comps == 1 ? 5 : num_comps;
|
||||
for (int item = 0; item < num_cells*num_comps; ++item) {
|
||||
if (item % num_per_line == 0) {
|
||||
Tag::indent(os);
|
||||
}
|
||||
double value = field[item];
|
||||
if (std::fabs(value) < std::numeric_limits<double>::min()) {
|
||||
// Avoiding denormal numbers to work around
|
||||
// bug in Paraview.
|
||||
value = 0.0;
|
||||
}
|
||||
os << value << ' ';
|
||||
if (item % num_per_line == num_per_line - 1
|
||||
|| item == num_cells - 1) {
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WRITEVTKDATA_HEADER_INCLUDED
|
||||
#define OPM_WRITEVTKDATA_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iosfwd>
|
||||
#include <opm/core/utility/DataMap.hpp>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// Vtk output for cartesian grids.
|
||||
void writeVtkData(const std::array<int, 3>& dims,
|
||||
const std::array<double, 3>& cell_size,
|
||||
const DataMap& data,
|
||||
std::ostream& os);
|
||||
|
||||
/// Vtk output for general grids.
|
||||
void writeVtkData(const UnstructuredGrid& grid,
|
||||
const DataMap& data,
|
||||
std::ostream& os);
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_WRITEVTKDATA_HEADER_INCLUDED
|
|
@ -17,7 +17,7 @@
|
|||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
|
@ -59,10 +59,21 @@ namespace Opm
|
|||
|
||||
|
||||
|
||||
LinearSolverFactory::LinearSolverFactory(const parameter::ParameterGroup& param)
|
||||
LinearSolverFactory::LinearSolverFactory(const ParameterGroup& param)
|
||||
{
|
||||
#if HAVE_SUITESPARSE_UMFPACK_H
|
||||
std::string default_solver = "umfpack";
|
||||
#elif HAVE_DUNE_ISTL
|
||||
std::string default_solver = "istl";
|
||||
#elif HAVE_PETSC
|
||||
std::string default_solver = "petsc";
|
||||
#else
|
||||
std::string default_solver = "no_solver_available";
|
||||
OPM_THROW(std::runtime_error, "No linear solver available, you must have UMFPACK , dune-istl or Petsc installed to use LinearSolverFactory.");
|
||||
#endif
|
||||
|
||||
const std::string ls =
|
||||
param.getDefault<std::string>("linsolver", "umfpack");
|
||||
param.getDefault("linsolver", default_solver);
|
||||
|
||||
if (ls == "umfpack") {
|
||||
#if HAVE_SUITESPARSE_UMFPACK_H
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
namespace Opm
|
||||
{
|
||||
|
||||
namespace parameter { class ParameterGroup; }
|
||||
class ParameterGroup;
|
||||
|
||||
|
||||
/// Concrete class encapsulating any available linear solver.
|
||||
|
@ -55,7 +55,7 @@ namespace Opm
|
|||
/// Any further parameters are passed on to the constructors
|
||||
/// of the actual solver used, see LinearSolverUmfpack,
|
||||
/// LinearSolverIstl and LinearSolverPetsc for details.
|
||||
LinearSolverFactory(const parameter::ParameterGroup& param);
|
||||
LinearSolverFactory(const ParameterGroup& param);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~LinearSolverFactory();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
|
@ -47,9 +47,7 @@
|
|||
#include <dune/istl/paamg/kamg.hh>
|
||||
#include <dune/istl/paamg/pinfo.hh>
|
||||
|
||||
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
|
||||
#include <dune/istl/paamg/fastamg.hh>
|
||||
#endif
|
||||
|
||||
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
|
||||
|
||||
|
@ -77,7 +75,6 @@ namespace Opm
|
|||
solveCG_AMG(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity,
|
||||
double prolongateFactor, int smoothsteps);
|
||||
|
||||
#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
|
||||
template<class O, class S, class C>
|
||||
LinearSolverInterface::LinearSolverReport
|
||||
solveKAMG(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity,
|
||||
|
@ -87,7 +84,6 @@ namespace Opm
|
|||
LinearSolverInterface::LinearSolverReport
|
||||
solveFastAMG(O& A, Vector& x, Vector& b, S& sp, const C& comm, double tolerance, int maxit, int verbosity,
|
||||
double prolongateFactor);
|
||||
#endif
|
||||
|
||||
template<class O, class S, class C>
|
||||
LinearSolverInterface::LinearSolverReport
|
||||
|
@ -111,7 +107,7 @@ namespace Opm
|
|||
|
||||
|
||||
|
||||
LinearSolverIstl::LinearSolverIstl(const parameter::ParameterGroup& param)
|
||||
LinearSolverIstl::LinearSolverIstl(const ParameterGroup& param)
|
||||
: linsolver_residual_tolerance_(1e-8),
|
||||
linsolver_verbosity_(0),
|
||||
linsolver_type_(CG_AMG),
|
||||
|
@ -223,16 +219,10 @@ namespace Opm
|
|||
linsolver_prolongate_factor_, linsolver_smooth_steps_);
|
||||
break;
|
||||
case KAMG:
|
||||
#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
|
||||
res = solveKAMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_,
|
||||
linsolver_prolongate_factor_, linsolver_smooth_steps_);
|
||||
#else
|
||||
throw std::runtime_error("KAMG not supported with this version of DUNE");
|
||||
#endif
|
||||
break;
|
||||
case FastAMG:
|
||||
#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
|
||||
|
||||
#if HAVE_MPI
|
||||
if(std::is_same<C,Dune::OwnerOverlapCopyCommunication<int,int> >::value)
|
||||
{
|
||||
|
@ -242,12 +232,6 @@ namespace Opm
|
|||
|
||||
res = solveFastAMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_,
|
||||
linsolver_prolongate_factor_);
|
||||
#else
|
||||
if(linsolver_verbosity_)
|
||||
std::cerr<<"Fast AMG is not available; falling back to CG preconditioned with the normal one"<<std::endl;
|
||||
res = solveCG_AMG(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_,
|
||||
linsolver_prolongate_factor_, linsolver_smooth_steps_);
|
||||
#endif
|
||||
break;
|
||||
case BiCGStab_ILU0:
|
||||
res = solveBiCGStab_ILU0(opA, x, b, sp, comm, linsolver_residual_tolerance_, maxit, linsolver_verbosity_);
|
||||
|
@ -409,7 +393,6 @@ namespace Opm
|
|||
}
|
||||
|
||||
|
||||
#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
|
||||
template<class O, class S, class C>
|
||||
LinearSolverInterface::LinearSolverReport
|
||||
solveKAMG(O& opA, Vector& x, Vector& b, S& /* sp */, const C& /* comm */, double tolerance, int maxit, int verbosity,
|
||||
|
@ -466,8 +449,8 @@ namespace Opm
|
|||
double linsolver_prolongate_factor)
|
||||
{
|
||||
// Solve with AMG solver.
|
||||
typedef Dune::MatrixAdapter<typename O::matrix_type, Vector, Vector> Operator;
|
||||
Operator sOpA(opA.getmat());
|
||||
typedef Dune::MatrixAdapter<typename O::matrix_type, Vector, Vector> AMGOperator;
|
||||
AMGOperator sOpA(opA.getmat());
|
||||
|
||||
#if FIRST_DIAGONAL
|
||||
typedef Dune::Amg::FirstDiagonal CouplingMetric;
|
||||
|
@ -482,7 +465,7 @@ namespace Opm
|
|||
#endif
|
||||
|
||||
typedef Dune::Amg::CoarsenCriterion<CriterionBase> Criterion;
|
||||
typedef Dune::Amg::FastAMG<Operator,Vector> Precond;
|
||||
typedef Dune::Amg::FastAMG<AMGOperator, Vector> Precond;
|
||||
|
||||
// Construct preconditioner.
|
||||
Criterion criterion;
|
||||
|
@ -509,7 +492,6 @@ namespace Opm
|
|||
res.residual_reduction = result.reduction;
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class O, class S, class C>
|
||||
LinearSolverInterface::LinearSolverReport
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace Opm
|
|||
/// Construct from parameters
|
||||
/// Accepted parameters are, with defaults, listed in the
|
||||
/// default constructor.
|
||||
LinearSolverIstl(const parameter::ParameterGroup& param);
|
||||
LinearSolverIstl(const ParameterGroup& param);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~LinearSolverIstl();
|
||||
|
|
|
@ -19,11 +19,16 @@
|
|||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_PETSC
|
||||
|
||||
#include <cstring>
|
||||
#include <opm/core/linalg/LinearSolverPetsc.hpp>
|
||||
#include <unordered_map>
|
||||
#define PETSC_CLANGUAGE_CXX 1 //enable CHKERRXX macro.
|
||||
#include <opm/common/utility/platform_dependent/disable_warnings.h>
|
||||
#include <petsc.h>
|
||||
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
namespace Opm
|
||||
|
@ -148,7 +153,6 @@ namespace{
|
|||
VecDestroy( &b );
|
||||
MatDestroy( &A );
|
||||
KSPDestroy( &ksp );
|
||||
PCDestroy( &preconditioner );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -181,11 +185,11 @@ namespace{
|
|||
VecRestoreArray( v, &vec );
|
||||
}
|
||||
|
||||
Mat to_petsc_mat( const int size, const int nonzeros,
|
||||
Mat to_petsc_mat( const int size, const int /* nonzeros */,
|
||||
const int* ia, const int* ja, const double* sa ) {
|
||||
|
||||
Mat A;
|
||||
auto err = MatCreateSeqAIJWithArrays( PETSC_COMM_WORLD, size, size, (int*)ia, (int*)ja, (double*)sa, &A );
|
||||
auto err = MatCreateSeqAIJWithArrays( PETSC_COMM_WORLD, size, size, const_cast<int*>(ia), const_cast<int*>(ja), (double*)sa, &A );
|
||||
CHKERRXX( err );
|
||||
return A;
|
||||
}
|
||||
|
@ -197,7 +201,12 @@ namespace{
|
|||
PetscReal residual;
|
||||
KSPConvergedReason reason;
|
||||
|
||||
#if PETSC_VERSION_MAJOR <= 3 && PETSC_VERSION_MINOR < 5
|
||||
KSPSetOperators( t.ksp, t.A, t.A, DIFFERENT_NONZERO_PATTERN );
|
||||
#else
|
||||
KSPSetOperators( t.ksp, t.A, t.A );
|
||||
KSPSetReusePreconditioner(t.ksp, PETSC_FALSE);
|
||||
#endif
|
||||
KSPGetPC( t.ksp, &t.preconditioner );
|
||||
auto err = KSPSetType( t.ksp, method );
|
||||
CHKERRXX( err );
|
||||
|
@ -216,13 +225,13 @@ namespace{
|
|||
if( ksp_view )
|
||||
KSPView( t.ksp, PETSC_VIEWER_STDOUT_WORLD );
|
||||
|
||||
err = PetscPrintf( PETSC_COMM_WORLD, "KSP Iterations %D, Final Residual %G\n", its, residual );
|
||||
err = PetscPrintf( PETSC_COMM_WORLD, "KSP Iterations %D, Final Residual %g\n", its, (double)residual );
|
||||
CHKERRXX( err );
|
||||
}
|
||||
|
||||
} // anonymous namespace.
|
||||
|
||||
LinearSolverPetsc::LinearSolverPetsc(const parameter::ParameterGroup& param)
|
||||
LinearSolverPetsc::LinearSolverPetsc(const ParameterGroup& param)
|
||||
: ksp_type_( param.getDefault( std::string( "ksp_type" ), std::string( "gmres" ) ) )
|
||||
, pc_type_( param.getDefault( std::string( "pc_type" ), std::string( "sor" ) ) )
|
||||
, ksp_view_( param.getDefault( std::string( "ksp_view" ), int( false ) ) )
|
||||
|
@ -281,3 +290,4 @@ namespace{
|
|||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // HAVE_PETSC
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
|
||||
#ifndef OPM_LINEARSOLVERPETSC_HEADER_INCLUDED
|
||||
#define OPM_LINEARSOLVERPETSC_HEADER_INCLUDED
|
||||
|
||||
#if !HAVE_PETSC
|
||||
#error "LinearSolverPetsc.hpp included, but the PETSc libraries are not available!"
|
||||
#endif
|
||||
|
||||
#include <opm/core/linalg/LinearSolverInterface.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
#include <string>
|
||||
|
@ -42,7 +47,7 @@ namespace Opm
|
|||
/// Construct from parameters
|
||||
/// Accepted parameters are, with defaults, listed in the
|
||||
/// default constructor.
|
||||
LinearSolverPetsc(const parameter::ParameterGroup& param);
|
||||
LinearSolverPetsc(const ParameterGroup& param);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~LinearSolverPetsc();
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
#include <boost/any.hpp>
|
||||
#include <exception>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
|
||||
#if HAVE_MPI && HAVE_DUNE_ISTL
|
||||
|
||||
#include <opm/common/utility/platform_dependent/disable_warnings.h>
|
||||
|
@ -37,12 +43,6 @@
|
|||
#include <dune/common/enumset.hh>
|
||||
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
namespace
|
||||
|
@ -144,8 +144,9 @@ public:
|
|||
void copyOwnerToAll (const T& source, T& dest) const
|
||||
{
|
||||
typedef Dune::Combine<Dune::EnumItem<Dune::OwnerOverlapCopyAttributeSet::AttributeSet,Dune::OwnerOverlapCopyAttributeSet::owner>,Dune::EnumItem<Dune::OwnerOverlapCopyAttributeSet::AttributeSet,Dune::OwnerOverlapCopyAttributeSet::overlap>,Dune::OwnerOverlapCopyAttributeSet::AttributeSet> OwnerOverlapSet;
|
||||
typedef Dune::EnumItem<Dune::OwnerOverlapCopyAttributeSet::AttributeSet,Dune::OwnerOverlapCopyAttributeSet::owner> OwnerSet;
|
||||
typedef Dune::Combine<OwnerOverlapSet, Dune::EnumItem<Dune::OwnerOverlapCopyAttributeSet::AttributeSet,Dune::OwnerOverlapCopyAttributeSet::copy>,Dune::OwnerOverlapCopyAttributeSet::AttributeSet> AllSet;
|
||||
OwnerOverlapSet sourceFlags;
|
||||
OwnerSet sourceFlags;
|
||||
AllSet destFlags;
|
||||
Dune::Interface interface(communicator_);
|
||||
if( !remoteIndices_->isSynced() )
|
||||
|
@ -159,7 +160,7 @@ public:
|
|||
communicator.free();
|
||||
}
|
||||
template<class T>
|
||||
void updateOwnerMask(const T& container) const
|
||||
const std::vector<double>& updateOwnerMask(const T& container) const
|
||||
{
|
||||
if( ! indexSet_ )
|
||||
{
|
||||
|
@ -176,13 +177,26 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
return ownerMask_;
|
||||
}
|
||||
|
||||
/// \brief Get the owner Mask.
|
||||
///
|
||||
/// \return A vector with entries 0, and 1. 0 marks an index that we cannot
|
||||
/// compute correct results for. 1 marks an index that this process
|
||||
/// is responsible for and computes correct results in parallel.
|
||||
const std::vector<double>& getOwnerMask() const
|
||||
{
|
||||
return ownerMask_;
|
||||
}
|
||||
|
||||
/// \brief Compute one or more global reductions.
|
||||
///
|
||||
/// This function can either be used with a container, an operator, and an initial value
|
||||
/// to compute a reduction. Or with tuples of them to compute multiple reductions with only
|
||||
/// one global communication.
|
||||
/// The possible functors needed can be constructed with Opm::Reduction::makeGlobalMaxFunctor(),
|
||||
/// Opm::Reduction::makeLInfinityNormFunctor(),
|
||||
/// Opm::Reduction::makeGlobalMinFunctor(), and
|
||||
/// Opm::Reduction::makeGlobalSumFunctor().
|
||||
/// \tparam type of the container or the tuple of containers.
|
||||
|
@ -572,6 +586,45 @@ private:
|
|||
(std::pointer_to_binary_function<const T&,const T&,const T&>
|
||||
((const T&(*)(const T&, const T&))std::max<T>));
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
/// \brief Computes the maximum of the absolute values of two values.
|
||||
template<typename T, typename Enable = void>
|
||||
struct MaxAbsFunctor
|
||||
{
|
||||
using result_type = T;
|
||||
result_type operator()(const T& t1,
|
||||
const T& t2)
|
||||
{
|
||||
return std::max(std::abs(t1), std::abs(t2));
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for unsigned integers. They need their own
|
||||
// version since abs(x) is ambiguous (as well as somewhat
|
||||
// meaningless).
|
||||
template<typename T>
|
||||
struct MaxAbsFunctor<T, typename std::enable_if<std::is_unsigned<T>::value>::type>
|
||||
{
|
||||
using result_type = T;
|
||||
result_type operator()(const T& t1,
|
||||
const T& t2)
|
||||
{
|
||||
return std::max(t1, t2);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// \brief Create a functor for computing a global L infinity norm
|
||||
///
|
||||
/// To be used with ParallelISTLInformation::computeReduction.
|
||||
template<class T>
|
||||
MaskIDOperator<detail::MaxAbsFunctor<T> >
|
||||
makeLInfinityNormFunctor()
|
||||
{
|
||||
return MaskIDOperator<detail::MaxAbsFunctor<T> >();
|
||||
}
|
||||
/// \brief Create a functor for computing a global minimum.
|
||||
///
|
||||
/// To be used with ParallelISTLInformation::computeReduction.
|
||||
|
@ -610,6 +663,29 @@ inline void extractParallelGridInformationToISTL(boost::any& anyComm, const Unst
|
|||
{
|
||||
(void)anyComm; (void)grid;
|
||||
}
|
||||
|
||||
/// \brief Accumulates entries masked with 1.
|
||||
/// \param container The container whose values to accumulate.
|
||||
/// \param maskContainer null pointer or a pointer to a container
|
||||
/// with entries 0 and 1. Only values at indices with a 1 stored
|
||||
/// will be accumulated. If null then all values will be accumulated
|
||||
/// \return the summ of all entries that should be represented.
|
||||
template<class T1>
|
||||
auto
|
||||
accumulateMaskedValues(const T1& container, const std::vector<double>* maskContainer)
|
||||
-> decltype(container[0]*(*maskContainer)[0])
|
||||
{
|
||||
decltype(container[0]*(*maskContainer)[0]) initial = 0;
|
||||
|
||||
if( maskContainer )
|
||||
{
|
||||
return std::inner_product(container.begin(), container.end(), maskContainer->begin(),
|
||||
initial);
|
||||
}else
|
||||
{
|
||||
return std::accumulate(container.begin(), container.end(), initial);
|
||||
}
|
||||
}
|
||||
} // end namespace Opm
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_BLAS_LAPACK_HEADER_INCLUDED
|
||||
#define OPM_BLAS_LAPACK_HEADER_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MATLAB_MEX_FILE) && MATLAB_MEX_FILE
|
||||
#include <mex.h>
|
||||
#undef MAT_SIZE_T
|
||||
#define MAT_SIZE_T mwSignedIndex
|
||||
#endif
|
||||
|
||||
#ifndef MAT_SIZE_T
|
||||
#define MAT_SIZE_T int
|
||||
#endif
|
||||
|
||||
|
||||
/* C <- a1*op(A)*op(B) + a2*C where op(X) in {X, X.'} */
|
||||
void dgemm_(const char *transA , const char *transB ,
|
||||
const MAT_SIZE_T* m, const MAT_SIZE_T* n , const MAT_SIZE_T* k ,
|
||||
const double* a1, const double* A , const MAT_SIZE_T* ldA,
|
||||
const double* B, const MAT_SIZE_T* ldB,
|
||||
const double* a2, double* C , const MAT_SIZE_T* ldC);
|
||||
|
||||
|
||||
/* C <- a1*A*A' + a2*C *or* C <- a1*A'*A + a2*C */
|
||||
void dsyrk_(const char *uplo, const char *trans,
|
||||
const MAT_SIZE_T *n , const MAT_SIZE_T *k ,
|
||||
const double *a1 , const double *A , const MAT_SIZE_T *ldA,
|
||||
const double *a2 , double *C , const MAT_SIZE_T *ldC);
|
||||
|
||||
|
||||
void dgeqrf_(const MAT_SIZE_T *m , const MAT_SIZE_T *n ,
|
||||
double *A , const MAT_SIZE_T *ld ,
|
||||
double *tau , double *work,
|
||||
const MAT_SIZE_T *lwork, MAT_SIZE_T *info);
|
||||
|
||||
|
||||
void dorgqr_(const MAT_SIZE_T *m , const MAT_SIZE_T *n , const MAT_SIZE_T *k ,
|
||||
double *A , const MAT_SIZE_T *ld , const double *tau,
|
||||
double *work, const MAT_SIZE_T *lwork, MAT_SIZE_T *info);
|
||||
|
||||
/* A <- LU(A) */
|
||||
void dgetrf_(const MAT_SIZE_T *m , const MAT_SIZE_T *n ,
|
||||
double *A , const MAT_SIZE_T *ld,
|
||||
MAT_SIZE_T *ipiv, MAT_SIZE_T *info);
|
||||
|
||||
/* B <- A \ B, when A is LU(A) from dgetrf() */
|
||||
void dgetrs_(const char *trans, const MAT_SIZE_T *n,
|
||||
const MAT_SIZE_T *nrhs ,
|
||||
const double *A , const MAT_SIZE_T *lda,
|
||||
const MAT_SIZE_T *ipiv , double *B,
|
||||
const MAT_SIZE_T *ldb , MAT_SIZE_T *info);
|
||||
|
||||
/* B <- A \ B, tridiagonal A with bands DL, D, DU */
|
||||
void dgtsv_(const MAT_SIZE_T *n ,
|
||||
const MAT_SIZE_T *nrhs ,
|
||||
double *DL ,
|
||||
double *D ,
|
||||
double *DU ,
|
||||
double *B ,
|
||||
const MAT_SIZE_T *ldb ,
|
||||
MAT_SIZE_T *info);
|
||||
|
||||
/* B <- A \ B, band matrix A stored in AB with kl subdiagonals, ku superdiagonals */
|
||||
void dgbsv_(const MAT_SIZE_T *n ,
|
||||
const MAT_SIZE_T *kl ,
|
||||
const MAT_SIZE_T *ku ,
|
||||
const MAT_SIZE_T *nrhs ,
|
||||
double *AB ,
|
||||
const MAT_SIZE_T *ldab ,
|
||||
MAT_SIZE_T *ipiv ,
|
||||
double *B ,
|
||||
const MAT_SIZE_T *ldb ,
|
||||
MAT_SIZE_T *info);
|
||||
|
||||
/* B <- A \ B, general solver */
|
||||
void dgesv_(const MAT_SIZE_T *n,
|
||||
const MAT_SIZE_T *nrhs ,
|
||||
double *A ,
|
||||
const MAT_SIZE_T *lda ,
|
||||
MAT_SIZE_T *piv ,
|
||||
double *B ,
|
||||
const MAT_SIZE_T *ldb ,
|
||||
MAT_SIZE_T *info);
|
||||
|
||||
/* A <- chol(A) */
|
||||
void dpotrf_(const char *uplo, const MAT_SIZE_T *n,
|
||||
double *A , const MAT_SIZE_T *lda,
|
||||
MAT_SIZE_T *info);
|
||||
|
||||
/* B <- (A \ (A' \ B)), when A=DPOTRF(A_orig) */
|
||||
void dpotrs_(const char *uplo, const MAT_SIZE_T *n , const MAT_SIZE_T *nrhs,
|
||||
double *A , const MAT_SIZE_T *lda,
|
||||
double *B , const MAT_SIZE_T *ldb, MAT_SIZE_T *info);
|
||||
|
||||
/* A <- chol(A), packed format. */
|
||||
void dpptrf_(const char *uplo, const MAT_SIZE_T *n,
|
||||
double *Ap , MAT_SIZE_T *info);
|
||||
|
||||
/* A <- (A \ (A' \ eye(n)) when A=DPPTRF(A_orig) (packed format). */
|
||||
void dpptri_(const char *uplo, const MAT_SIZE_T *n,
|
||||
double *Ap , MAT_SIZE_T *info);
|
||||
|
||||
/* y <- a1*op(A)*x + a2*y */
|
||||
void dgemv_(const char *trans,
|
||||
const MAT_SIZE_T *m , const MAT_SIZE_T *n,
|
||||
const double *a1 , const double *A, const MAT_SIZE_T *ldA ,
|
||||
const double *x, const MAT_SIZE_T *incX,
|
||||
const double *a2 , double *y, const MAT_SIZE_T *incY);
|
||||
|
||||
|
||||
/* y <- a*x + y */
|
||||
void daxpy_(const MAT_SIZE_T *n, const double *a,
|
||||
const double *x, const MAT_SIZE_T *incx,
|
||||
double *y, const MAT_SIZE_T *incy);
|
||||
|
||||
/* s <- x' * y */
|
||||
double ddot_(const MAT_SIZE_T *n, const double *x, const MAT_SIZE_T *incx,
|
||||
const double *y, const MAT_SIZE_T *incy);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_BLAS_LAPACK_HEADER_INCLUDED */
|
|
@ -34,6 +34,8 @@
|
|||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_UMFPACK
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -183,3 +185,15 @@ call_UMFPACK(struct CSRMatrix *A, const double *b, double *x)
|
|||
csc_deallocate(csc);
|
||||
}
|
||||
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <opm/core/linalg/call_umfpack.h>
|
||||
|
||||
void
|
||||
call_UMFPACK(struct CSRMatrix *A, const double *b, double *x)
|
||||
{
|
||||
/* UMFPACK is not available */
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,8 +18,10 @@
|
|||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <opm/core/pressure/IncompTpfa.hpp>
|
||||
|
||||
#include <opm/common/data/SimulationDataContainer.hpp>
|
||||
|
||||
#include <opm/core/pressure/IncompTpfa.hpp>
|
||||
#include <opm/core/props/IncompPropertiesInterface.hpp>
|
||||
#include <opm/core/props/rock/RockCompressibility.hpp>
|
||||
#include <opm/core/pressure/tpfa/ifs_tpfa.h>
|
||||
|
@ -28,7 +30,6 @@
|
|||
#include <opm/core/pressure/flow_bc.h>
|
||||
#include <opm/core/linalg/LinearSolverInterface.hpp>
|
||||
#include <opm/core/linalg/sparse_sys.h>
|
||||
#include <opm/core/simulator/TwophaseState.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/utility/miscUtilities.hpp>
|
||||
|
@ -155,7 +156,7 @@ namespace Opm
|
|||
/// May throw an exception if the number of iterations
|
||||
/// exceed maxiter (set in constructor).
|
||||
void IncompTpfa::solve(const double dt,
|
||||
TwophaseState& state,
|
||||
SimulationDataContainer& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
if (rock_comp_props_ != 0 && rock_comp_props_->isActive()) {
|
||||
|
@ -169,7 +170,7 @@ namespace Opm
|
|||
|
||||
// Solve with no rock compressibility (linear eqn).
|
||||
void IncompTpfa::solveIncomp(const double dt,
|
||||
TwophaseState& state,
|
||||
SimulationDataContainer& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
// Set up properties.
|
||||
|
@ -207,7 +208,7 @@ namespace Opm
|
|||
|
||||
// Solve with rock compressibility (nonlinear eqn).
|
||||
void IncompTpfa::solveRockComp(const double dt,
|
||||
TwophaseState& state,
|
||||
SimulationDataContainer& state,
|
||||
WellState& well_state)
|
||||
{
|
||||
// This function is identical to CompressibleTpfa::solve().
|
||||
|
@ -321,7 +322,7 @@ namespace Opm
|
|||
|
||||
/// Compute per-solve dynamic properties.
|
||||
void IncompTpfa::computePerSolveDynamicData(const double /*dt*/,
|
||||
const TwophaseState& state,
|
||||
const SimulationDataContainer& state,
|
||||
const WellState& /*well_state*/)
|
||||
{
|
||||
// Computed here:
|
||||
|
@ -369,7 +370,7 @@ namespace Opm
|
|||
|
||||
/// Compute per-iteration dynamic properties.
|
||||
void IncompTpfa::computePerIterationDynamicData(const double /*dt*/,
|
||||
const TwophaseState& state,
|
||||
const SimulationDataContainer& state,
|
||||
const WellState& well_state)
|
||||
{
|
||||
// These are the variables that get computed by this function:
|
||||
|
@ -396,7 +397,7 @@ namespace Opm
|
|||
|
||||
/// Compute the residual in h_->b and Jacobian in h_->A.
|
||||
void IncompTpfa::assemble(const double dt,
|
||||
const TwophaseState& state,
|
||||
const SimulationDataContainer& state,
|
||||
const WellState& /*well_state*/)
|
||||
{
|
||||
const double* pressures = wells_ ? &pressures_[0] : &state.pressure()[0];
|
||||
|
@ -462,7 +463,7 @@ namespace Opm
|
|||
|
||||
|
||||
/// Compute the output.
|
||||
void IncompTpfa::computeResults(TwophaseState& state,
|
||||
void IncompTpfa::computeResults(SimulationDataContainer& state,
|
||||
WellState& well_state) const
|
||||
{
|
||||
// Make sure h_ contains the direct-solution matrix
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#ifndef OPM_INCOMPTPFA_HEADER_INCLUDED
|
||||
#define OPM_INCOMPTPFA_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/pressure/tpfa/ifs_tpfa.h>
|
||||
#include <vector>
|
||||
|
||||
|
@ -34,8 +33,9 @@ namespace Opm
|
|||
class IncompPropertiesInterface;
|
||||
class RockCompressibility;
|
||||
class LinearSolverInterface;
|
||||
class TwophaseState;
|
||||
class WellState;
|
||||
class SimulationDataContainer;
|
||||
|
||||
|
||||
/// Encapsulating a tpfa pressure solver for the incompressible-fluid case.
|
||||
/// Supports gravity, wells controlled by bhp or reservoir rates,
|
||||
|
@ -112,7 +112,7 @@ namespace Opm
|
|||
/// May throw an exception if the number of iterations
|
||||
/// exceed maxiter (set in constructor).
|
||||
void solve(const double dt,
|
||||
TwophaseState& state,
|
||||
SimulationDataContainer& state,
|
||||
WellState& well_state);
|
||||
|
||||
|
||||
|
@ -122,28 +122,28 @@ namespace Opm
|
|||
protected:
|
||||
// Solve with no rock compressibility (linear eqn).
|
||||
void solveIncomp(const double dt,
|
||||
TwophaseState& state,
|
||||
SimulationDataContainer& state,
|
||||
WellState& well_state);
|
||||
// Solve with rock compressibility (nonlinear eqn).
|
||||
void solveRockComp(const double dt,
|
||||
TwophaseState& state,
|
||||
SimulationDataContainer& state,
|
||||
WellState& well_state);
|
||||
private:
|
||||
// Helper functions.
|
||||
void computeStaticData();
|
||||
virtual void computePerSolveDynamicData(const double dt,
|
||||
const TwophaseState& state,
|
||||
const SimulationDataContainer& state,
|
||||
const WellState& well_state);
|
||||
void computePerIterationDynamicData(const double dt,
|
||||
const TwophaseState& state,
|
||||
const SimulationDataContainer& state,
|
||||
const WellState& well_state);
|
||||
void assemble(const double dt,
|
||||
const TwophaseState& state,
|
||||
const SimulationDataContainer& state,
|
||||
const WellState& well_state);
|
||||
void solveIncrement();
|
||||
double residualNorm() const;
|
||||
double incrementNorm() const;
|
||||
void computeResults(TwophaseState& state,
|
||||
void computeResults(SimulationDataContainer& state,
|
||||
WellState& well_state) const;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opm/core/pressure/fsh.h>
|
||||
#include <opm/core/pressure/fsh_common_impl.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys_global.h>
|
||||
|
||||
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
cfsh_assemble_grid(struct FlowBoundaryConditions *bc,
|
||||
const double *Binv,
|
||||
const double *gpress,
|
||||
const double *src,
|
||||
struct fsh_data *h)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, n, nc, p1, p2;
|
||||
int npp;
|
||||
int *pgconn, *gconn;
|
||||
|
||||
nc = h->pimpl->nc;
|
||||
pgconn = h->pimpl->gdof_pos;
|
||||
gconn = h->pimpl->gdof;
|
||||
|
||||
p1 = p2 = npp = 0;
|
||||
for (c = 0; c < nc; c++) {
|
||||
n = pgconn[c + 1] - pgconn[c];
|
||||
|
||||
hybsys_cellcontrib_unsymm(c, n, p1, p2, gpress, src, Binv,
|
||||
h->pimpl->sys);
|
||||
|
||||
npp += fsh_impose_bc(n, gconn + p1, bc, h->pimpl);
|
||||
|
||||
hybsys_global_assemble_cell(n, gconn + p1,
|
||||
h->pimpl->sys->S,
|
||||
h->pimpl->sys->r, h->A, h->b);
|
||||
|
||||
p1 += n;
|
||||
p2 += n * n;
|
||||
}
|
||||
|
||||
return npp;
|
||||
}
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
* Public routines follow.
|
||||
* ====================================================================== */
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Allocate and define supporting structures for assembling the global
|
||||
* system of linear equations to couple the grid (reservoir)
|
||||
* connections represented by 'G' and, if present (i.e., non-NULL),
|
||||
* the well connections represented by 'W'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct fsh_data *
|
||||
cfsh_construct(struct UnstructuredGrid *G, well_t *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int nc, ngconn_tot;
|
||||
size_t idata_sz, ddata_sz, nnu;
|
||||
struct fsh_data *new;
|
||||
|
||||
assert (G != NULL);
|
||||
|
||||
/* Allocate master structure, define system matrix sparsity */
|
||||
new = malloc(1 * sizeof *new);
|
||||
if (new != NULL) {
|
||||
new->A = hybsys_define_globconn(G, W);
|
||||
new->pimpl = NULL;
|
||||
|
||||
if (new->A == NULL) {
|
||||
fsh_destroy(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Allocate implementation structure */
|
||||
if (new != NULL) {
|
||||
fsh_count_grid_dof(G, &new->max_ngconn, &new->sum_ngconn2);
|
||||
|
||||
fsh_compute_table_sz(G, W, new->max_ngconn,
|
||||
&nnu, &idata_sz, &ddata_sz);
|
||||
|
||||
new->pimpl = fsh_impl_allocate_basic(idata_sz, ddata_sz);
|
||||
|
||||
if (new->pimpl == NULL) {
|
||||
fsh_destroy(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Allocate Schur complement contributions. Unsymmetric system. */
|
||||
if (new != NULL) {
|
||||
nc = G->number_of_cells;
|
||||
ngconn_tot = G->cell_facepos[nc];
|
||||
|
||||
fsh_define_linsys_arrays(new);
|
||||
fsh_define_impl_arrays(nc, G->number_of_faces, nnu,
|
||||
ngconn_tot, new->max_ngconn, W, new->pimpl);
|
||||
|
||||
new->pimpl->sys = hybsys_allocate_unsymm(new->max_ngconn,
|
||||
nc, ngconn_tot);
|
||||
|
||||
if (W != NULL) {
|
||||
fsh_define_cell_wells(nc, W, new->pimpl);
|
||||
|
||||
new->pimpl->wsys =
|
||||
hybsys_well_allocate_unsymm(new->max_ngconn, nc,
|
||||
new->pimpl->cwell_pos);
|
||||
}
|
||||
|
||||
if ((new->pimpl->sys == NULL) ||
|
||||
((W != NULL) && (new->pimpl->wsys == NULL))) {
|
||||
/* Failed to allocate ->sys or ->wsys (if W != NULL) */
|
||||
fsh_destroy(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (new != NULL) {
|
||||
/* All allocations succeded. Fill metadata and return. */
|
||||
new->pimpl->nc = nc;
|
||||
new->pimpl->nf = G->number_of_faces;
|
||||
new->pimpl->nw = (W != NULL) ? W->number_of_wells : 0;
|
||||
|
||||
memcpy(new->pimpl->gdof_pos,
|
||||
G->cell_facepos ,
|
||||
(nc + 1) * sizeof *new->pimpl->gdof_pos);
|
||||
|
||||
memcpy(new->pimpl->gdof ,
|
||||
G->cell_faces ,
|
||||
ngconn_tot * sizeof *new->pimpl->gdof);
|
||||
|
||||
hybsys_init(new->max_ngconn, new->pimpl->sys);
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Assemble global system of linear equations
|
||||
*
|
||||
* fsh->A * fsh->x = fsh->b
|
||||
*/
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
cfsh_assemble(struct FlowBoundaryConditions *bc,
|
||||
const double *src,
|
||||
const double *Binv,
|
||||
const double *Biv,
|
||||
const double *P,
|
||||
const double *gpress,
|
||||
well_control_t *wctrl,
|
||||
const double *WI,
|
||||
const double *BivW,
|
||||
const double *wdp,
|
||||
struct fsh_data *h)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int npp; /* Number of prescribed pressure values */
|
||||
|
||||
/* Suppress warnings about unused parameters. */
|
||||
(void) wctrl; (void) WI; (void) BivW; (void) wdp;
|
||||
|
||||
hybsys_schur_comp_unsymm(h->pimpl->nc,
|
||||
h->pimpl->gdof_pos,
|
||||
Binv, Biv, P, h->pimpl->sys);
|
||||
|
||||
fsh_map_bdry_condition(bc, h->pimpl);
|
||||
|
||||
npp = cfsh_assemble_grid(bc, Binv, gpress, src, h);
|
||||
|
||||
if (npp == 0) {
|
||||
h->A->sa[0] *= 2; /* Remove zero eigenvalue */
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opm/core/pressure/fsh.h>
|
||||
#include <opm/core/pressure/fsh_common_impl.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys_global.h>
|
||||
|
||||
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Compute cell pressures (cpress) and interface fluxes (fflux) from
|
||||
* current solution of system of linear equations, h->x. Back
|
||||
* substitution process, projected half-contact fluxes. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_press_flux(struct UnstructuredGrid *G,
|
||||
const double *Binv, const double *gpress,
|
||||
struct fsh_data *h,
|
||||
double *cpress, double *fflux,
|
||||
double *wpress, double *wflux)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, f, i;
|
||||
double s;
|
||||
|
||||
hybsys_compute_press_flux(G->number_of_cells,
|
||||
G->cell_facepos,
|
||||
G->cell_faces,
|
||||
gpress, Binv,
|
||||
h->pimpl->sys,
|
||||
h->x, cpress, h->pimpl->cflux,
|
||||
h->pimpl->work);
|
||||
|
||||
if (h->pimpl->nw > 0) {
|
||||
assert ((wpress != NULL) && (wflux != NULL));
|
||||
hybsys_compute_press_flux_well(G->number_of_cells, G->cell_facepos,
|
||||
G->number_of_faces, h->pimpl->nw,
|
||||
h->pimpl->cwell_pos, h->pimpl->cwells,
|
||||
Binv, h->pimpl->WI,
|
||||
h->pimpl->wdp, h->pimpl->sys,
|
||||
h->pimpl->wsys, h->x, cpress,
|
||||
h->pimpl->cflux, wpress, wflux,
|
||||
h->pimpl->work);
|
||||
}
|
||||
|
||||
for (f = 0; f < G->number_of_faces; f++) { fflux[f] = 0.0; }
|
||||
|
||||
i = 0;
|
||||
for (c = 0; c < G->number_of_cells; c++) {
|
||||
for (; i < G->cell_facepos[c + 1]; i++) {
|
||||
f = G->cell_faces[i];
|
||||
s = 2.0*(G->face_cells[2*f + 0] == c) - 1.0;
|
||||
|
||||
fflux[f] += s * h->pimpl->cflux[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (f = 0; f < G->number_of_faces; f++) {
|
||||
i = (G->face_cells[2*f + 0] >= 0) +
|
||||
(G->face_cells[2*f + 1] >= 0);
|
||||
|
||||
fflux[f] /= i;
|
||||
}
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_FSH_HEADER_INCLUDED
|
||||
#define OPM_FSH_HEADER_INCLUDED
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Routines and data structures to support the construction and
|
||||
* formation of hybridized pressure solvers based on Schur
|
||||
* complement reductions.
|
||||
*
|
||||
* Pressure solvers based on this strategy will be structured
|
||||
* according to the following scheme
|
||||
* -# Construct @c FSH data object suitable for the particular
|
||||
* problem using either of the functions cfsh_construct() or
|
||||
* ifsh_construct() for compressible or incompressible flow,
|
||||
* respectively
|
||||
* -# Compute static discretisation quantities, for instance
|
||||
* using functions mim_ip_simple_all() and mim_ip_compute_gpress()
|
||||
* -# For each time step or non-linear iteration:
|
||||
* -# Compute dynamic discretisation quantities incorporating
|
||||
* effects of multiple phases and non-linear feedback.
|
||||
* -# Assemble system of simultaneous linear equations using
|
||||
* functions cfsh_assemble() or ifsh_assemble()
|
||||
* -# Solve the resulting system of linear equations, available
|
||||
* in the @c A and @c b objects of the @c FSH data object,
|
||||
* using some linear solver software. The solution should
|
||||
* be placed in the @c x object of the @c FSH object.
|
||||
* -# Reconstruct derived quantities such as cell pressures and
|
||||
* interface fluxes using function fsh_press_flux().
|
||||
* Function fsh_press_flux() relies on the solution to the
|
||||
* linear system being stored in the @c x object.
|
||||
* -# Release resources using function fsh_destroy() at end of
|
||||
* simulation.
|
||||
*/
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/pressure/legacy_well.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
struct CSRMatrix;
|
||||
struct fsh_impl;
|
||||
|
||||
/**
|
||||
* Main data structure of hybridized pressure solvers based on Schur
|
||||
* complement reductions. Mainly intended to present a common view
|
||||
* of a Schur complement system of simultaneous linear equations
|
||||
* and to hold the solution of said system.
|
||||
*/
|
||||
struct fsh_data {
|
||||
/**
|
||||
* Maximum number of connections in any grid cell,
|
||||
* \f[
|
||||
* \mathit{max\_ngconn} = \max_c \{ n_c \}
|
||||
* \f]
|
||||
* in which \f$n_c\f$ denotes the number connections
|
||||
* (i.e., faces) of cell \f$c\f$.
|
||||
*/
|
||||
int max_ngconn;
|
||||
|
||||
/**
|
||||
* Sum of squared number of connections in all grid cells,
|
||||
* \f[
|
||||
* \mathit{sum\_ngconn2} = \sum_c n_c^2.
|
||||
* \f]
|
||||
*/
|
||||
size_t sum_ngconn2;
|
||||
|
||||
/* Linear system */
|
||||
struct CSRMatrix *A; /**< Coefficient matrix */
|
||||
double *b; /**< System RHS */
|
||||
double *x; /**< Solution */
|
||||
|
||||
/** Private implementational details. */
|
||||
struct fsh_impl *pimpl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Dispose of all memory associated to <CODE>FSH</CODE> object.
|
||||
*
|
||||
* @param[in,out] h <CODE>FSH</CODE> object. Following a call
|
||||
* to function fsh_destroy(), the pointer is
|
||||
* invalid.
|
||||
*/
|
||||
void
|
||||
fsh_destroy(struct fsh_data *h);
|
||||
|
||||
|
||||
/**
|
||||
* Construct compressible hybrid flow-solver data object for a
|
||||
* given grid and well pattern.
|
||||
*
|
||||
* @param[in] G Grid.
|
||||
* @param[in] W Well topology. Ignored.
|
||||
* @return Fully formed data object suitable for use in a
|
||||
* compressible pressure solver. @c NULL in case of construction
|
||||
* failure.
|
||||
*/
|
||||
struct fsh_data *
|
||||
cfsh_construct(struct UnstructuredGrid *G, well_t *W);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Form Schur-complement system of simultaneous linear equations
|
||||
* arising in compressible flow using a hybridized formulation.
|
||||
*
|
||||
* Upon returning from function cfsh_assemble(), the resulting
|
||||
* system of simultaneous linear equations is stored in
|
||||
* <CODE>h->A</CODE> and <CODE>h->b</CODE>.
|
||||
*
|
||||
* @param[in] bc Boundary conditions.
|
||||
* @param[in] src Explicit source terms.
|
||||
* @param[in] Binv Inverse of block-diagonal matrix \f$B\f$
|
||||
* Typically computed using functions
|
||||
* mim_ip_simple_all() and
|
||||
* mim_ip_mobility_update().
|
||||
* @param[in] Biv \f$B^{-1}v\f$.
|
||||
* @param[in] P Compressible accumulation term.
|
||||
* @param[in] gpress Gravity pressure.
|
||||
* @param[in] wctrl Well controls. Ignored.
|
||||
* @param[in] WI Well indices. Ignored.
|
||||
* @param[in] BivW \f$B^{-1}v\f$ for wells. Ignored.
|
||||
* @param[in] wdp Gravity pressure along well track. Ignored.
|
||||
* @param[in,out] h Data object.
|
||||
*/
|
||||
void
|
||||
cfsh_assemble(struct FlowBoundaryConditions *bc,
|
||||
const double *src,
|
||||
const double *Binv,
|
||||
const double *Biv,
|
||||
const double *P,
|
||||
const double *gpress,
|
||||
well_control_t *wctrl,
|
||||
const double *WI,
|
||||
const double *BivW,
|
||||
const double *wdp,
|
||||
struct fsh_data *h);
|
||||
|
||||
|
||||
/**
|
||||
* Construct incompressible hybrid flow-solver data object for a
|
||||
* given grid and well pattern.
|
||||
*
|
||||
* @param G Grid.
|
||||
* @param W Well topology.
|
||||
* @return Fully formed data object suitable for use in an
|
||||
* incompressible pressure solver. @c NULL in case of construction
|
||||
* failure.
|
||||
*/
|
||||
struct fsh_data *
|
||||
ifsh_construct(struct UnstructuredGrid *G, well_t *W);
|
||||
|
||||
|
||||
/**
|
||||
* Form Schur-complement system of simultaneous linear equations
|
||||
* arising in compressible flow using a hybridized formulation.
|
||||
*
|
||||
* Upon returning from function cfsh_assemble(), the resulting
|
||||
* system of simultaneous linear equations is stored in
|
||||
* <CODE>h->A</CODE> and <CODE>h->b</CODE>.
|
||||
*
|
||||
* @param[in] bc Boundary conditions.
|
||||
* @param[in] src Explicit source terms.
|
||||
* @param[in] Binv Inverse of block-diagonal matrix \f$B\f$
|
||||
* Typically computed using functions
|
||||
* mim_ip_simple_all() and
|
||||
* mim_ip_mobility_update().
|
||||
* @param[in] gpress Gravity pressure.
|
||||
* @param[in] wctrl Well controls.
|
||||
* @param[in] WI Well indices.
|
||||
* @param[in] wdp Gravity pressure along well track.
|
||||
* @param[in,out] h Data object.
|
||||
*/
|
||||
void
|
||||
ifsh_assemble(struct FlowBoundaryConditions *bc,
|
||||
const double *src,
|
||||
const double *Binv,
|
||||
const double *gpress,
|
||||
well_control_t *wctrl,
|
||||
const double *WI,
|
||||
const double *wdp,
|
||||
struct fsh_data *h);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compute cell pressures (cpress) and interface fluxes (fflux) from
|
||||
* current solution of system of linear equations, <CODE>h->x</CODE>.
|
||||
* Back substitution process, projected half-contact fluxes.
|
||||
*
|
||||
* @param[in] G Grid.
|
||||
* @param[in] Binv Inverse of block-diagonal matrix \f$B\f$
|
||||
* Must coincide with the matrix used to
|
||||
* form the system of linear equations
|
||||
* currently stored in the data object.
|
||||
* @param[in] gpress Gravity pressure. Must coincide with
|
||||
* the array used to form the system of
|
||||
* linear equations.
|
||||
* @param[in] h Data object.
|
||||
* @param[out] cpress Cell pressure.
|
||||
* @param[out] fflux Interface fluxes.
|
||||
* @param[out] wpress Well pressure.
|
||||
* @param[out] wflux Well perforation fluxes.
|
||||
*/
|
||||
void
|
||||
fsh_press_flux(struct UnstructuredGrid *G,
|
||||
const double *Binv, const double *gpress,
|
||||
struct fsh_data *h,
|
||||
double *cpress, double *fflux,
|
||||
double *wpress, double *wflux);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* OPM_FSH_HEADER_INCLUDED */
|
|
@ -1,317 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/pressure/legacy_well.h>
|
||||
#include <opm/core/pressure/flow_bc.h>
|
||||
|
||||
#include <opm/core/pressure/fsh.h>
|
||||
#include <opm/core/pressure/fsh_common_impl.h>
|
||||
|
||||
#include <opm/core/pressure/mimetic/hybsys.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys_global.h>
|
||||
|
||||
#if defined MAX
|
||||
#undef MAX
|
||||
#endif
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Release dynamic memory resources associated to internal data of a
|
||||
* particular (incompressible) flow solver instance. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
fsh_destroy_impl(struct fsh_impl *pimpl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (pimpl != NULL) {
|
||||
hybsys_well_free(pimpl->wsys );
|
||||
hybsys_free (pimpl->sys );
|
||||
free (pimpl->ddata);
|
||||
free (pimpl->idata);
|
||||
}
|
||||
|
||||
free(pimpl);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Eliminate 'npp' prescribed (Dirichlet) conditions (locdof,dofval)
|
||||
* from global system of linear equations. Move known values to RHS
|
||||
* whilst zeroing coefficient matrix contributions. Local system of
|
||||
* dimension 'n'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
fsh_explicit_elimination(int n, int npp,
|
||||
int *locdof, double *dofval,
|
||||
double *S, double *r)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, p;
|
||||
|
||||
for (i = 0; i < npp; i++) {
|
||||
for (p = (locdof[i] + 1) % n; p != locdof[i]; p = (p + 1) % n) {
|
||||
/* Subtract known values from RHS. */
|
||||
r[p] -= S[p + locdof[i]*n] * dofval[i];
|
||||
|
||||
/* Eliminate DOF (zero row/col "locdof[i]"; leave diagonal). */
|
||||
S[p + locdof[i]*n] = 0.0;
|
||||
S[locdof[i] + p*n] = 0.0;
|
||||
}
|
||||
|
||||
/* Produce trivial equation S(i,i)*x(i) = S(i,i)*x0(i). */
|
||||
r[p] = S[p * (n + 1)] * dofval[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Release memory resources for ifsh data-handle 'h' */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_destroy(struct fsh_data *h)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (h != NULL) {
|
||||
fsh_destroy_impl(h->pimpl);
|
||||
csrmatrix_delete(h->A );
|
||||
}
|
||||
|
||||
free(h);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct fsh_impl *
|
||||
fsh_impl_allocate_basic(size_t idata_sz, size_t ddata_sz)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct fsh_impl *new;
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
|
||||
if (new != NULL) {
|
||||
new->idata = malloc(idata_sz * sizeof *new->idata);
|
||||
new->ddata = malloc(ddata_sz * sizeof *new->ddata);
|
||||
|
||||
new->sys = NULL;
|
||||
new->wsys = NULL;
|
||||
|
||||
if ((new->idata == NULL) || (new->ddata == NULL)) {
|
||||
fsh_destroy_impl(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Determine nnz (=sum(diff(facePos)^2)) and max(diff(facePos) for grid */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_count_grid_dof(struct UnstructuredGrid *G, int *max_ngdof, size_t *sum_ngdof2)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, n;
|
||||
|
||||
*max_ngdof = INT_MIN;
|
||||
*sum_ngdof2 = 0;
|
||||
|
||||
for (c = 0; c < G->number_of_cells; c++) {
|
||||
n = G->cell_facepos[c + 1] - G->cell_facepos[c];
|
||||
|
||||
*max_ngdof = MAX(*max_ngdof, n);
|
||||
*sum_ngdof2 += n * n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Impose boundary conditions on local contribution to global system. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int
|
||||
fsh_impose_bc(int nconn, int *conn, struct FlowBoundaryConditions *bc,
|
||||
struct fsh_impl *pimpl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, j, npp, f;
|
||||
|
||||
npp = 0;
|
||||
for (i = 0; i < nconn; i++) {
|
||||
f = conn[i];
|
||||
|
||||
j = pimpl->bdry_condition[ f ];
|
||||
|
||||
if (j != -1) {
|
||||
|
||||
if (bc->type[j] == BC_PRESSURE) {
|
||||
pimpl->work [npp] = bc->value[j];
|
||||
pimpl->iwork[npp] = i;
|
||||
|
||||
npp += 1;
|
||||
} else if (bc->type[j] == BC_FLUX_TOTVOL) {
|
||||
pimpl->sys->r[i] -= bc->value[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (npp > 0) {
|
||||
fsh_explicit_elimination(nconn, npp,
|
||||
pimpl->iwork,
|
||||
pimpl->work,
|
||||
pimpl->sys->S,
|
||||
pimpl->sys->r);
|
||||
}
|
||||
|
||||
return npp;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_map_bdry_condition(struct FlowBoundaryConditions *fbc ,
|
||||
struct fsh_impl *pimpl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int f, i;
|
||||
size_t j;
|
||||
|
||||
for (i = 0; i < pimpl->nf; i++) { pimpl->bdry_condition[ i ] = -1; }
|
||||
|
||||
if (fbc != NULL) {
|
||||
assert (fbc->cond_pos[0] == 0);
|
||||
|
||||
for (i = 0, j = 0; ((size_t) i) < fbc->nbc; i++) {
|
||||
for (; j < fbc->cond_pos[i + 1]; j++) {
|
||||
f = fbc->face[ j ];
|
||||
|
||||
assert ((0 <= f) && (f < pimpl->nf));
|
||||
|
||||
pimpl->bdry_condition[ f ] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_define_impl_arrays(size_t nc,
|
||||
size_t nf,
|
||||
size_t nnu,
|
||||
size_t nhf,
|
||||
size_t max_ncf,
|
||||
well_t *W,
|
||||
struct fsh_impl *pimpl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
pimpl->cflux = pimpl->ddata + 2 * nnu;
|
||||
pimpl->work = pimpl->cflux + nhf;
|
||||
|
||||
pimpl->gdof_pos = pimpl->idata;
|
||||
pimpl->gdof = pimpl->gdof_pos + (nc + 1);
|
||||
pimpl->iwork = pimpl->gdof + nhf;
|
||||
|
||||
pimpl->bdry_condition = pimpl->iwork + max_ncf;
|
||||
|
||||
if (W != NULL) {
|
||||
pimpl->cwell_pos = pimpl->bdry_condition + nf;
|
||||
pimpl->cwells = pimpl->cwell_pos + nc + 1;
|
||||
|
||||
pimpl->WI = pimpl->work + max_ncf;
|
||||
pimpl->wdp = pimpl->WI + W->well_connpos[ W->number_of_wells ];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_define_cell_wells(size_t nc, well_t *W, struct fsh_impl *pimpl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nc + 1; i++) {
|
||||
pimpl->cwell_pos[i] = 0;
|
||||
}
|
||||
|
||||
derive_cell_wells(nc, W, pimpl->cwell_pos, pimpl->cwells);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_define_linsys_arrays(struct fsh_data *h)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
h->b = h->pimpl->ddata;
|
||||
h->x = h->b + h->A->m;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
fsh_compute_table_sz(struct UnstructuredGrid *G, well_t *W, int max_ngconn,
|
||||
size_t *nnu, size_t *idata_sz, size_t *ddata_sz)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int nc, ngconn_tot;
|
||||
|
||||
*nnu = G->number_of_faces;
|
||||
|
||||
nc = G->number_of_cells;
|
||||
ngconn_tot = G->cell_facepos[nc];
|
||||
|
||||
*idata_sz = nc + 1; /* gdof_pos */
|
||||
*idata_sz += ngconn_tot; /* gdof */
|
||||
*idata_sz += G->number_of_faces; /* bdry_condition */
|
||||
*idata_sz += max_ngconn; /* iwork */
|
||||
|
||||
*ddata_sz = 2 * (*nnu); /* rhs + soln */
|
||||
*ddata_sz += ngconn_tot; /* cflux */
|
||||
*ddata_sz += max_ngconn; /* work */
|
||||
|
||||
if (W != NULL) {
|
||||
*nnu += W->number_of_wells;
|
||||
|
||||
/* cwell_pos */
|
||||
*idata_sz += nc + 1;
|
||||
|
||||
/* cwells */
|
||||
*idata_sz += 2 * W->well_connpos[ W->number_of_wells ];
|
||||
|
||||
/* rhs + soln */
|
||||
*ddata_sz += 2 * W->number_of_wells;
|
||||
|
||||
/* WI, wdp */
|
||||
*ddata_sz += 2 * W->well_connpos[ W->number_of_wells ];
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_FSH_COMMON_IMPL_HEADER_INCLUDED
|
||||
#define OPM_FSH_COMMON_IMPL_HEADER_INCLUDED
|
||||
|
||||
/* Internal header. Don't install. */
|
||||
|
||||
struct fsh_impl {
|
||||
int nc, nf, nw; /* Number of cells, faces, wells */
|
||||
|
||||
/* Topology */
|
||||
int *gdof_pos; /* Pointers, grid DOFs (== cell_facepos) */
|
||||
int *gdof; /* Grid DOFs (== cell_faces) */
|
||||
|
||||
int *cwell_pos; /* Start pointers, well DOFs (c->w) */
|
||||
int *cwells; /* Well DOFs (c->w) */
|
||||
|
||||
/* Discretisation data */
|
||||
double *WI; /* Permuted well production indices */
|
||||
double *wdp; /* Permuted well gravity pressures */
|
||||
|
||||
double *cflux; /* Cell (half-contact) fluxes */
|
||||
|
||||
struct hybsys *sys; /* Hybrid cell contribs */
|
||||
struct hybsys_well *wsys; /* Hybrid cell contribs from wells */
|
||||
|
||||
double *work; /* Scratch array, floating point */
|
||||
int *iwork; /* Scratch array, integers */
|
||||
|
||||
int *bdry_condition; /* Map face->boundary condition ID */
|
||||
|
||||
/* Linear storage goes here... */
|
||||
int *idata; /* Actual storage array, integers */
|
||||
double *ddata; /* Actual storage array, floating point */
|
||||
};
|
||||
|
||||
|
||||
struct fsh_impl *
|
||||
fsh_impl_allocate_basic(size_t idata_sz, size_t ddata_sz);
|
||||
|
||||
void
|
||||
fsh_count_grid_dof(struct UnstructuredGrid *G, int *max_ngdof, size_t *sum_ngdof2);
|
||||
|
||||
void
|
||||
fsh_map_bdry_condition(struct FlowBoundaryConditions *fbc ,
|
||||
struct fsh_impl *pimpl);
|
||||
|
||||
int
|
||||
fsh_impose_bc(int ndof,
|
||||
int *dof,
|
||||
struct FlowBoundaryConditions *bc,
|
||||
struct fsh_impl *pimpl);
|
||||
|
||||
void
|
||||
fsh_define_impl_arrays(size_t nc,
|
||||
size_t nf,
|
||||
size_t nnu,
|
||||
size_t nhf,
|
||||
size_t max_ncf,
|
||||
well_t *W,
|
||||
struct fsh_impl *pimpl);
|
||||
|
||||
void
|
||||
fsh_define_cell_wells(size_t nc, well_t *W, struct fsh_impl *pimpl);
|
||||
|
||||
void
|
||||
fsh_define_linsys_arrays(struct fsh_data *h);
|
||||
|
||||
void
|
||||
fsh_compute_table_sz(struct UnstructuredGrid *G, well_t *W, int max_ngconn,
|
||||
size_t *nnu, size_t *idata_sz, size_t *ddata_sz);
|
||||
|
||||
#endif /* OPM_FSH_COMMON_IMPL_HEADER_INCLUDED */
|
|
@ -1,390 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opm/core/pressure/fsh.h>
|
||||
#include <opm/core/pressure/fsh_common_impl.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys_global.h>
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
ifsh_set_effective_well_params(const double *WI,
|
||||
const double *wdp,
|
||||
struct fsh_data *ifsh)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, nc, i, perf;
|
||||
int *cwpos, *cwells;
|
||||
double *wsys_WI, *wsys_wdp;
|
||||
|
||||
nc = ifsh->pimpl->nc;
|
||||
cwpos = ifsh->pimpl->cwell_pos;
|
||||
cwells = ifsh->pimpl->cwells;
|
||||
wsys_WI = ifsh->pimpl->WI;
|
||||
wsys_wdp = ifsh->pimpl->wdp;
|
||||
|
||||
for (c = i = 0; c < nc; c++) {
|
||||
for (; i < cwpos[c + 1]; i++) {
|
||||
perf = cwells[2*i + 1];
|
||||
|
||||
wsys_WI [i] = WI [perf];
|
||||
wsys_wdp[i] = wdp[perf];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
ifsh_assemble_grid(struct FlowBoundaryConditions *bc,
|
||||
const double *Binv,
|
||||
const double *gpress,
|
||||
const double *src,
|
||||
struct fsh_data *ifsh)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, n, nc, p1, p2;
|
||||
int npp;
|
||||
int *pgconn, *gconn;
|
||||
|
||||
nc = ifsh->pimpl->nc;
|
||||
pgconn = ifsh->pimpl->gdof_pos;
|
||||
gconn = ifsh->pimpl->gdof;
|
||||
|
||||
p1 = p2 = npp = 0;
|
||||
for (c = 0; c < nc; c++) {
|
||||
n = pgconn[c + 1] - pgconn[c];
|
||||
|
||||
hybsys_cellcontrib_symm(c, n, p1, p2, gpress, src, Binv,
|
||||
ifsh->pimpl->sys);
|
||||
|
||||
npp += fsh_impose_bc(n, gconn + p1, bc, ifsh->pimpl);
|
||||
|
||||
hybsys_global_assemble_cell(n, gconn + p1,
|
||||
ifsh->pimpl->sys->S,
|
||||
ifsh->pimpl->sys->r,
|
||||
ifsh->A, ifsh->b);
|
||||
|
||||
p1 += n;
|
||||
p2 += n * n;
|
||||
}
|
||||
|
||||
return npp;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
ifsh_impose_well_control(int c,
|
||||
struct FlowBoundaryConditions *bc,
|
||||
well_control_t *wctrl,
|
||||
struct fsh_data *ifsh)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ngconn, nwconn, i, j, w1, w2, wg, f;
|
||||
int *pgconn, *gconn, *pwconn, *wconn;
|
||||
|
||||
double bhp;
|
||||
double *r, *r2w, *w2w;
|
||||
|
||||
/* Enforce symmetric system */
|
||||
assert (ifsh->pimpl->wsys->r2w == ifsh->pimpl->wsys->w2r);
|
||||
|
||||
pgconn = ifsh->pimpl->gdof_pos;
|
||||
pwconn = ifsh->pimpl->cwell_pos;
|
||||
|
||||
gconn = ifsh->pimpl->gdof + pgconn[c];
|
||||
wconn = ifsh->pimpl->cwells + 2*pwconn[c];
|
||||
|
||||
ngconn = pgconn[c + 1] - pgconn[c];
|
||||
nwconn = pwconn[c + 1] - pwconn[c];
|
||||
|
||||
r2w = ifsh->pimpl->wsys->r2w;
|
||||
w2w = ifsh->pimpl->wsys->w2w;
|
||||
r = ifsh->pimpl->wsys->r ;
|
||||
|
||||
/* Adapt local system to prescribed boundary pressures (r->w) */
|
||||
for (i = 0; i < ngconn; i++) {
|
||||
f = gconn[i];
|
||||
j = ifsh->pimpl->bdry_condition[ f ];
|
||||
|
||||
if (j != -1) {
|
||||
for (w1 = 0; w1 < nwconn; w1++) {
|
||||
/* Eliminate prescribed (boundary) pressure value */
|
||||
r [ngconn + w1] -= r2w[i + w1*ngconn] * bc->value[j];
|
||||
r2w[i + w1*ngconn] = 0.0;
|
||||
}
|
||||
|
||||
r[i] = 0.0; /* RHS value handled in *reservoir* asm */
|
||||
}
|
||||
}
|
||||
|
||||
/* Adapt local system to prescribed well (bottom-hole) pressures;
|
||||
* w->r and w->w. */
|
||||
for (w1 = 0; w1 < nwconn; w1++) {
|
||||
wg = wconn[2*w1 + 0];
|
||||
|
||||
if (wctrl->ctrl[wg] == BHP) {
|
||||
bhp = wctrl->target[wg];
|
||||
|
||||
/* Well->reservoir */
|
||||
for (i = 0; i < ngconn; i++) {
|
||||
#ifndef NDEBUG
|
||||
j = ifsh->pimpl->bdry_condition[ gconn[i] ];
|
||||
|
||||
assert ((j == -1) ||
|
||||
(bc->type[j] != BC_PRESSURE) ||
|
||||
!(fabs(r2w[i + w1*ngconn]) > 0.0));
|
||||
#endif
|
||||
|
||||
r [i] -= r2w[i + w1*ngconn] * bhp;
|
||||
r2w[i + w1*ngconn] = 0.0;
|
||||
}
|
||||
|
||||
/* Well->well */
|
||||
for (w2 = (w1 + 1) % nwconn; w2 != w1; w2 = (w2 + 1) % nwconn) {
|
||||
r [ngconn + w2] -= w2w[w2 + w1*nwconn] * bhp;
|
||||
w2w[w2 + w1*ngconn] = 0.0;
|
||||
w2w[w1 + w2*ngconn] = 0.0;
|
||||
}
|
||||
|
||||
/* Assemble final well equation of the form S*p_bh = S*p_bh^0 */
|
||||
assert (fabs(w2w[w1 * (nwconn + 1)]) > 0.0);
|
||||
|
||||
r[ngconn + w1] = w2w[w1 * (nwconn + 1)] * bhp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
ifsh_assemble_well(struct FlowBoundaryConditions *bc,
|
||||
well_control_t *wctrl,
|
||||
struct fsh_data *ifsh)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int npp;
|
||||
int ngconn, nwconn, c, nc, w;
|
||||
|
||||
int *pgconn, *gconn, *pwconn, *wconn;
|
||||
|
||||
nc = ifsh->pimpl->nc;
|
||||
|
||||
pgconn = ifsh->pimpl->gdof_pos;
|
||||
gconn = ifsh->pimpl->gdof;
|
||||
pwconn = ifsh->pimpl->cwell_pos;
|
||||
wconn = ifsh->pimpl->cwells;
|
||||
|
||||
for (c = 0; c < nc; c++) {
|
||||
ngconn = pgconn[c + 1] - pgconn[c];
|
||||
nwconn = pwconn[c + 1] - pwconn[c];
|
||||
|
||||
if (nwconn > 0) {
|
||||
hybsys_well_cellcontrib_symm(c, ngconn, pgconn[c],
|
||||
pwconn,
|
||||
ifsh->pimpl->WI,
|
||||
ifsh->pimpl->wdp,
|
||||
ifsh->pimpl->sys,
|
||||
ifsh->pimpl->wsys);
|
||||
|
||||
ifsh_impose_well_control(c, bc, wctrl, ifsh);
|
||||
|
||||
hybsys_global_assemble_well_sym(ifsh->pimpl->nf,
|
||||
ngconn, gconn + pgconn[c],
|
||||
nwconn, wconn + 2*pwconn[c] + 0,
|
||||
ifsh->pimpl->wsys->r2w,
|
||||
ifsh->pimpl->wsys->w2w,
|
||||
ifsh->pimpl->wsys->r,
|
||||
ifsh->A, ifsh->b);
|
||||
}
|
||||
}
|
||||
|
||||
npp = 0;
|
||||
for (w = 0; w < ifsh->pimpl->nw; w++) {
|
||||
if (wctrl->ctrl[w] == BHP) {
|
||||
npp += 1;
|
||||
} else if (wctrl->ctrl[w] == RATE) {
|
||||
/* Impose total rate constraint.
|
||||
*
|
||||
* Note sign resulting from ->target[w] denoting
|
||||
* *injection* flux. */
|
||||
ifsh->b[ifsh->pimpl->nf + w] -= - wctrl->target[w];
|
||||
}
|
||||
}
|
||||
|
||||
return npp;
|
||||
}
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
* Public routines follow.
|
||||
* ====================================================================== */
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Allocate and define supporting structures for assembling the global
|
||||
* system of linear equations to couple the grid (reservoir)
|
||||
* connections represented by 'G' and, if present (i.e., non-NULL),
|
||||
* the well connections represented by 'W'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct fsh_data *
|
||||
ifsh_construct(struct UnstructuredGrid *G, well_t *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int nc, ngconn_tot;
|
||||
size_t idata_sz, ddata_sz, nnu;
|
||||
struct fsh_data *new;
|
||||
|
||||
assert (G != NULL);
|
||||
|
||||
/* Allocate master structure, define system matrix sparsity */
|
||||
new = malloc(1 * sizeof *new);
|
||||
if (new != NULL) {
|
||||
new->A = hybsys_define_globconn(G, W);
|
||||
new->pimpl = NULL;
|
||||
|
||||
if (new->A == NULL) {
|
||||
fsh_destroy(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Allocate implementation structure */
|
||||
if (new != NULL) {
|
||||
fsh_count_grid_dof(G, &new->max_ngconn, &new->sum_ngconn2);
|
||||
|
||||
fsh_compute_table_sz(G, W, new->max_ngconn,
|
||||
&nnu, &idata_sz, &ddata_sz);
|
||||
|
||||
new->pimpl = fsh_impl_allocate_basic(idata_sz, ddata_sz);
|
||||
|
||||
if (new->pimpl == NULL) {
|
||||
fsh_destroy(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Allocate Schur complement contributions. Symmetric system. */
|
||||
if (new != NULL) {
|
||||
nc = G->number_of_cells;
|
||||
ngconn_tot = G->cell_facepos[nc];
|
||||
|
||||
fsh_define_linsys_arrays(new);
|
||||
fsh_define_impl_arrays(nc, G->number_of_faces, nnu,
|
||||
ngconn_tot, new->max_ngconn, W, new->pimpl);
|
||||
|
||||
new->pimpl->sys = hybsys_allocate_symm(new->max_ngconn,
|
||||
nc, ngconn_tot);
|
||||
|
||||
if (W != NULL) {
|
||||
fsh_define_cell_wells(nc, W, new->pimpl);
|
||||
|
||||
new->pimpl->wsys = hybsys_well_allocate_symm(new->max_ngconn, nc,
|
||||
new->pimpl->cwell_pos);
|
||||
}
|
||||
|
||||
if ((new->pimpl->sys == NULL) ||
|
||||
((W != NULL) && (new->pimpl->wsys == NULL))) {
|
||||
/* Failed to allocate ->sys or ->wsys (if W != NULL) */
|
||||
fsh_destroy(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (new != NULL) {
|
||||
/* All allocations succeded. Fill metadata and return. */
|
||||
new->pimpl->nc = nc;
|
||||
new->pimpl->nf = G->number_of_faces;
|
||||
new->pimpl->nw = (W != NULL) ? W->number_of_wells : 0;
|
||||
|
||||
memcpy(new->pimpl->gdof_pos,
|
||||
G->cell_facepos ,
|
||||
(nc + 1) * sizeof *new->pimpl->gdof_pos);
|
||||
|
||||
memcpy(new->pimpl->gdof ,
|
||||
G->cell_faces ,
|
||||
ngconn_tot * sizeof *new->pimpl->gdof);
|
||||
|
||||
hybsys_init(new->max_ngconn, new->pimpl->sys);
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Assemble global system of linear equations
|
||||
*
|
||||
* ifsh->A * ifsh->x = ifsh->b
|
||||
*
|
||||
* from effective local inner product matrices Binv, effective gravity
|
||||
* pressure gpress, boundary conditions bc, and source terms src. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
ifsh_assemble(struct FlowBoundaryConditions *bc,
|
||||
const double *src,
|
||||
const double *Binv,
|
||||
const double *gpress,
|
||||
well_control_t *wctrl,
|
||||
const double *WI,
|
||||
const double *wdp,
|
||||
struct fsh_data *ifsh)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int npp; /* Number of prescribed pressure values */
|
||||
|
||||
fsh_map_bdry_condition(bc, ifsh->pimpl);
|
||||
|
||||
hybsys_schur_comp_symm(ifsh->pimpl->nc,
|
||||
ifsh->pimpl->gdof_pos,
|
||||
Binv, ifsh->pimpl->sys);
|
||||
|
||||
if (ifsh->pimpl->nw > 0) {
|
||||
ifsh_set_effective_well_params(WI, wdp, ifsh);
|
||||
|
||||
hybsys_well_schur_comp_symm(ifsh->pimpl->nc,
|
||||
ifsh->pimpl->cwell_pos,
|
||||
ifsh->pimpl->WI,
|
||||
ifsh->pimpl->sys,
|
||||
ifsh->pimpl->wsys);
|
||||
}
|
||||
|
||||
npp = ifsh_assemble_grid(bc, Binv, gpress, src, ifsh);
|
||||
|
||||
if (ifsh->pimpl->nw > 0) {
|
||||
npp += ifsh_assemble_well(bc, wctrl, ifsh);
|
||||
}
|
||||
|
||||
if (npp == 0) {
|
||||
ifsh->A->sa[0] *= 2; /* Remove zero eigenvalue */
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <opm/core/pressure/legacy_well.h>
|
||||
|
||||
|
||||
/* Release memory resources for cell->well mapping. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
deallocate_cell_wells(int *cwpos, int *cwells)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
free(cwells);
|
||||
free(cwpos);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate memory resources for cell->well mapping.
|
||||
*
|
||||
* Returns 1 if successful and 0 if not. CSR array pair set to NULL
|
||||
* unless allocation succeeds. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int
|
||||
allocate_cell_wells(int nc, well_t *W, int **cwpos, int **cwells)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, totwconn;
|
||||
|
||||
totwconn = W->well_connpos[W->number_of_wells];
|
||||
|
||||
*cwpos = malloc((nc + 1) * sizeof **cwpos );
|
||||
*cwells = malloc(2 * totwconn * sizeof **cwells);
|
||||
|
||||
if ((*cwpos == NULL) || (*cwells == NULL)) {
|
||||
deallocate_cell_wells(*cwpos, *cwells);
|
||||
|
||||
*cwpos = NULL;
|
||||
*cwells = NULL;
|
||||
|
||||
totwconn = 0;
|
||||
} else {
|
||||
for (i = 0; i < nc + 1; i++) {
|
||||
(*cwpos)[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return totwconn;
|
||||
}
|
||||
|
||||
|
||||
/* Derive cell->well mapping from well->cell (connection) mapping. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
derive_cell_wells(int nc, well_t *W, int *cwpos, int *cwells)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, w, *c, *connpos;
|
||||
|
||||
connpos = W->well_connpos;
|
||||
|
||||
c = W->well_cells;
|
||||
for (w = 0; w < W->number_of_wells; w++) {
|
||||
for (; c != W->well_cells + connpos[w + 1]; c++) {
|
||||
cwpos[*c + 1] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1; i <= nc; i++) {
|
||||
cwpos[0] += cwpos[i];
|
||||
cwpos[i] = cwpos[0] - cwpos[i];
|
||||
}
|
||||
|
||||
cwpos[0] = 0;
|
||||
c = W->well_cells;
|
||||
for (w = 0; w < W->number_of_wells; w++) {
|
||||
for (; c != W->well_cells + connpos[w + 1]; c++) {
|
||||
cwells[ 2*cwpos[*c + 1] + 0 ] = w;
|
||||
cwells[ 2*cwpos[*c + 1] + 1 ] = c - W->well_cells;
|
||||
|
||||
cwpos[*c + 1] += 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -87,48 +87,6 @@ typedef struct LegacyWellCompletions well_t;
|
|||
*/
|
||||
typedef struct LegacyWellControls well_control_t;
|
||||
|
||||
/**
|
||||
* Allocate cell-to-well mapping (as a sparse array).
|
||||
*
|
||||
* @param[in] nc Total number of cells.
|
||||
* @param[in] W Well topology (well-to-cell mapping).
|
||||
* @param[out] cwpos Indirection array. Points to array of size
|
||||
* <CODE>nc + 1</CODE> if successful.
|
||||
* @param[out] cwells Cell-to-well mapping. Points to array
|
||||
* of size <CODE>W->well_connpos[
|
||||
* W->number_of_wells]</CODE> if successful.
|
||||
* @return Positive number (size of <CODE>*cwells</CODE>)
|
||||
* if successful. Zero in case of allocation failure.
|
||||
*/
|
||||
int
|
||||
allocate_cell_wells(int nc, well_t *W, int **cwpos, int **cwells);
|
||||
|
||||
/**
|
||||
* Dispose of memory resources allocated using function
|
||||
* allocate_cell_wells().
|
||||
*
|
||||
* Following a call to deallocate_cell_wells(), the input pointers
|
||||
* are no longer valid.
|
||||
*
|
||||
* @param[in,out] cvpos Cell-to-well start pointers.
|
||||
* @param[in,out] cwells Cell-to-well mapping.
|
||||
*/
|
||||
void
|
||||
deallocate_cell_wells(int *cvpos, int *cwells);
|
||||
|
||||
/**
|
||||
* Construct cell-to-well mapping (i.e., transpose the
|
||||
* well-to-cell mapping represented by <CODE>W->well_cells</CODE>).
|
||||
*
|
||||
* @param[in] nc Total number of cells.
|
||||
* @param[in] W Well topology (well-to-cell mapping).
|
||||
* @param[out] cwpos Cell-to-well start pointers.
|
||||
* @param[out] cwells Cell-to-well mapping.
|
||||
*/
|
||||
void
|
||||
derive_cell_wells(int nc, well_t *W, int *cwpos, int *cwells);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,694 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opm/core/linalg/blas_lapack.h>
|
||||
#include <opm/core/pressure/mimetic/hybsys.h>
|
||||
|
||||
|
||||
#if defined(MAX)
|
||||
#undef MAX
|
||||
#endif
|
||||
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct hybsys *
|
||||
hybsys_allocate_symm(int max_nconn, int nc, int nconn_tot)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct hybsys *new;
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
if (new != NULL) {
|
||||
new->one = malloc(max_nconn * sizeof *new->one);
|
||||
new->r = malloc(max_nconn * sizeof *new->r );
|
||||
new->S = malloc(max_nconn * max_nconn * sizeof *new->S );
|
||||
new->L = malloc(nc * sizeof *new->L );
|
||||
new->q = malloc(nc * sizeof *new->q );
|
||||
new->F1 = malloc(nconn_tot * sizeof *new->F1 );
|
||||
|
||||
if ((new->one == NULL) || (new->r == NULL) || (new->S == NULL) ||
|
||||
(new->L == NULL) || (new->q == NULL) || (new->F1 == NULL)) {
|
||||
hybsys_free(new);
|
||||
|
||||
new = NULL;
|
||||
} else {
|
||||
new->F2 = new->F1;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct hybsys *
|
||||
hybsys_allocate_unsymm(int max_nconn, int nc, int nconn_tot)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct hybsys *new;
|
||||
|
||||
new = hybsys_allocate_symm(max_nconn, nc, nconn_tot);
|
||||
|
||||
if (new != NULL) {
|
||||
new->F2 = malloc(nconn_tot * sizeof *new->F2);
|
||||
|
||||
if (new->F2 == NULL) {
|
||||
hybsys_free(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
hybsys_well_count_conn(int nc, const int *cwpos,
|
||||
int *max_nw, size_t *sum_nwc)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, nw;
|
||||
|
||||
*max_nw = 0;
|
||||
*sum_nwc = 0;
|
||||
|
||||
for (c = 0; c < nc; c++) {
|
||||
nw = cwpos[c + 1] - cwpos[c];
|
||||
|
||||
assert (nw >= 0);
|
||||
|
||||
*max_nw = MAX(*max_nw, nw);
|
||||
*sum_nwc += nw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct hybsys_well *
|
||||
hybsys_well_allocate_symm(int max_nconn, int nc, int *cwpos)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int max_nw;
|
||||
size_t sum_nwc, alloc_sz;
|
||||
|
||||
struct hybsys_well *new;
|
||||
|
||||
assert (cwpos[nc] > cwpos[0]); /* Else no wells. */
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
|
||||
if (new != NULL) {
|
||||
hybsys_well_count_conn(nc, cwpos, &max_nw, &sum_nwc);
|
||||
|
||||
alloc_sz = sum_nwc; /* F1 */
|
||||
alloc_sz += max_nconn + max_nw; /* r */
|
||||
alloc_sz += max_nw * max_nconn; /* w2r */
|
||||
alloc_sz += max_nw * max_nw; /* w2w */
|
||||
|
||||
new->data = malloc(alloc_sz * sizeof *new->data);
|
||||
|
||||
if (new->data != NULL) {
|
||||
new->F1 = new->data;
|
||||
new->F2 = new->F1;
|
||||
|
||||
new->r = new->F2 + sum_nwc;
|
||||
|
||||
new->w2r = new->r + max_nconn + max_nw;
|
||||
new->r2w = new->w2r;
|
||||
|
||||
new->w2w = new->r2w + (max_nw * max_nconn);
|
||||
} else {
|
||||
hybsys_well_free(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct hybsys_well *
|
||||
hybsys_well_allocate_unsymm(int max_nconn, int nc, int *cwpos)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int max_nw;
|
||||
size_t sum_nwc, alloc_sz;
|
||||
|
||||
struct hybsys_well *new;
|
||||
|
||||
assert (cwpos[nc] > cwpos[0]); /* Else no wells. */
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
|
||||
if (new != NULL) {
|
||||
hybsys_well_count_conn(nc, cwpos, &max_nw, &sum_nwc);
|
||||
|
||||
alloc_sz = 2 * sum_nwc; /* F1, F2 */
|
||||
alloc_sz += max_nconn + max_nw; /* r */
|
||||
alloc_sz += 2 * max_nw * max_nconn; /* w2r, r2w */
|
||||
alloc_sz += max_nw * max_nw; /* w2w */
|
||||
|
||||
new->data = malloc(alloc_sz * sizeof *new->data);
|
||||
|
||||
if (new->data != NULL) {
|
||||
new->F1 = new->data;
|
||||
new->F2 = new->F1 + sum_nwc;
|
||||
|
||||
new->r = new->F2 + sum_nwc;
|
||||
|
||||
new->w2r = new->r + max_nconn + max_nw;
|
||||
new->r2w = new->w2r + (max_nw * max_nconn);
|
||||
|
||||
new->w2w = new->r2w + (max_nw * max_nconn);
|
||||
} else {
|
||||
hybsys_well_free(new);
|
||||
new = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_free(struct hybsys *sys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (sys != NULL) {
|
||||
if (sys->F2 != sys->F1) { free(sys->F2); } /* unsymmetric system */
|
||||
|
||||
free(sys->F1 );
|
||||
free(sys->q );
|
||||
free(sys->L );
|
||||
free(sys->S );
|
||||
free(sys->r );
|
||||
free(sys->one);
|
||||
}
|
||||
|
||||
free(sys);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_well_free(struct hybsys_well *wsys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (wsys != NULL) {
|
||||
free(wsys->data);
|
||||
}
|
||||
|
||||
free(wsys);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_init(int max_nconn, struct hybsys *sys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_nconn; i++) {
|
||||
sys->one[i] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_schur_comp_symm(int nc, const int *pconn,
|
||||
const double *Binv, struct hybsys *sys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, p1, p2, nconn;
|
||||
double a1, a2;
|
||||
|
||||
MAT_SIZE_T incx, incy;
|
||||
MAT_SIZE_T nrows, ncols, lda;
|
||||
|
||||
incx = incy = 1;
|
||||
p1 = p2 = 0;
|
||||
|
||||
for (c = 0; c < nc; c++) {
|
||||
p1 = pconn[c + 0];
|
||||
nconn = pconn[c + 1] - pconn[c];
|
||||
nrows = ncols = lda = nconn;
|
||||
|
||||
/* F <- C' * inv(B) == (inv(B) * ones(n,1))' in single cell */
|
||||
a1 = 1.0; a2 = 0.0;
|
||||
dgemv_("No Transpose" , &nrows, &ncols,
|
||||
&a1, &Binv[p2] , &lda, sys->one, &incx,
|
||||
&a2, &sys->F1[p1], &incy);
|
||||
|
||||
/* L <- C' * inv(B) * C == SUM(F) == ones(n,1)' * F */
|
||||
sys->L[c] = ddot_(&nrows, sys->one, &incx, &sys->F1[p1], &incy);
|
||||
|
||||
p2 += nconn * nconn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_schur_comp_unsymm(int nc, const int *pconn,
|
||||
const double *Binv, const double *BIV,
|
||||
const double *P, struct hybsys *sys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, p1, p2, nconn;
|
||||
double a1, a2;
|
||||
|
||||
MAT_SIZE_T incx, incy;
|
||||
MAT_SIZE_T nrows, ncols, lda;
|
||||
|
||||
assert ((sys->F2 != sys->F1) &&
|
||||
(sys->F2 != NULL));
|
||||
|
||||
incx = incy = 1;
|
||||
p2 = 0;
|
||||
|
||||
for (c = 0; c < nc; c++) {
|
||||
p1 = pconn[c + 0];
|
||||
nconn = pconn[c + 1] - pconn[c];
|
||||
|
||||
nrows = ncols = lda = nconn;
|
||||
|
||||
/* F1 <- C' * inv(B) */
|
||||
a1 = 1.0; a2 = 0.0;
|
||||
dgemv_("No Transpose" , &nrows, &ncols,
|
||||
&a1, &Binv[p2] , &lda, sys->one, &incx,
|
||||
&a2, &sys->F1[p1], &incy);
|
||||
|
||||
/* F2 <- (C - V)' * inv(B) == F1 - V'*inv(B) */
|
||||
a1 = -1.0;
|
||||
memcpy(&sys->F2[p1], &sys->F1[p1], nconn * sizeof sys->F2[p1]);
|
||||
daxpy_(&nrows, &a1, &BIV[p1], &incx, &sys->F2[p1], &incy);
|
||||
|
||||
/* L <- (C - V)' * inv(B) * C - P */
|
||||
sys->L[c] = ddot_(&nrows, sys->one, &incx, &sys->F1[p1], &incy);
|
||||
sys->L[c] -= ddot_(&nrows, sys->one, &incx, &BIV[p1] , &incy);
|
||||
sys->L[c] -= P[c];
|
||||
|
||||
p2 += nconn * nconn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_schur_comp_gen(int nc, const int *pconn,
|
||||
const double *Binv, const double *C2,
|
||||
const double *P, struct hybsys *sys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, p1, p2, nconn;
|
||||
double a1, a2;
|
||||
|
||||
MAT_SIZE_T incx, incy;
|
||||
MAT_SIZE_T nrows, ncols, lda;
|
||||
|
||||
assert ((sys->F2 != sys->F1) &&
|
||||
(sys->F2 != NULL));
|
||||
|
||||
incx = incy = 1;
|
||||
p2 = 0;
|
||||
|
||||
for (c = 0; c < nc; c++) {
|
||||
p1 = pconn[c + 0];
|
||||
nconn = pconn[c + 1] - pconn[c];
|
||||
|
||||
nrows = ncols = lda = nconn;
|
||||
|
||||
/* F1 <- C' * inv(B) */
|
||||
a1 = 1.0; a2 = 0.0;
|
||||
dgemv_("No Transpose" , &nrows, &ncols,
|
||||
&a1, &Binv[p2] , &lda, sys->one, &incx,
|
||||
&a2, &sys->F1[p1], &incy);
|
||||
|
||||
/* F2 <- C2' * inv(B) */
|
||||
dgemv_("No Transpose" , &nrows, &ncols,
|
||||
&a1, &Binv[p2] , &lda, &C2[p1], &incx,
|
||||
&a2, &sys->F2[p1], &incy);
|
||||
|
||||
/* L <- C2' * inv(B) * C - P == F2'*ones(n,1) - P */
|
||||
sys->L[c] = ddot_(&nrows, sys->one, &incx, &sys->F2[p1], &incy);
|
||||
sys->L[c] -= P[c];
|
||||
|
||||
p2 += nconn * nconn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_well_schur_comp_symm(int nc, const int *cwpos,
|
||||
double *WI,
|
||||
struct hybsys *sys,
|
||||
struct hybsys_well *wsys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, i;
|
||||
|
||||
for (c = i = 0; c < nc; c++) {
|
||||
for (; i < cwpos[c + 1]; i++) {
|
||||
wsys->F1[i] = WI[i];
|
||||
sys->L [c] += WI[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
hybsys_cellmat_symm_core(int nconn, const double *Binv, double L,
|
||||
const double *F, double *S)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, j;
|
||||
MAT_SIZE_T n, k, ldA, ldC;
|
||||
double a1, a2;
|
||||
|
||||
/* S <- D' * inv(B) * D == inv(B) in single cell */
|
||||
memcpy(S, Binv, nconn * nconn * sizeof *S);
|
||||
|
||||
/* S <- S - F'*inv(L)*F */
|
||||
n = ldA = ldC = nconn;
|
||||
k = 1;
|
||||
a1 = -1.0 / L;
|
||||
a2 = 1.0;
|
||||
dsyrk_("Upper Triangular", "No Transpose", &n, &k,
|
||||
&a1, F, &ldA, &a2, S, &ldC);
|
||||
|
||||
/* Account for DSYRK only updating the upper triangular part of S */
|
||||
for (j = 0; j < nconn; j++) {
|
||||
for (i = j + 1; i < nconn; i++) {
|
||||
S[i + j*nconn] = S[j + i*nconn];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
hybsys_cellmat_unsymm_core(int nconn, const double *Binv, double L,
|
||||
const double *F1, const double *F2,
|
||||
double *S)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
MAT_SIZE_T m, n, k, ldF1, ldF2, ldS;
|
||||
double a1, a2;
|
||||
|
||||
/* S <- D' * inv(B) * D == inv(B) in single cell */
|
||||
memcpy(S, Binv, nconn * nconn * sizeof *S);
|
||||
|
||||
/* S <- S - F1'*inv(L)*F2 */
|
||||
a1 = -1.0 / L;
|
||||
a2 = 1.0;
|
||||
|
||||
m = n = nconn;
|
||||
k = 1;
|
||||
ldF1 = ldF2 = 1;
|
||||
ldS = nconn;
|
||||
|
||||
dgemm_("Transpose", "No Transpose", &m, &n, &k,
|
||||
&a1, F1, &ldF1, F2, &ldF2, &a2, S, &ldS);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static double
|
||||
hybsys_cellrhs_core(int nconn, const double *gpress, double src,
|
||||
const double *Binv, double L, const double *F1,
|
||||
const double *F2, double *R)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
MAT_SIZE_T n, k, ldA, incx, incy;
|
||||
double a1, a2;
|
||||
|
||||
/* r <- inv(B)*gpress + F1'*inv(L)*(src - F2*gpress)
|
||||
* == inv(B)*gpress + F1'*inv(L)*(src - C2'*inv(B)*gpress) */
|
||||
k = 1;
|
||||
a1 = 1.0; a2 = 0.0;
|
||||
incx = incy = 1;
|
||||
|
||||
n = k = ldA = nconn;
|
||||
|
||||
dgemv_("No Transpose", &n, &k,
|
||||
&a1, Binv, &ldA, gpress, &incx,
|
||||
&a2, R , &incy);
|
||||
|
||||
src -= ddot_(&n, F2, &incx, gpress, &incy);
|
||||
|
||||
a1 = src / L;
|
||||
daxpy_(&n, &a1, F1, &incx, R, &incy);
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_cellcontrib_symm(int c, int nconn, int p1, int p2,
|
||||
const double *gpress, const double *src,
|
||||
const double *Binv, struct hybsys *sys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
hybsys_cellmat_symm_core(nconn, &Binv[p2],
|
||||
sys->L[c], &sys->F1[p1],
|
||||
sys->S);
|
||||
|
||||
sys->q[c] = hybsys_cellrhs_core(nconn, &gpress[p1], src[c], &Binv[p2],
|
||||
sys->L[c], &sys->F1[p1], &sys->F1[p1],
|
||||
sys->r);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_cellcontrib_unsymm(int c, int nconn, int p1, int p2,
|
||||
const double *gpress, const double *src,
|
||||
const double *Binv, struct hybsys *sys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
assert ((sys->F2 != sys->F1) &&
|
||||
(sys->F2 != NULL));
|
||||
|
||||
hybsys_cellmat_unsymm_core(nconn, &Binv[p2],
|
||||
sys->L[c], &sys->F1[p1], &sys->F2[p1],
|
||||
sys->S);
|
||||
|
||||
sys->q[c] = hybsys_cellrhs_core(nconn, &gpress[p1], src[c], &Binv[p2],
|
||||
sys->L[c], &sys->F1[p1], &sys->F2[p1],
|
||||
sys->r);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_well_cellcontrib_symm(int c, int ngconn, int p1,
|
||||
const int *cwpos,
|
||||
const double *WI, const double *wdp,
|
||||
struct hybsys *sys, struct hybsys_well *wsys)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, w, nw, wp1;
|
||||
MAT_SIZE_T mm, nn, kk, ld1, ld2, ld3, incx, incy;
|
||||
double a1, a2, q;
|
||||
|
||||
nw = cwpos[c + 1] - cwpos[c];
|
||||
wp1 = cwpos[c];
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* w2r = - F1(r)'*F2(w)/L, r2w = w2r' */
|
||||
mm = ngconn; ld1 = 1;
|
||||
nn = nw; ld2 = 1;
|
||||
kk = 1; ld3 = ngconn;
|
||||
|
||||
a1 = -1.0 / sys->L[c];
|
||||
a2 = 0.0;
|
||||
|
||||
dgemm_("Transpose", "No Transpose", &mm, &nn, &kk,
|
||||
&a1, &sys->F1[p1], &ld1, &wsys->F2[wp1], &ld2,
|
||||
&a2, wsys->w2r, &ld3);
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* w2w = BI - F1(w)'*F2(w)/L */
|
||||
mm = nw; ld1 = 1;
|
||||
nn = nw; ld2 = 1;
|
||||
kk = 1; ld3 = nw;
|
||||
|
||||
a1 = -1.0 / sys->L[c];
|
||||
a2 = 0.0;
|
||||
|
||||
dgemm_("Transpose", "No Transpose", &mm, &nn, &kk,
|
||||
&a1, &wsys->F1[wp1], &ld1, &wsys->F2[wp1], &ld2,
|
||||
&a2, wsys->w2w, &ld3);
|
||||
|
||||
for (w = 0; w < nw; w++) {
|
||||
wsys->w2w[w * (nw + 1)] += WI[wp1 + w];
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/* Global RHS contributions */
|
||||
mm = nw;
|
||||
incx = incy = 1;
|
||||
q = ddot_(&mm, &wsys->F2[wp1], &incx, &wdp[wp1], &incy);
|
||||
|
||||
a1 = -q / sys->L[c];
|
||||
for (i = 0; i < ngconn; i++) {
|
||||
wsys->r[i] = a1 * sys->F1[p1 + i];
|
||||
}
|
||||
|
||||
sys->q[c] -= q;
|
||||
a1 = sys->q[c] / sys->L[c];
|
||||
for (w = 0; w < nw; w++) {
|
||||
wsys->r[ngconn + w] = a1*wsys->F1[wp1 + w] +
|
||||
WI[wp1 + w] * wdp[wp1 + w];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_compute_press_flux(int nc, const int *pconn, const int *conn,
|
||||
const double *gpress,
|
||||
const double *Binv, const struct hybsys *sys,
|
||||
const double *pi, double *press, double *flux,
|
||||
double *work)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, i, nconn, p1, p2;
|
||||
double a1, a2;
|
||||
|
||||
MAT_SIZE_T incx, incy, nrows, ncols, lda;
|
||||
|
||||
incx = incy = 1;
|
||||
|
||||
p2 = 0;
|
||||
a1 = 1.0;
|
||||
a2 = 0.0;
|
||||
for (c = 0; c < nc; c++) {
|
||||
p1 = pconn[c + 0];
|
||||
nconn = pconn[c + 1] - p1;
|
||||
|
||||
/* Serialise interface pressures for cell */
|
||||
for (i = 0; i < nconn; i++) {
|
||||
/* work[i] = pi[conn[p1 + i]] - gpress[p1 + i]; */
|
||||
work[i] = pi[conn[p1 + i]];
|
||||
}
|
||||
|
||||
nrows = ncols = lda = nconn;
|
||||
|
||||
/* Solve Lp = g - F2*f + F2*pi (for cell pressure) */
|
||||
press[c] = sys->q[c]; /* src[c]; */
|
||||
press[c] += ddot_(&nrows, &sys->F2[p1], &incx, work, &incy);
|
||||
press[c] /= sys->L[c];
|
||||
|
||||
/* Form rhs of system B*v = f + C*p - D*pi */
|
||||
for (i = 0; i < nconn; i++) {
|
||||
work[i] = gpress[p1 + i] + press[c] - work[i];
|
||||
}
|
||||
|
||||
/* Solve resulting system (-> half face fluxes) */
|
||||
dgemv_("No Transpose", &nrows, &ncols,
|
||||
&a1, &Binv[p2], &lda, work, &incx,
|
||||
&a2, &flux[p1], &incy);
|
||||
|
||||
p2 += nconn * nconn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_compute_press_flux_well(int nc, const int *pgconn, int nf,
|
||||
int nw, const int *pwconn, const int *wconn,
|
||||
const double *Binv,
|
||||
const double *WI,
|
||||
const double *wdp,
|
||||
const struct hybsys *sys,
|
||||
const struct hybsys_well *wsys,
|
||||
const double *pi,
|
||||
double *cpress, double *cflux,
|
||||
double *wpress, double *wflux,
|
||||
double *work)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, w, wg, perf;
|
||||
int ngconn, nwconn;
|
||||
size_t gp1, gp2, wp1;
|
||||
|
||||
MAT_SIZE_T mm, nn, incx, incy, ld;
|
||||
|
||||
double dcp, one;
|
||||
|
||||
gp2 = 0;
|
||||
for (c = 0; c < nc; c++) {
|
||||
ngconn = pgconn[c + 1] - pgconn[c];
|
||||
nwconn = pwconn[c + 1] - pwconn[c];
|
||||
|
||||
if (nwconn > 0) {
|
||||
dcp = 0.0;
|
||||
|
||||
gp1 = pgconn[c];
|
||||
wp1 = pwconn[c];
|
||||
|
||||
for (w = 0; w < nwconn; w++) {
|
||||
wg = wconn[2*(wp1 + w) + 0];
|
||||
work[w] = pi[nf + wg];
|
||||
}
|
||||
|
||||
mm = nwconn; incx = incy = 1;
|
||||
dcp = ddot_(&mm, &wsys->F2[wp1], &incx, work, &incy);
|
||||
dcp /= sys->L[c];
|
||||
|
||||
cpress[c] += dcp;
|
||||
|
||||
mm = nn = ld = ngconn;
|
||||
one = 1.0;
|
||||
dgemv_("No Transpose", &mm, &nn,
|
||||
&dcp, &Binv[gp2], &ld, sys->one , &incx,
|
||||
&one, &cflux[gp1], &incy);
|
||||
|
||||
for (w = 0; w < nwconn; w++) {
|
||||
perf = wconn[2*(wp1 + w) + 1];
|
||||
|
||||
wflux[perf] = wdp[wp1 + w] + cpress[c] - work[w];
|
||||
wflux[perf] *= - WI [wp1 + w]; /* Sign => positive inj. */
|
||||
}
|
||||
}
|
||||
|
||||
gp2 += ngconn + ngconn;
|
||||
}
|
||||
|
||||
/* Assign well BHP from linsolve output */
|
||||
memcpy(wpress, pi + nf, nw * sizeof *wpress);
|
||||
}
|
|
@ -1,619 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_HYBSYS_HEADER_INCLUDED
|
||||
#define OPM_HYBSYS_HEADER_INCLUDED
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Routines and data structures to manage local contributions to a
|
||||
* global system of simultaneous linear equations arising from a
|
||||
* Schur complement reduction of an original block system.
|
||||
*
|
||||
* Specifically, these data structures and related routines compute
|
||||
* and store the elemental (cell-based) contributions of the Schur
|
||||
* complement reduction of the block system of simultaneous linear
|
||||
* equations
|
||||
* \f[
|
||||
* \begin{pmatrix}
|
||||
* B & C_1 & D \\
|
||||
* C_2^\mathsf{T} & P & 0 \\
|
||||
* D^\mathsf{T} & 0 & 0
|
||||
* \end{pmatrix}
|
||||
* \begin{pmatrix}
|
||||
* v \\ -p \\ \pi
|
||||
* \end{pmatrix} = \begin{pmatrix}
|
||||
* G \\ g \\ h
|
||||
* \end{pmatrix}
|
||||
* \f]
|
||||
* in which \f$G\f$ accounts for effects of gravity. The traditional
|
||||
* Schurcomplement reduction (block Gaussian elimination) then produces
|
||||
* the equivalent system of simultaneous linear equations
|
||||
* \f[
|
||||
* \begin{pmatrix}
|
||||
* B & C_1 & D \\
|
||||
* 0 & -L & -F_2 \\
|
||||
* 0 & 0 & A
|
||||
* \end{pmatrix}
|
||||
* \begin{pmatrix}
|
||||
* v \\ -p \\ \pi
|
||||
* \end{pmatrix} = \begin{pmatrix}
|
||||
* G \\ g - C_2^\mathsf{T}B^{-1}G \\ b
|
||||
* \end{pmatrix}.
|
||||
* \f]
|
||||
* Here, the matrix \f$A\f$ and the right hand side vector \f$b\f$ are given
|
||||
* by
|
||||
* \f[
|
||||
* \begin{aligned}
|
||||
* A &= D^\mathsf{T}B^{-1}D - F_1^\mathsf{T}L^{-1}F_2 \\
|
||||
* b &= D^\mathsf{T}B^{-1}G +
|
||||
* F_1^\mathsf{T}L^{-1}(g - C_2^\mathsf{T}B^{-1}G) - h,
|
||||
* \end{aligned}
|
||||
* \f]
|
||||
* and the component matrices \f$F_1\f$, \f$F_2\f$, and \f$L\f$ are given
|
||||
* by
|
||||
* \f[
|
||||
* F_1 = C_1^\mathsf{T}B^{-1}D, \quad
|
||||
* F_2 = C_2^\mathsf{T}B^{-1}D, \quad
|
||||
* L = C_2^\mathsf{T}B^{-1}C_1 - P.
|
||||
* \f]
|
||||
* In the case of incompressible flow, the matrix \f$C_2\f$ is the same
|
||||
* as \f$C_1\f$ and \f$P=0\f$ whence the coefficient matrix \f$A\f$ of
|
||||
* the Schur complement system \f$A\pi=b\f$ is symmetric.
|
||||
*
|
||||
* A great deal of simplification arises from the simple characterisation
|
||||
* of the \f$C_1\f$ and \f$D\f$ matrices. Specifically,
|
||||
* \f[
|
||||
* (C_1)_{ij} = \begin{cases}
|
||||
* 1, &\quad i\in\{\mathit{pconn}_j, \dots, \mathit{pconn}_{j+1}-1\}, \\
|
||||
* 0, &\quad \text{otherwise},
|
||||
* \end{cases}
|
||||
* \f]
|
||||
* and
|
||||
* \f[
|
||||
* (D)_{ij} = \begin{cases}
|
||||
* 1, &\quad \mathit{conn}_i = j, \\
|
||||
* 0, &\quad \text{otherwise}.
|
||||
* \end{cases}
|
||||
* \f]
|
||||
* When viewed in the context of a single cell, then the \f$D\f$ matrix
|
||||
* is, effectively, the identity with the \f$\mathit{conn}\f$ array
|
||||
* simply affecting a finite-element style redistribution (assembly)
|
||||
* of the local contributions. This module leverages that property
|
||||
* extensively.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Elemental contributions (from cells) to block system of simultaneous
|
||||
* linear equations. Mixes quantities of single cells (@c r,
|
||||
* @c S and @c one) with those that cater to all cells (@c L,
|
||||
* @c q, @c F1, and--possibly--@c F2).
|
||||
*/
|
||||
struct hybsys {
|
||||
double *L; /**< \f$C_2^\mathsf{T}B^{-1}C - P\f$, all cells */
|
||||
double *q; /**< \f$g - F_2 G\f$, all cells */
|
||||
double *F1; /**< \f$C_1^\mathsf{T}B^{-1}\f$, all cells */
|
||||
double *F2; /**< \f$C_2^\mathsf{T}B^{-1}\f$, all cells*/
|
||||
double *r; /**< Data buffer for system right-hand side, single cell */
|
||||
double *S; /**< Data buffer system matrix, single cell */
|
||||
double *one; /**< \f$(1,1,\dots,1)^\mathsf{T}\f$, single cell */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Elemental contributions (from wells) to block system of simultaneous
|
||||
* linear equations. Mixes quantities of single cell connections (@c r,
|
||||
* @c w2r, @c r2w, and @c w2w) and those that pertain to all well
|
||||
* connections (perforations) in concert (@c F1 and @c F2).
|
||||
*/
|
||||
struct hybsys_well {
|
||||
double *F1; /**< \f$C_1^\mathsf{T}B^{-1}\f$, all connections. */
|
||||
double *F2; /**< \f$C_2^\mathsf{T}B^{-1}\f$, all connections. */
|
||||
double *r; /**< Data buffer for system right-hand side, single cell. */
|
||||
|
||||
double *w2r; /**< Well-to-reservoir connection strength, single cell. */
|
||||
double *r2w; /**< Reservoir-to-well connection strength, single cell. */
|
||||
double *w2w; /**< Aggregate well-to-well connection strength. */
|
||||
|
||||
double *data; /**< Linear storage array. Structure undisclosed. */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a hybrid system management structure suitable for discretising
|
||||
* a symmetric (i.e., incompressible) flow problem on a grid model of
|
||||
* given size.
|
||||
*
|
||||
* @param[in] max_nconn Maximum number of single cell faces.
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] nconn_tot Aggregate number of cell faces for all cells.
|
||||
* @return Fully formed hybrid system management structure if successful or
|
||||
* @c NULL in case of allocation failure.
|
||||
*/
|
||||
struct hybsys *
|
||||
hybsys_allocate_symm(int max_nconn, int nc, int nconn_tot);
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a hybrid system management structure suitable for discretising
|
||||
* an unsymmetric (i.e., compressible) flow problem on a grid model of
|
||||
* given size.
|
||||
*
|
||||
* @param[in] max_nconn Maximum number of single cell faces.
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] nconn_tot Aggregate number of cell faces for all cells.
|
||||
* @return Fully formed hybrid system management structure if successful or
|
||||
* @c NULL in case of allocation failure.
|
||||
*/
|
||||
struct hybsys *
|
||||
hybsys_allocate_unsymm(int max_nconn, int nc, int nconn_tot);
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a hybrid system management structure suitable for discretising
|
||||
* an incompressible (i.e., symmetric) well flow problem on a grid model
|
||||
* of given size.
|
||||
*
|
||||
* @param[in] max_nconn Maximum number of single cell faces.
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] cwpos Indirection array that defines each cell's
|
||||
* connecting wells. Values typically computed
|
||||
* using function derive_cell_wells().
|
||||
* @return Fully formed hybrid system management structure if successful or
|
||||
* @c NULL in case of allocation failure.
|
||||
*/
|
||||
struct hybsys_well *
|
||||
hybsys_well_allocate_symm(int max_nconn, int nc, int *cwpos);
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a hybrid system management structure suitable for discretising
|
||||
* a compressible (i.e., unsymmetric) well flow problem on a grid model
|
||||
* of given size.
|
||||
*
|
||||
* @param[in] max_nconn Maximum number of single cell faces.
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] cwpos Indirection array that defines each cell's
|
||||
* connecting wells. Values typically computed
|
||||
* using function derive_cell_wells().
|
||||
* @return Fully formed hybrid system management structure if successful
|
||||
* or @c NULL in case of allocation failure.
|
||||
*/
|
||||
struct hybsys_well *
|
||||
hybsys_well_allocate_unsymm(int max_nconn, int nc, int *cwpos);
|
||||
|
||||
|
||||
/**
|
||||
* Dispose of memory resources previously obtained through one of the
|
||||
* allocation functions, hybsys_allocate_symm() or
|
||||
* hybsys_allocate_unsymm().
|
||||
*
|
||||
* Following a call to hybsys_free(), the input pointer is no longer
|
||||
* valid. <CODE>hybsys_free(NULL)</CODE> does nothing.
|
||||
*
|
||||
* @param[in,out] sys Previously allocated hybrid system management
|
||||
* structure (or @c NULL).
|
||||
*/
|
||||
void
|
||||
hybsys_free(struct hybsys *sys);
|
||||
|
||||
/**
|
||||
* Dispose of memory resources previously obtained through one of the
|
||||
* allocation functions, hybsys_well_allocate_symm() or
|
||||
* hybsys_well_allocate_unsymm().
|
||||
*
|
||||
* Following a call to hybsys_well_free(), the input pointer is
|
||||
* no longer valid. <CODE>hybsys_well_free(NULL)</CODE> does nothing.
|
||||
*
|
||||
* @param[in,out] wsys Previously allocated hybrid system management
|
||||
* structure (or @c NULL).
|
||||
*/
|
||||
void
|
||||
hybsys_well_free(struct hybsys_well *wsys);
|
||||
|
||||
|
||||
/**
|
||||
* Perform post-construction dynamic initialisation of system
|
||||
* structure obtained from function hybsys_allocate_symm() or
|
||||
* hybsys_allocate_unsymm().
|
||||
*
|
||||
* @param[in] max_nconn Maximum number of single cell faces.
|
||||
* Must coincide with the equally named
|
||||
* parameter of functions hybsys_allocate_symm()
|
||||
* or hybsys_allocate_unsymm().
|
||||
* @param[in,out] sys Previously allocated hybrid system management
|
||||
* structure.
|
||||
*/
|
||||
void
|
||||
hybsys_init(int max_nconn, struct hybsys *sys);
|
||||
|
||||
/**
|
||||
* Compute elemental (per-cell) contributions to symmetric Schur
|
||||
* system of simultaneous linear equations.
|
||||
*
|
||||
* This function assumes that the coefficient matrix of the hybrid
|
||||
* system of linear equations is that of the introduction with the
|
||||
* additional provision that \f$C_1=C_2=C\f$ and that \f$P=0\f$.
|
||||
* In other words, this function assumes that the coefficient matrix
|
||||
* is of the form
|
||||
* \f[
|
||||
* \begin{pmatrix}
|
||||
* B & C & D \\
|
||||
* C^\mathsf{T} & 0 & 0 \\
|
||||
* D^\mathsf{T} & 0 & 0
|
||||
* \end{pmatrix}.
|
||||
* \f]
|
||||
* This function fills the @c F1 and @c L fields of the management
|
||||
* structure.
|
||||
*
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] pconn Cell-to-face start pointers.
|
||||
* @param[in] Binv Inverse inner product results, usually
|
||||
* computed using mim_ip_simple_all() and
|
||||
* mim_ip_mobility_update().
|
||||
* @param[in,out] sys Hybrid system management structure allocated
|
||||
* using hybsys_allocate_symm() and initialised
|
||||
* using hybsys_init().
|
||||
*/
|
||||
void
|
||||
hybsys_schur_comp_symm(int nc, const int *pconn,
|
||||
const double *Binv, struct hybsys *sys);
|
||||
|
||||
|
||||
/**
|
||||
* Compute elemental (per-cell) contributions to unsymmetric Schur
|
||||
* system of simultaneous linear equations.
|
||||
*
|
||||
* This function assumes that the coefficient matrix of the hybrid
|
||||
* system of linear equations is that of the introduction with the
|
||||
* additional provision that \f$C_2=C_1-V\f$. In other words, this
|
||||
* function assumes that the coefficient matrix is of the form
|
||||
* \f[
|
||||
* \begin{pmatrix}
|
||||
* B & C & D \\
|
||||
* (C-V)^\mathsf{T} & P & 0 \\
|
||||
* D^\mathsf{T} & 0 & 0
|
||||
* \end{pmatrix}.
|
||||
* \f]
|
||||
* This matrix arises in the ``\f$v^2\f$'' phase compressibility
|
||||
* formulation of the compressible black-oil model. This function
|
||||
* fills the @c F1, @c F2 and @c L fields of the management structure.
|
||||
*
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] pconn Cell-to-face start pointers.
|
||||
* @param[in] Binv Inverse inner product results, usually
|
||||
* computed using mim_ip_simple_all() and
|
||||
* mim_ip_mobility_update().
|
||||
* @param[in] BIV \f$B^{-1}v\f$ in which \f$v\f$ is the flux
|
||||
* field of a previous time step or non-linear
|
||||
* iteration.
|
||||
* @param[in] P Per cell compressible accumulation term. One
|
||||
* scalar per cell.
|
||||
* @param[in,out] sys Hybrid system management structure allocated
|
||||
* using hybsys_allocate_symm() and initialised
|
||||
* using hybsys_init().
|
||||
*/
|
||||
void
|
||||
hybsys_schur_comp_unsymm(int nc, const int *pconn,
|
||||
const double *Binv, const double *BIV,
|
||||
const double *P, struct hybsys *sys);
|
||||
|
||||
|
||||
/**
|
||||
* Compute elemental (per-cell) contributions to unsymmetric Schur
|
||||
* system of simultaneous linear equations.
|
||||
*
|
||||
* This function assumes that the coefficient matrix of the hybrid
|
||||
* system of linear equations is that of the introduction with no
|
||||
* additional provisions. In other words, this
|
||||
* function assumes that the coefficient matrix is of the form
|
||||
* \f[
|
||||
* \begin{pmatrix}
|
||||
* B & C_1 & D \\
|
||||
* C_2^\mathsf{T} & P & 0 \\
|
||||
* D^\mathsf{T} & 0 & 0
|
||||
* \end{pmatrix}.
|
||||
* \f]
|
||||
* This function fills the @c F1, @c F2 and @c L fields of
|
||||
* the management structure.
|
||||
*
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] pconn Cell-to-face start pointers.
|
||||
* @param[in] Binv Inverse inner product results, usually
|
||||
* computed using mim_ip_simple_all() and
|
||||
* mim_ip_mobility_update().
|
||||
* @param[in] C2 Explicit representation of the \f$C_2\f$
|
||||
* matrix as a linear array. Assumed to only
|
||||
* contain the (structurally) non-zero matrix
|
||||
* elements (that correspond to the non-zero
|
||||
* structure of \f$C_1\f$).
|
||||
* @param[in] P Per cell compressible accumulation term. One
|
||||
* scalar per cell.
|
||||
* @param[in,out] sys Hybrid system management structure allocated
|
||||
* using hybsys_allocate_symm() and initialised
|
||||
* using hybsys_init().
|
||||
*/
|
||||
void
|
||||
hybsys_schur_comp_gen(int nc, const int *pconn,
|
||||
const double *Binv, const double *C2,
|
||||
const double *P, struct hybsys *sys);
|
||||
|
||||
/**
|
||||
* Compute elemental contributions to global, symmetric system of
|
||||
* simultaneous linear equations from cell<->well connections.
|
||||
*
|
||||
* Specifically, for a well @c w intersecting a cell @c c, this function
|
||||
* computes the elemental contributions
|
||||
* \f[
|
||||
* (F_1)_{wc} = C_{wc}^\mathsf{T} B_{wc}^{-1} D_{wc} = \mathit{WI}_{wc}
|
||||
* \f]
|
||||
* and
|
||||
* \f[
|
||||
* L_{wc} = C_{wc}^\mathsf{T} B_{wc}^{-1} C_{wc} = \mathit{WI}_{wc}
|
||||
* \f]
|
||||
* and incorporates the contributions into the global system quantities
|
||||
* as appropriate.
|
||||
*
|
||||
* This function modifies <CODE>sys->L</CODE> and <CODE>wsys->F1</CODE>.
|
||||
*
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] cwpos Indirection array that defines each cell's
|
||||
* connecting wells. Values typically computed
|
||||
* using function derive_cell_wells().
|
||||
* @param[in] WI Peaceman well connection indices. Array of
|
||||
* size <CODE>cwpos[nc]</CODE>. Must incorporate
|
||||
* effects of multiple phases (i.e., total mobility)
|
||||
* if applicable.
|
||||
* @param[in,out] sys Hybrid system management structure allocated
|
||||
* using hybsys_allocate_symm() and initialised
|
||||
* using hybsys_init() and/or filled using function
|
||||
* hybsys_schur_comp_symm().
|
||||
* @param[in,out] wsys Hybrid well-system management structure obtained
|
||||
* from function hybsys_well_allocate_symm().
|
||||
*/
|
||||
void
|
||||
hybsys_well_schur_comp_symm(int nc, const int *cwpos,
|
||||
double *WI,
|
||||
struct hybsys *sys,
|
||||
struct hybsys_well *wsys);
|
||||
|
||||
/**
|
||||
* Compute final (symmetric) Schur complement contributions to
|
||||
* global system of simultaneous linear equations.
|
||||
*
|
||||
* This function forms the coefficient matrix
|
||||
* \f[
|
||||
* S_c = D^\mathsf{T}B_c^{-1}D - F_c^\mathsf{T}L_c^{-1}F_c
|
||||
* \f]
|
||||
* and similar right-hand side \f$r_c\f$ elemental contributions.
|
||||
* These values must be subsequently assembled into the global system
|
||||
* using function hybsys_global_assemble_cell() after imposing any
|
||||
* applicable boundary conditions.
|
||||
*
|
||||
* This function overwrites the fields @c S and @c r of the hybrid system
|
||||
* structure.
|
||||
*
|
||||
* @param[in] c Cell for which to compute local contributions.
|
||||
* @param[in] nconn Number of connections (faces) of cell @c c.
|
||||
* @param[in] p1 Start address (into @c gpress) of the gravity
|
||||
* contributions of cell @c c.
|
||||
* @param[in] p2 Start address (into @c Binv) of the inverse
|
||||
* inner product of cell @c c.
|
||||
* @param[in] gpress Gravity contributions of all cells. Must
|
||||
* include effects of multiple phases if applicable.
|
||||
* @param[in] src Explicit source terms for all cells.
|
||||
* @param[in] Binv Inverse inner products for all cells. Must
|
||||
* include effects of multiple phases if applicable.
|
||||
* @param[in,out] sys Hybrid system management structure allocated
|
||||
* using hybsys_allocate_symm() and initialised
|
||||
* using hybsys_init() and/or filled using function
|
||||
* hybsys_schur_comp_symm() and
|
||||
* hybsys_well_schur_comp_symm() if applicable.
|
||||
*/
|
||||
void
|
||||
hybsys_cellcontrib_symm(int c, int nconn, int p1, int p2,
|
||||
const double *gpress, const double *src,
|
||||
const double *Binv, struct hybsys *sys);
|
||||
|
||||
|
||||
/**
|
||||
* Compute final (non-symmetric) Schur complement contributions to
|
||||
* global system of simultaneous linear equations.
|
||||
*
|
||||
* This function forms the coefficient matrix
|
||||
* \f[
|
||||
* S_c = D^\mathsf{T}B_c^{-1}D - (F_1)_c^\mathsf{T}L_c^{-1}(F_2)_c
|
||||
* \f]
|
||||
* and similar right-hand side \f$r_c\f$ elemental contributions.
|
||||
* These values must be subsequently assembled into the global system
|
||||
* using function hybsys_global_assemble_cell() after imposing any
|
||||
* applicable boundary conditions.
|
||||
*
|
||||
* This function overwrites the fields @c S and @c r of the hybrid system
|
||||
* structure.
|
||||
*
|
||||
* @param[in] c Cell for which to compute local contributions.
|
||||
* @param[in] nconn Number of connections (faces) of cell @c c.
|
||||
* @param[in] p1 Start address (into @c gpress) of the gravity
|
||||
* contributions of cell @c c.
|
||||
* @param[in] p2 Start address (into @c Binv) of the inverse
|
||||
* inner product of cell @c c.
|
||||
* @param[in] gpress Gravity contributions of all cells. Must
|
||||
* include effects of multiple phases if applicable.
|
||||
* @param[in] src Explicit source terms for all cells.
|
||||
* @param[in] Binv Inverse inner products for all cells. Must
|
||||
* include effects of multiple phases if applicable.
|
||||
* @param[in,out] sys Hybrid system management structure allocated
|
||||
* using hybsys_allocate_symm() and initialised
|
||||
* using hybsys_init() and/or filled using functions
|
||||
* hybsys_schur_comp_unsymm() or hybsys_schur_comp_gen().
|
||||
*/
|
||||
void
|
||||
hybsys_cellcontrib_unsymm(int c, int nconn, int p1, int p2,
|
||||
const double *gpress, const double *src,
|
||||
const double *Binv, struct hybsys *sys);
|
||||
|
||||
|
||||
/**
|
||||
* Form elemental direct contributions to global system of simultaneous linear
|
||||
* equations from cell<->well interactions.
|
||||
*
|
||||
* Plays a role similar to function hybsys_cellcontrib_symm(), but for wells.
|
||||
*
|
||||
* @param[in] c Cell for which to compute cell<->well Schur complement
|
||||
* @param[in] ngconn Number of inter-cell connections (faces) of cell @c c.
|
||||
* @param[in] p1 Start index (into <CODE>sys->F1</CODE>) of cell @c c.
|
||||
* @param[in] cwpos Indirection array that defines each cell's connecting
|
||||
* wells. Must coincide with equally named parameter of
|
||||
* function hybsys_well_schur_comp_symm().
|
||||
* @param[in] WI Peaceman well connection indices. Array of
|
||||
* size <CODE>pwconn[nc]</CODE>. Must coincide with
|
||||
* equally named parameter of contribution function
|
||||
* hybsys_well_schur_comp_symm().
|
||||
* @param[in] wdp Well connection gravity pressure adjustments.
|
||||
* One scalar for each well connection in an array of size
|
||||
* <CODE>pwconn[nc]</CODE>.
|
||||
* @param[in,out] sys Hybrid system management structure filled using
|
||||
* functions hybsys_schur_comp_unsymm() or
|
||||
* hybsys_schur_comp_gen().
|
||||
* @param[in,out] wsys Hybrid well-system management structure filled using
|
||||
* function hybsys_well_schur_comp_symm().
|
||||
*/
|
||||
void
|
||||
hybsys_well_cellcontrib_symm(int c, int ngconn, int p1,
|
||||
const int *cwpos,
|
||||
const double *WI, const double *wdp,
|
||||
struct hybsys *sys, struct hybsys_well *wsys);
|
||||
|
||||
|
||||
/**
|
||||
* Recover cell pressures and outward fluxes (with respect to cells--i.e., the
|
||||
* ``half-face fluxes'') through back substitution after solving a symmetric
|
||||
* (i.e., incompressible) Schur complement system of simultaneous linear
|
||||
* equations.
|
||||
*
|
||||
* Specifically, given the solution \f$\pi\f$ to the global system of
|
||||
* simultaneous linear equations, \f$A\pi=b\f$, that arises as a result of the
|
||||
* Schur complement analysis, this function recovers the cell pressures \f$p\f$
|
||||
* and outward fluxes \f$v\f$ defined by
|
||||
* \f[
|
||||
* \begin{aligned}
|
||||
* Lp &= g - C_2^\mathsf{T}B^{-1}G + F_2\pi \\
|
||||
* Bv &= G + C_1p - D\pi
|
||||
* \end{aligned}.
|
||||
* \f]
|
||||
*
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] pconn Cell-to-face start pointers.
|
||||
* @param[in] conn Cell-to-face mapping.
|
||||
* @param[in] gpress Gravity contributions of all cells. Must coincide with
|
||||
* equally named parameter in calls to cell contribution
|
||||
* functions such as hybsys_cellcontrib_symm().
|
||||
* @param[in] Binv Inverse inner products for all cells. Must coincide
|
||||
* with equally named parameter in calls to contribution
|
||||
* functions such as hybsys_cellcontrib_symm().
|
||||
* @param[in] sys Hybrid system management structure coinciding with
|
||||
* equally named parameter in contribution functions such
|
||||
* as hybsys_cellcontrib_symm() or
|
||||
* hybsys_cellcontrib_unsymm().
|
||||
* @param[in] pi Solution (interface/contact pressure) obtained from
|
||||
* solving the global system \f$A\pi = b\f$.
|
||||
* @param[out] press Cell pressures, \f$p\f$. Array of size @c nc.
|
||||
* @param[out] flux Outward interface fluxes, \f$v\f$. Array of size
|
||||
* <CODE>pconn[nc]</CODE>.
|
||||
* @param[in,out] work Scratch array for temporary results. Array of size at
|
||||
* least \f$\max_c \{ \mathit{pconn}_{c + 1}
|
||||
* - \mathit{pconn}_c \} \f$.
|
||||
*/
|
||||
void
|
||||
hybsys_compute_press_flux(int nc, const int *pconn, const int *conn,
|
||||
const double *gpress,
|
||||
const double *Binv, const struct hybsys *sys,
|
||||
const double *pi, double *press, double *flux,
|
||||
double *work);
|
||||
|
||||
|
||||
/**
|
||||
* Recover well pressures (i.e., bottom-hole pressure values) and well
|
||||
* connection (perforation) fluxes.
|
||||
*
|
||||
* Specifically, this function performs the same role (i.e., back-substitution)
|
||||
* for wells as function hybsys_compute_press_flux() does for grid cells and
|
||||
* grid contacts (interfaces).
|
||||
*
|
||||
* @param[in] nc Total number of grid cells.
|
||||
* @param[in] pgconn Cell-to-face start pointers.
|
||||
* @param[in] nf Total number of grid faces.
|
||||
* @param[in] nw Total number of wells.
|
||||
* @param[in] pwconn Cell-to-well start pointers. If <CODE>nw > 0</CODE>,
|
||||
* then this parameter must coincide with the @c cwpos
|
||||
* array used in call to hybsys_well_schur_comp_symm().
|
||||
* @param[in] wconn Cell-to-well mapping.
|
||||
* @param[in] Binv Inverse inner products for all cells. Must coincide
|
||||
* with equally named parameter in calls to contribution
|
||||
* functions such as hybsys_well_cellcontrib_symm().
|
||||
* @param[in] WI Peaceman well connection indices. Array of
|
||||
* size <CODE>pwconn[nc]</CODE>. Must coincide with
|
||||
* equally named parameter of contribution function
|
||||
* hybsys_well_cellcontrib_symm().
|
||||
* @param[in] wdp Well connection gravity pressure adjustments.
|
||||
* @param[in] sys Hybrid system management structure coinciding with
|
||||
* equally named parameter in contribution functions such
|
||||
* as hybsys_cellcontrib_symm() and
|
||||
* hybsys_well_cellcontrib_symm().
|
||||
* @param[in] wsys Hybrid well-system management structure. Must coincide
|
||||
* with equally named paramter of contribution function
|
||||
* hybsys_well_cellcontrib_symm().
|
||||
* @param[in] pi Solution (interface/contact pressure and well BHPs)
|
||||
* obtained from solving the global system \f$A\pi = b\f$.
|
||||
* @param[in] cpress Cell pressures, \f$p\f$, obtained from a previous call
|
||||
* to function hybsys_compute_press_flux().
|
||||
* @param[in] cflux Outward fluxes, \f$v\f$, obtained from a previous call
|
||||
* to function hybsys_compute_press_flux().
|
||||
* @param[out] wpress Well (i.e., bottom-hole) pressures. Array of size
|
||||
* @c nw.
|
||||
* @param[out] wflux Well connection (perforation) fluxes. Array of size
|
||||
* <CODE>pwconn[nw]</CODE>.
|
||||
* @param[in,out] work Scratch array for storing intermediate results. Array
|
||||
* of size at least \f$\max_w \{ \mathit{pwconn}_{w + 1}
|
||||
* - \mathit{pwconn}_w\}\f$.
|
||||
*/
|
||||
void
|
||||
hybsys_compute_press_flux_well(int nc, const int *pgconn, int nf,
|
||||
int nw, const int *pwconn, const int *wconn,
|
||||
const double *Binv,
|
||||
const double *WI,
|
||||
const double *wdp,
|
||||
const struct hybsys *sys,
|
||||
const struct hybsys_well *wsys,
|
||||
const double *pi,
|
||||
double *cpress, double *cflux,
|
||||
double *wpress, double *wflux,
|
||||
double *work);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_HYBSYS_HEADER_INCLUDED */
|
|
@ -1,418 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* ---> Layering violation! */
|
||||
#include <opm/core/pressure/msmfem/hash_set.h>
|
||||
/* <--- Layering violation! */
|
||||
|
||||
#include <opm/core/pressure/mimetic/hybsys_global.h>
|
||||
|
||||
|
||||
#if defined MAX
|
||||
#undef MAX
|
||||
#endif
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
|
||||
/* Release memory resources for each individual well's DOF set (and
|
||||
* the aggregate well DOF set structure). */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
deallocate_well_dofset(size_t nw, struct hash_set **wia)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t w;
|
||||
|
||||
if (wia != NULL) {
|
||||
for (w = 0; w < nw; w++) {
|
||||
hash_set_deallocate(wia[w]);
|
||||
}
|
||||
}
|
||||
|
||||
free(wia);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate DOF set for each well.
|
||||
*
|
||||
* Returns fully created vector of W->number_of_wells DOF sets if
|
||||
* successful and NULL otherwise. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static struct hash_set **
|
||||
allocate_well_dofset(struct UnstructuredGrid *G, well_t *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int w, i, c, ok;
|
||||
int set_size;
|
||||
struct hash_set **wia;
|
||||
|
||||
wia = malloc(W->number_of_wells * sizeof *wia);
|
||||
|
||||
if (wia != NULL) {
|
||||
ok = 1;
|
||||
for (w = 0; ok && (w < W->number_of_wells); w++) {
|
||||
set_size = 1; /* Approximate expected set size */
|
||||
|
||||
for (i = W->well_connpos[w]; i < W->well_connpos[w + 1]; i++) {
|
||||
c = W->well_cells[i];
|
||||
|
||||
set_size += G->cell_facepos[c + 1] - G->cell_facepos[c];
|
||||
}
|
||||
|
||||
wia[w] = hash_set_allocate(set_size);
|
||||
|
||||
ok = wia[w] != NULL;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
deallocate_well_dofset(w, wia);
|
||||
wia = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return wia;
|
||||
}
|
||||
|
||||
|
||||
/* Count number of coefficient matrix connections (non-zeros) per row
|
||||
* from grid contributions. Add self connections for all rows. Well
|
||||
* connection counts will be overwritten in count_conn_per_row_well()
|
||||
* if applicable. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
count_conn_per_row_grid(struct UnstructuredGrid *G, struct CSRMatrix *A)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, nc, *ia, *ja;
|
||||
size_t m;
|
||||
|
||||
nc = G->number_of_cells;
|
||||
ia = G->cell_facepos;
|
||||
ja = G->cell_faces;
|
||||
|
||||
A->ia[0] = 0;
|
||||
for (m = 0; m < A->m; m++) {
|
||||
A->ia[m + 1] = 1;
|
||||
}
|
||||
|
||||
for (c = 0; c < nc; c++) {
|
||||
for (; ja != G->cell_faces + ia[c + 1]; ja++) {
|
||||
A->ia[ *ja + 1 ] += ia[c + 1] - ia[c] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Count number of coefficient matrix connections per row from well
|
||||
* contributions. Increment connection count for other-to-well,
|
||||
* define count for well-to-other.
|
||||
*
|
||||
* Fails if unable to insert a DOF into a DOF set.
|
||||
*
|
||||
* Returns 1 if successful, and zero otherwise. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
count_conn_per_row_well(struct UnstructuredGrid *G, well_t *W,
|
||||
int *cwpos,
|
||||
int *cwells,
|
||||
struct hash_set **wia,
|
||||
struct CSRMatrix *A)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, w, nc, nf, nwdof, ok, i, dof, *ia, *ja, *wja;
|
||||
|
||||
size_t m;
|
||||
|
||||
nc = G->number_of_cells;
|
||||
nf = G->number_of_faces;
|
||||
ia = G->cell_facepos;
|
||||
wja = cwells;
|
||||
|
||||
ok = 1;
|
||||
for (c = 0; c < nc; c++) {
|
||||
for (; ok && (wja != cwells + 2*cwpos[c + 1]); wja += 2) {
|
||||
for ( ja = G->cell_faces + ia[c + 0];
|
||||
ok && (ja != G->cell_faces + ia[c + 1]); ja++) {
|
||||
dof = *ja;
|
||||
ok = hash_set_insert(dof, wia[*wja]) == dof;
|
||||
}
|
||||
|
||||
for (i = cwpos[c]; ok && (i < cwpos[c + 1]); i++) {
|
||||
dof = nf + cwells[2*i + 0];
|
||||
ok = hash_set_insert(dof, wia[*wja]) == dof;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
for (w = 0; w < W->number_of_wells; w++) {
|
||||
nwdof = 0;
|
||||
|
||||
for (m = 0; m < wia[w]->m; m++) {
|
||||
if ((dof = wia[w]->s[m]) >= 0) {
|
||||
A->ia[ dof + 1 ] += 1; /* face to well */
|
||||
nwdof += 1; /* well to face */
|
||||
}
|
||||
}
|
||||
|
||||
A->ia[ nf + w + 1 ] = nwdof;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/* Fill self-connections (i.e., diagonal of coefficient matrix) for
|
||||
* all DOFs. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
fill_self_connections(struct CSRMatrix *A)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t r;
|
||||
|
||||
for (r = 0; r < A->m; r++) {
|
||||
A->ja[ A->ia[r + 1] ++ ] = r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Fill self-to-other DOF connections (i.e., define 'ja') for grid. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
fill_grid_connections(struct UnstructuredGrid *G, struct CSRMatrix *A)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int c, i, j, n;
|
||||
int dof1, dof2;
|
||||
|
||||
int *ia, *ja;
|
||||
|
||||
ia = G->cell_facepos;
|
||||
ja = G->cell_faces;
|
||||
|
||||
for (c = 0; c < G->number_of_cells; c++) {
|
||||
n = ia[c + 1] - ia[c];
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
dof1 = ja[ ia[c] + i ];
|
||||
|
||||
if (dof1 >= 0) {
|
||||
for (j = (i + 1) % n; j != i; j = (j + 1) % n) {
|
||||
dof2 = ja[ ia[c] + j ];
|
||||
|
||||
if (dof2 >= 0) {
|
||||
A->ja[ A->ia[dof1 + 1] ++ ] = dof2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Fill self-to-other and other-to-self DOF connections ('ja') for wells. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
fill_well_connections(int nf, int nw,
|
||||
struct hash_set **wia,
|
||||
struct CSRMatrix *A)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int w, dof;
|
||||
size_t i;
|
||||
|
||||
for (w = 0; w < nw; w++) {
|
||||
for (i = 0; i < wia[w]->m; i++) {
|
||||
dof = wia[w]->s[i];
|
||||
|
||||
if (dof >= 0) {
|
||||
if (dof < nf) { /* Connect face to well */
|
||||
A->ja[ A->ia[ dof + 1 ] ++ ] = nf + w;
|
||||
}
|
||||
|
||||
if (dof != nf + w) {/* Connect well to dof (avoid self) */
|
||||
A->ja[ A->ia[ nf + w + 1 ] ++ ] = dof;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Define pressure system coefficient matrix sparsity structure
|
||||
* (A->ia, A->ja) from grid and well contributions. Allocate
|
||||
* coefficient matrix elements (A->sa).
|
||||
*
|
||||
* Returns fully defined CSR matrix structure if successful or NULL
|
||||
* otherwise. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct CSRMatrix *
|
||||
hybsys_define_globconn(struct UnstructuredGrid *G, well_t *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int nw, ok;
|
||||
int *cwell_pos = NULL, *cwells = NULL;
|
||||
|
||||
struct hash_set **wia;
|
||||
struct CSRMatrix *A;
|
||||
|
||||
assert (G != NULL);
|
||||
|
||||
ok = 1;
|
||||
nw = 0;
|
||||
wia = NULL;
|
||||
if (W != NULL) {
|
||||
ok = allocate_cell_wells(G->number_of_cells, W, &cwell_pos, &cwells);
|
||||
if (ok) {
|
||||
derive_cell_wells(G->number_of_cells, W, cwell_pos, cwells);
|
||||
}
|
||||
nw = ok ? W->number_of_wells : 0;
|
||||
|
||||
if (nw > 0) {
|
||||
wia = allocate_well_dofset(G, W);
|
||||
if (wia == NULL) { nw = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
A = csrmatrix_new_count_nnz(G->number_of_faces + nw);
|
||||
|
||||
if (A != NULL) {
|
||||
count_conn_per_row_grid(G, A);
|
||||
|
||||
if (nw > 0) {
|
||||
assert (nw == W->number_of_wells);
|
||||
ok = count_conn_per_row_well(G, W, cwell_pos,
|
||||
cwells, wia, A) > 0;
|
||||
} else {
|
||||
ok = 1;
|
||||
}
|
||||
|
||||
ok = ok && (csrmatrix_new_elms_pushback(A) > 0);
|
||||
|
||||
if (ok) {
|
||||
fill_self_connections(A);
|
||||
fill_grid_connections(G, A);
|
||||
fill_well_connections(G->number_of_faces, nw, wia, A);
|
||||
|
||||
csrmatrix_sortrows(A);
|
||||
} else {
|
||||
csrmatrix_delete(A);
|
||||
A = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
deallocate_well_dofset(nw, wia);
|
||||
deallocate_cell_wells(cwell_pos, cwells);
|
||||
|
||||
return A;
|
||||
}
|
||||
|
||||
|
||||
/* Assemble (hybrid) cell contributions into global system coefficient
|
||||
* matrix and right hand side. Traditional FEM assembly process.
|
||||
* Boundary conditions assumed enforced outside.
|
||||
*
|
||||
* Local coefficient matrix contributions assumed organised in row
|
||||
* major format (row index cycling the most rapidly--Fortran
|
||||
* conventions). Convention immaterial if matrix is symmetric. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_global_assemble_cell(int nconn, int *conn,
|
||||
const double *S,
|
||||
const double *r,
|
||||
struct CSRMatrix *A,
|
||||
double *b)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int il, jl; /* local */
|
||||
size_t ig, jg; /* global */
|
||||
|
||||
for (il = 0; il < nconn; il++) {
|
||||
assert ((0 <= conn[il]) && ((size_t) conn[il] < A->m));
|
||||
|
||||
ig = conn[il];
|
||||
|
||||
for (jl = 0; jl < nconn; jl++) {
|
||||
jg = csrmatrix_elm_index(ig, conn[jl], A);
|
||||
|
||||
assert ((((size_t)(A->ia[ig])) <= jg) &&
|
||||
(jg < ((size_t)(A->ia[ig + 1]))));
|
||||
|
||||
A->sa[jg] += S[il + jl*nconn]; /* Row major per cell */
|
||||
}
|
||||
|
||||
b[ig] += r[il];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hybsys_global_assemble_well_sym(int ngconn_tot,
|
||||
int ngconn, const int *gconn,
|
||||
int nwconn, const int *wconn,
|
||||
const double *r2w,
|
||||
const double *w2w,
|
||||
const double *r,
|
||||
struct CSRMatrix *A,
|
||||
double *b)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int il, wl1, wl2;
|
||||
size_t ig, wg1, wg2, jg, jw;
|
||||
|
||||
/* Global matrix contributions for this cell's wells */
|
||||
for (wl1 = 0; wl1 < nwconn; wl1++) {
|
||||
wg1 = ngconn_tot + wconn[2*wl1 + 0];
|
||||
|
||||
for (il = 0; il < ngconn; il++) {
|
||||
ig = gconn[il];
|
||||
|
||||
jw = csrmatrix_elm_index(ig, wg1, A);
|
||||
jg = csrmatrix_elm_index(wg1, ig, A);
|
||||
|
||||
A->sa[jw] += r2w[il + wl1*ngconn]; /* Row major per cell */
|
||||
A->sa[jg] += r2w[il + wl1*ngconn]; /* Symmetry */
|
||||
}
|
||||
|
||||
for (wl2 = 0; wl2 < nwconn; wl2++) {
|
||||
wg2 = ngconn_tot + wconn[2*wl2 + 0];
|
||||
|
||||
jw = csrmatrix_elm_index(wg1, wg2, A);
|
||||
|
||||
A->sa[jw] += w2w[wl1 + wl2*nwconn]; /* Row major per well */
|
||||
}
|
||||
}
|
||||
|
||||
/* Global right-hand side contributions */
|
||||
for (il = 0; il < ngconn; il++) {
|
||||
b[gconn[il]] += r[il];
|
||||
}
|
||||
|
||||
for (wl1 = 0; wl1 < nwconn; wl1++) {
|
||||
b[ngconn_tot + wconn[2*wl1 + 0]] += r[ngconn + wl1];
|
||||
}
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_HYBSYS_GLOBAL_HEADER_INCLUDED
|
||||
#define OPM_HYBSYS_GLOBAL_HEADER_INCLUDED
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Routines to assist in the formation and assembly of a global
|
||||
* system of simultaneous linear equations derived from a Schur
|
||||
* complement reduction of an original hybrid block system.
|
||||
*
|
||||
* We assume that the original block system of linear equations
|
||||
* is given by
|
||||
* \f[
|
||||
* \begin{pmatrix}
|
||||
* B & C & D \\
|
||||
* C^\mathsf{T} & 0 & 0 \\
|
||||
* D^\mathsf{T} & 0 & 0
|
||||
* \end{pmatrix}
|
||||
* \begin{pmatrix}
|
||||
* v \\ -p \\ \pi
|
||||
* \end{pmatrix} = \begin{pmatrix}
|
||||
* f \\ g \\ h
|
||||
* \end{pmatrix}
|
||||
* \f]
|
||||
* in which the block matrices \f$C\f$ and \f$D\f$ are assumed to
|
||||
* have a particularly simple structure and the matrix \f$B\f$ is
|
||||
* block diagonal.
|
||||
*
|
||||
* The Schur complement reduction process (a block Gaussian elimination)
|
||||
* then produces the following block system of simultaneous linear
|
||||
* equations
|
||||
* \f[
|
||||
* \begin{pmatrix}
|
||||
* B & C & D \\
|
||||
* 0 & -L & -F \\
|
||||
* 0 & 0 & A
|
||||
* \end{pmatrix}
|
||||
* \begin{pmatrix}
|
||||
* v \\ -p \\ \pi
|
||||
* \end{pmatrix} = \begin{pmatrix}
|
||||
* f \\ g - C^\mathsf{T}B^{-1} f \\ b
|
||||
* \end{pmatrix}
|
||||
* \f]
|
||||
* in which
|
||||
* \f[
|
||||
* \begin{aligned}
|
||||
* A &= D^\mathsf{T}B^{-1}D - F^\mathsf{T}L^{-1}F \text{ and} \\
|
||||
* b &= D^\mathsf{T}B^{-1}f + F^\mathsf{T}L^{-1} (g - C^\mathsf{T}B^{-1}f) - h.
|
||||
* \end{aligned}
|
||||
* \f] The component matrices \f$F\f$
|
||||
* and \f$L\f$ are given by
|
||||
* \f[
|
||||
* \begin{aligned}
|
||||
* L &= C^\mathsf{T} B^{-1} C \\
|
||||
* F &= C^\mathsf{T} B^{-1} D.
|
||||
* \end{aligned}
|
||||
* \f]
|
||||
* The primary degrees of freedom, \f$\pi\f$, may then be recovered
|
||||
* by solving the Schur complement system
|
||||
* \f[A\pi = b\f]
|
||||
* from which the derived quantities \f$p\f$ and \f$v\f$ may be
|
||||
* computed through a back-substitution process.
|
||||
*
|
||||
* The functions in this module assist in the creation of the sparsity
|
||||
* pattern of matrix \f$A\f$ and in the global assembling of values
|
||||
* into the matrix \f$A\f$ and the right-hand side vector \f$b\f$.
|
||||
* Specifically, function hybsys_define_globconn() builds the
|
||||
* sparsity pattern of \f$A\f$ while functions hybsys_global_assemble_cell()
|
||||
* and hybsys_global_assemble_well_sym() perform the task of
|
||||
* adding matrix and right-hand side values from local contributions.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/pressure/legacy_well.h>
|
||||
#include <opm/core/linalg/sparse_sys.h>
|
||||
|
||||
|
||||
/**
|
||||
* Construct sparse matrix capable of managing a (reduced) hybrid
|
||||
* system of simultaneous linear equations with one degree of freedom
|
||||
* for each grid interface and each well.
|
||||
*
|
||||
* The return value is suitable for use in pressure solvers based on
|
||||
* Schur-complement reductions such as the mimetic finite-difference
|
||||
* or multiscale mixed finite-element classes of discretisations. In
|
||||
* typical applications, the matrix will be cleared using function
|
||||
* csrmatrix_zero() and then filled using an assembly process similar
|
||||
* to the traditional finite-element algorithm.
|
||||
*
|
||||
* Functions hybsys_global_assemble_cell() and
|
||||
* hybsys_global_assemble_well_sys() may be used to assist such an
|
||||
* assembly process.
|
||||
*
|
||||
* @param[in] G Grid.
|
||||
* @param[in] W Well topology. @c NULL in a model without wells.
|
||||
* @return Fully formed and structurally consistent sparse matrix
|
||||
* whose number of rows (i.e., degrees-of-freedom) equals
|
||||
* the number of grid faces (<CODE>G->number_of_faces</CODE>)
|
||||
* plus the number of wells (<CODE>W->number_of_wells</CODE>).
|
||||
*/
|
||||
struct CSRMatrix *
|
||||
hybsys_define_globconn(struct UnstructuredGrid *G, well_t *W);
|
||||
|
||||
|
||||
/**
|
||||
* Assemble local contributions into global system of simultaneous
|
||||
* linear equations.
|
||||
*
|
||||
* The contributions will typically have been computed using
|
||||
* function hybsys_schur_comp_symm() and function
|
||||
* hybsys_cellcontrib_symm() or some of the related functions.
|
||||
*
|
||||
* @param[in] nconn Number of cell faces.
|
||||
* @param[in] l2g Local-to-global mapping of cell's primary
|
||||
* degrees of freedom (i.e., the faces).
|
||||
* @param[in] S Single cell local contribution to global
|
||||
* coefficient matrix. An
|
||||
* \f$\mathit{nconn}\times\mathit{nconn}\f$
|
||||
* dense matrix in column major (Fortran) order.
|
||||
* @param[in] r Single cell local contribution to global
|
||||
* system right-hand side. An
|
||||
* \f$\mathit{nconn}\times 1\f$ dense vector.
|
||||
* @param[in,out] A Global coefficient matrix (of Schur
|
||||
* complement system).
|
||||
* @param[in,out] b Global system right-hand side (of Schur
|
||||
* complement system).
|
||||
*/
|
||||
void
|
||||
hybsys_global_assemble_cell(int nconn, int *l2g,
|
||||
const double *S,
|
||||
const double *r,
|
||||
struct CSRMatrix *A,
|
||||
double *b);
|
||||
|
||||
|
||||
/**
|
||||
* Assemble local contributions from single cell's well connections
|
||||
* (perforations) into global system of simultaneous linear equations.
|
||||
*
|
||||
* This function assumes that the connection strength from cell to well
|
||||
* equals the connection strength from well to cell. In other words,
|
||||
* that the numerical values of the well contributions are symmetric.
|
||||
*
|
||||
* The contributions are typically computed using functions
|
||||
* hybsys_well_schur_comp_symm() and hybsys_well_cellcontrib_symm().
|
||||
*
|
||||
* @param[in] ngconn_tot Total number of grid connections.
|
||||
* Expected to equal
|
||||
* <CODE>G->number_of_faces</CODE>
|
||||
* when @c G is the grid used to form the
|
||||
* original matrix in
|
||||
* hybsys_define_globconn().
|
||||
* @param[in] ngconn Number of grid connections referenced by
|
||||
* given cell.
|
||||
* @param[in] gconn Actual grid connections (DOFs) referenced
|
||||
* by given cell. Pointer to @c ngconn
|
||||
* consecutive DOF indices.
|
||||
* @param[in] nwconn Number of well connections intersecting
|
||||
* given cell. Typically \f$\mathit{ngconn} = 1\f$.
|
||||
* @param[in] wconn Actual well connections (DOFs) intersecting
|
||||
* given cell. Pointer to @c nwconn consecutive
|
||||
* DOF indices.
|
||||
* @param[in] r2w Reservoir-to-well connection strengths.
|
||||
* @param[in] w2w Well-to-well-connection strenghts.
|
||||
* @param[in] r Single cell local contribution to global
|
||||
* system right-hand side. An
|
||||
* \f$(\mathit{ngconn} + \mathit{nwconn})
|
||||
* \times 1\f$ dense vector.
|
||||
* @param[in,out] A Global coefficient matrix (of Schur
|
||||
* complement system).
|
||||
* @param[in,out] b Global system right-hand side (of Schur
|
||||
* complement system).
|
||||
*/
|
||||
void
|
||||
hybsys_global_assemble_well_sym(int ngconn_tot,
|
||||
int ngconn, const int *gconn,
|
||||
int nwconn, const int *wconn,
|
||||
const double *r2w,
|
||||
const double *w2w,
|
||||
const double *r,
|
||||
struct CSRMatrix *A,
|
||||
double *b);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_HYBSYS_GLOBAL_HEADER_INCLUDED */
|
|
@ -1,626 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opm/core/pressure/msmfem/hash_set.h>
|
||||
#include <opm/core/pressure/msmfem/coarse_conn.h>
|
||||
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define MIN(a,b) (-MAX(-(a), -(b)))
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
* Data structures
|
||||
* ====================================================================== */
|
||||
|
||||
/* Individual block connection. */
|
||||
struct block_neighbour {
|
||||
int b; /* Neighbouring block */
|
||||
struct hash_set *fconns; /* Constituent connections */
|
||||
};
|
||||
|
||||
|
||||
/* Adjacency list of single block (directed graph) */
|
||||
struct block_neighbours {
|
||||
int nneigh; /* Number of neighbours. */
|
||||
int cpty; /* Neighbour capacity. */
|
||||
struct block_neighbour **neigh; /* Actual neighbours (sorted on neigh[i]->b) */
|
||||
};
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
* Operations
|
||||
* ====================================================================== */
|
||||
|
||||
|
||||
/* Relase dynamic memory resources for single block neighbour 'bn'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
block_neighbour_deallocate(struct block_neighbour *bn)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (bn != NULL) {
|
||||
hash_set_deallocate(bn->fconns);
|
||||
}
|
||||
|
||||
free(bn);
|
||||
}
|
||||
|
||||
|
||||
/* Construct empty block neighbour connection capable of holding
|
||||
* 'nconn' fine-scale connections (e.g., fine-scale interfaces).
|
||||
* The fine-scale table is not allocated unless nconn > 0. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static struct block_neighbour *
|
||||
block_neighbour_allocate(int nconn)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct block_neighbour *new;
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
if (new != NULL) {
|
||||
if (nconn > 0) {
|
||||
new->fconns = hash_set_allocate(nconn);
|
||||
|
||||
if (new->fconns != NULL) {
|
||||
new->b = INT_MIN;
|
||||
} else {
|
||||
block_neighbour_deallocate(new);
|
||||
new = NULL;
|
||||
}
|
||||
} else {
|
||||
new->b = INT_MIN;
|
||||
new->fconns = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* Insert fine-scale connection 'fconn' into block neighbour
|
||||
* connection 'bn', but only if the bn->fconns table has been allocated. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
block_neighbour_insert_fconn(int fconn, struct block_neighbour *bn)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert (bn != NULL);
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (bn->fconns != NULL) {
|
||||
ret = hash_set_insert(fconn, bn->fconns);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Relase dynamic memory resources for single-block adjacency list 'bns'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
block_neighbours_deallocate(struct block_neighbours *bns)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bns != NULL) {
|
||||
if (bns->neigh != NULL) {
|
||||
for (i = bns->nneigh - 1; i >= 0; i--) {
|
||||
block_neighbour_deallocate(bns->neigh[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(bns->neigh);
|
||||
}
|
||||
|
||||
free(bns);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate a single-block adjacency list capable of holding 'nneigh'
|
||||
* connections. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static struct block_neighbours *
|
||||
block_neighbours_allocate(int nneigh)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i;
|
||||
struct block_neighbours *new;
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
|
||||
if (new != NULL) {
|
||||
if (nneigh > 0) {
|
||||
new->neigh = malloc(nneigh * sizeof *new->neigh);
|
||||
|
||||
if (new->neigh != NULL) {
|
||||
for (i = 0; i < nneigh; i++) { new->neigh[i] = NULL; }
|
||||
new->nneigh = 0;
|
||||
new->cpty = nneigh;
|
||||
} else {
|
||||
block_neighbours_deallocate(new);
|
||||
new = NULL;
|
||||
}
|
||||
} else {
|
||||
new->nneigh = 0;
|
||||
new->cpty = 0;
|
||||
new->neigh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* Increase size of single-block adjacency list 'bns' to hold 'nneigh'
|
||||
* coarse-scale connections. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
block_neighbours_expand(int nneigh, struct block_neighbours *bns)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ret;
|
||||
struct block_neighbour **neigh;
|
||||
|
||||
assert (bns != NULL);
|
||||
|
||||
neigh = realloc(bns->neigh, nneigh * sizeof *neigh);
|
||||
|
||||
if (neigh != NULL) {
|
||||
bns->neigh = neigh;
|
||||
bns->cpty = nneigh;
|
||||
|
||||
for (ret = bns->nneigh; ret < bns->cpty; ret++) {
|
||||
bns->neigh[ret] = NULL;
|
||||
}
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Insert fine-scale connection 'fconn' into single-block adjacency
|
||||
* list 'bns' in slot corresponding to connection 'b'.
|
||||
*
|
||||
* New coarse-scale connections are assumed to hold 'expct_nconn'
|
||||
* fine-scale connections.*/
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
block_neighbours_insert_neighbour(int b, int fconn, int expct_nconn,
|
||||
struct block_neighbours *bns)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, j, p, t, nmove, ret;
|
||||
|
||||
assert (bns != NULL);
|
||||
|
||||
ret = 1;
|
||||
if ((bns->neigh == NULL) || (bns->cpty == 0)) {
|
||||
ret = block_neighbours_expand(1, bns);
|
||||
}
|
||||
|
||||
if (ret == 1) {
|
||||
/* bns->neigh points to table containing at least one slot. */
|
||||
i = 0;
|
||||
j = bns->nneigh;
|
||||
|
||||
while (i < j) {
|
||||
p = (i + j) / 2;
|
||||
|
||||
assert (bns->neigh[p] != NULL);
|
||||
|
||||
t = bns->neigh[p]->b;
|
||||
|
||||
if (t < b) { i = p + 1; }
|
||||
else if (t > b) { j = p + 0; }
|
||||
else { i = j = p; }
|
||||
}
|
||||
|
||||
if ((i < bns->nneigh) &&
|
||||
(bns->neigh[i] != NULL) && (bns->neigh[i]->b == b)) {
|
||||
ret = block_neighbour_insert_fconn(fconn, bns->neigh[i]);
|
||||
} else {
|
||||
if (bns->nneigh == bns->cpty) {
|
||||
assert (bns->cpty >= 1);
|
||||
ret = block_neighbours_expand(2 * bns->cpty, bns);
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
if (i < bns->nneigh) {
|
||||
nmove = bns->nneigh - i;
|
||||
|
||||
memmove(bns->neigh + i + 1, bns->neigh + i + 0,
|
||||
nmove * sizeof *bns->neigh);
|
||||
}
|
||||
|
||||
bns->neigh[i] = block_neighbour_allocate(expct_nconn);
|
||||
|
||||
if (bns->neigh[i] != NULL) {
|
||||
ret = block_neighbour_insert_fconn(fconn, bns->neigh[i]);
|
||||
|
||||
bns->neigh[i]->b = b;
|
||||
bns->nneigh += 1;
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Count number of (presumably) contiguously numbered blocks
|
||||
* represented by partition vector 'p'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
count_blocks(int nc, const int *p)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i, max_blk;
|
||||
|
||||
max_blk = -1;
|
||||
for (i = 0; i < nc; i++) {
|
||||
max_blk = MAX(max_blk, p[i]);
|
||||
}
|
||||
|
||||
return max_blk + 1;
|
||||
}
|
||||
|
||||
|
||||
/* Derive coarse-scale block faces from fine-scale neighbour-ship
|
||||
* definition 'neighbours' ('nfinef' connections) and partition vector
|
||||
* 'p' (representing 'nblk' coarse blocks). Inter-block faces keyed
|
||||
* off minimum block number if internal and valid block number if
|
||||
* external.
|
||||
*
|
||||
* Fine-scale constituents of each coarse face are computed if
|
||||
* 'expct_nconn' is positive, in which case 'expct_nconn' is
|
||||
* interpreted as the expected number of constituents in each coarse
|
||||
* face and used as an initial size of a hash_set.
|
||||
*
|
||||
* Return number of coarse faces if successful and -1 otherwise. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
derive_block_faces(int nfinef, int nblk, int expct_nconn,
|
||||
const int *p, const int *neighbours,
|
||||
struct block_neighbours **bns)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int f, c1, b1, c2, b2, b_in, b_out;
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
for (f = 0; (f < nfinef) && (0 <= ret); f++) {
|
||||
c1 = neighbours[2*f + 0]; b1 = (c1 >= 0) ? p[c1] : -1;
|
||||
c2 = neighbours[2*f + 1]; b2 = (c2 >= 0) ? p[c2] : -1;
|
||||
|
||||
assert ((b1 >= 0) || (b2 >= 0));
|
||||
|
||||
if ((b1 >= 0) && (b2 >= 0)) {
|
||||
b_in = MIN(b1, b2);
|
||||
b_out = MAX(b1, b2);
|
||||
} else if (b1 >= 0) { /* (b2 == -1) */
|
||||
b_in = b1;
|
||||
b_out = b2;
|
||||
} else {/*(b2 >= 0) *//* (b1 == -1) */
|
||||
b_in = b2;
|
||||
b_out = b1;
|
||||
}
|
||||
|
||||
if (b_in != b_out) {
|
||||
/* Block boundary */
|
||||
if (bns[b_in] == NULL) {
|
||||
bns[b_in] = block_neighbours_allocate(1);
|
||||
}
|
||||
|
||||
if (bns[b_in] != NULL) {
|
||||
ret = block_neighbours_insert_neighbour(b_out, f,
|
||||
expct_nconn,
|
||||
bns[b_in]);
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
ret = 0;
|
||||
|
||||
for (b1 = 0; b1 < nblk; b1++) {
|
||||
if (bns[b1] != NULL) {
|
||||
ret += bns[b1]->nneigh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Create coarse-scale neighbour-ship definition from block-to-block
|
||||
* connectivity information ('bns') keyed off block numbers. Set
|
||||
* start pointers for CSR push-back build mode.
|
||||
*
|
||||
* Cannot fail. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
coarse_topology_build_coarsef(int nblk, struct block_neighbours **bns,
|
||||
int *neighbours, int *blkfacepos,
|
||||
size_t *nblkf, size_t *nsubf)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int b, n, coarse_f;
|
||||
|
||||
coarse_f = 0;
|
||||
*nsubf = 0;
|
||||
|
||||
for (b = 0; b < nblk; b++) {
|
||||
if (bns[b] != NULL) {
|
||||
for (n = 0; n < bns[b]->nneigh; n++) {
|
||||
neighbours[2*coarse_f + 0] = b;
|
||||
neighbours[2*coarse_f + 1] = bns[b]->neigh[n]->b;
|
||||
|
||||
coarse_f += 1;
|
||||
blkfacepos[b + 1] += 1;
|
||||
|
||||
if (bns[b]->neigh[n]->b >= 0) {
|
||||
blkfacepos[bns[b]->neigh[n]->b + 1] += 1;
|
||||
}
|
||||
|
||||
if (bns[b]->neigh[n]->fconns != NULL) {
|
||||
*nsubf += hash_set_count_elms(bns[b]->neigh[n]->fconns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Derive start pointers */
|
||||
for (b = 1; b <= nblk; b++) {
|
||||
blkfacepos[0] += blkfacepos[b];
|
||||
blkfacepos[b] = blkfacepos[0] - blkfacepos[b];
|
||||
}
|
||||
*nblkf = blkfacepos[0];
|
||||
blkfacepos[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Create coarse-scale block-to-face mapping and, if requested,
|
||||
* coarse-scale constituent faces for each coarse face.
|
||||
*
|
||||
* Constituent faces requested if subfacepos and subfaces non-NULL.
|
||||
* In this case, all coarse faces must carry sub-face information.
|
||||
*
|
||||
* Returns 1 if successful (i.e., no sub-face information requested or
|
||||
* sub-face information requested and available for all coarse faces)
|
||||
* and zero otherwise. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
coarse_topology_build_final(int ncoarse_f, int nblk,
|
||||
const int *neighbours,
|
||||
int *blkfacepos, int *blkfaces,
|
||||
struct block_neighbours **bns,
|
||||
int *subfacepos, int *subfaces)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int coarse_f, b1, b2, n, subpos, subface_valid = 1;
|
||||
size_t i;
|
||||
struct hash_set *set;
|
||||
|
||||
assert ((subfacepos == NULL) == (subfaces == NULL));
|
||||
|
||||
for (coarse_f = 0; coarse_f < ncoarse_f; coarse_f++) {
|
||||
b1 = neighbours[2*coarse_f + 0];
|
||||
b2 = neighbours[2*coarse_f + 1];
|
||||
|
||||
assert (b1 != b2);
|
||||
|
||||
if (b1 >= 0) { blkfaces[blkfacepos[b1 + 1] ++] = coarse_f; }
|
||||
if (b2 >= 0) { blkfaces[blkfacepos[b2 + 1] ++] = coarse_f; }
|
||||
}
|
||||
|
||||
if (subfacepos != NULL) {
|
||||
coarse_f = 0;
|
||||
subpos = 0;
|
||||
|
||||
for (b1 = 0; (b1 < nblk) && subface_valid; b1++) {
|
||||
if (bns[b1] != NULL) {
|
||||
for (n = 0; n < bns[b1]->nneigh; n++) {
|
||||
set = bns[b1]->neigh[n]->fconns;
|
||||
subface_valid = set != NULL;
|
||||
|
||||
if (subface_valid) {
|
||||
for (i = 0; i < set->m; i++) {
|
||||
if (set->s[i] != -1) {
|
||||
subfaces[subpos ++] = set->s[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
subfacepos[++ coarse_f] = subpos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (subfacepos == NULL) || subface_valid;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate and assemble coarse-grid structure from non-linear
|
||||
* block-to-block connection information keyed off block numbers. The
|
||||
* final coarse grid consists of 'ncoarse_f' coarse faces numbered
|
||||
* 0..ncoarse_f-1 and 'nblk' coarse blocks numbered 0..nblk-1.
|
||||
*
|
||||
* Returns fully assembled coarse-grid structure if successful or NULL
|
||||
* otherwise. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static struct coarse_topology *
|
||||
coarse_topology_build(int ncoarse_f, int nblk,
|
||||
struct block_neighbours **bns)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int i;
|
||||
int subface_valid;
|
||||
size_t nblkf, nsubf;
|
||||
struct coarse_topology *new;
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
if (new != NULL) {
|
||||
new->neighbours = malloc(2 * ncoarse_f * sizeof *new->neighbours);
|
||||
new->blkfacepos = malloc((nblk + 1) * sizeof *new->blkfacepos);
|
||||
|
||||
new->blkfaces = NULL;
|
||||
new->subfacepos = NULL;
|
||||
new->subfaces = NULL;
|
||||
|
||||
if ((new->neighbours == NULL) ||
|
||||
(new->blkfacepos == NULL)) {
|
||||
coarse_topology_destroy(new);
|
||||
new = NULL;
|
||||
} else {
|
||||
for (i = 0; i < 2 * ncoarse_f; i++) {
|
||||
new->neighbours[i] = INT_MIN;
|
||||
}
|
||||
for (i = 0; i < nblk + 1; i++) {
|
||||
new->blkfacepos[i] = 0;
|
||||
}
|
||||
|
||||
coarse_topology_build_coarsef(nblk, bns, new->neighbours,
|
||||
new->blkfacepos, &nblkf, &nsubf);
|
||||
|
||||
if (nsubf > 0) {
|
||||
new->subfacepos = malloc((ncoarse_f + 1) * sizeof *new->subfacepos);
|
||||
new->subfaces = malloc(nsubf * sizeof *new->subfaces);
|
||||
|
||||
if ((new->subfacepos == NULL) || (new->subfaces == NULL)) {
|
||||
free(new->subfaces); new->subfaces = NULL;
|
||||
free(new->subfacepos); new->subfacepos = NULL;
|
||||
} else {
|
||||
for (i = 0; i < ncoarse_f + 1; i++) {
|
||||
new->subfacepos[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new->blkfaces = malloc(nblkf * sizeof *new->blkfaces);
|
||||
|
||||
if (new->blkfaces == NULL) {
|
||||
coarse_topology_destroy(new);
|
||||
new = NULL;
|
||||
} else {
|
||||
subface_valid = coarse_topology_build_final(ncoarse_f, nblk,
|
||||
new->neighbours,
|
||||
new->blkfacepos,
|
||||
new->blkfaces,
|
||||
bns,
|
||||
new->subfacepos,
|
||||
new->subfaces);
|
||||
|
||||
if (!subface_valid) {
|
||||
free(new->subfaces); new->subfaces = NULL;
|
||||
free(new->subfacepos); new->subfacepos = NULL;
|
||||
} else {
|
||||
new->nblocks = nblk;
|
||||
new->nfaces = ncoarse_f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* Create coarse-grid topology structure from fine-scale
|
||||
* neighbour-ship definition 'neighbours' and partition vector 'p'.
|
||||
*
|
||||
* Returns fully allocated and assembled coarse-grid structure if
|
||||
* successful and NULL otherwise. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct coarse_topology *
|
||||
coarse_topology_create(int nc, int nf, int expct_nconn,
|
||||
const int *p, const int *neighbours)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int b, nblocks, ncoarse_f;
|
||||
|
||||
struct block_neighbours **bns;
|
||||
struct coarse_topology *topo;
|
||||
|
||||
nblocks = count_blocks(nc, p);
|
||||
|
||||
bns = malloc(nblocks * sizeof *bns);
|
||||
if (bns != NULL) {
|
||||
for (b = 0; b < nblocks; b++) {
|
||||
bns[b] = NULL;
|
||||
}
|
||||
|
||||
ncoarse_f = derive_block_faces(nf, nblocks, expct_nconn,
|
||||
p, neighbours, bns);
|
||||
|
||||
topo = coarse_topology_build(ncoarse_f, nblocks, bns);
|
||||
|
||||
for (b = 0; b < nblocks; b++) {
|
||||
block_neighbours_deallocate(bns[b]);
|
||||
}
|
||||
|
||||
free(bns);
|
||||
} else {
|
||||
topo = NULL;
|
||||
}
|
||||
|
||||
return topo;
|
||||
}
|
||||
|
||||
|
||||
/* Release memory resources for dynamically allocated coarse-grid
|
||||
* topology structure 't'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
coarse_topology_destroy(struct coarse_topology *t)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (t != NULL) {
|
||||
free(t->subfaces);
|
||||
free(t->subfacepos);
|
||||
|
||||
free(t->blkfaces);
|
||||
free(t->blkfacepos);
|
||||
|
||||
free(t->neighbours);
|
||||
}
|
||||
|
||||
free(t);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_COARSE_CONN_HEADER_INCLUDED
|
||||
#define OPM_COARSE_CONN_HEADER_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct coarse_topology {
|
||||
int nblocks; /* Number of blocks */
|
||||
int nfaces; /* Number of faces */
|
||||
|
||||
int *neighbours; /* Neighbourship definition */
|
||||
|
||||
int *blkfacepos; /* Index into blkfaces */
|
||||
int *blkfaces; /* Faces per block */
|
||||
|
||||
int *subfacepos; /* Index into subfaces */
|
||||
int *subfaces; /* FS faces per coarse face */
|
||||
};
|
||||
|
||||
|
||||
struct coarse_topology *
|
||||
coarse_topology_create(int nc, int nf, int expct_nconn,
|
||||
const int *p, const int *neighbours);
|
||||
|
||||
|
||||
void
|
||||
coarse_topology_destroy(struct coarse_topology *t);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_COARSE_CONN_HEADER_INCLUDED */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_COARSE_SYS_HEADER_INCLUDED
|
||||
#define OPM_COARSE_SYS_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
struct coarse_sys {
|
||||
int *dof2conn; /* Map dof->connection (coarse interface) */
|
||||
int *blkdof_pos; /* Start pointers to each block's dofs */
|
||||
int *basis_pos; /* Start pointers to each block's bf's */
|
||||
int *cell_ip_pos; /* Start pointers to each block's IP */
|
||||
|
||||
int *blkdof; /* Each block's dofs */
|
||||
double *basis; /* All basis functions */
|
||||
double *cell_ip; /* Fine-scale IP contributions */
|
||||
double *Binv; /* Coarse-scale inverse IP per block */
|
||||
};
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
struct coarse_topology;
|
||||
struct CSRMatrix;
|
||||
|
||||
typedef void (*LocalSolver)(struct CSRMatrix *A,
|
||||
double *b,
|
||||
double *x);
|
||||
|
||||
struct coarse_sys *
|
||||
coarse_sys_construct(struct UnstructuredGrid *g, const int *p,
|
||||
struct coarse_topology *ct,
|
||||
const double *perm,
|
||||
const double *src,
|
||||
const double *totmob,
|
||||
LocalSolver linsolve);
|
||||
|
||||
void
|
||||
coarse_sys_destroy(struct coarse_sys *sys);
|
||||
|
||||
void
|
||||
coarse_sys_compute_cell_ip(int nc,
|
||||
int max_nconn,
|
||||
int nb,
|
||||
const int *pconn,
|
||||
const double *Binv,
|
||||
const int *b2c_pos,
|
||||
const int *b2c,
|
||||
struct coarse_sys *sys);
|
||||
|
||||
void
|
||||
coarse_sys_compute_Binv(int nb,
|
||||
int max_bcells,
|
||||
const double *totmob,
|
||||
const int *b2c_pos,
|
||||
const int *b2c,
|
||||
struct coarse_sys *sys,
|
||||
double *work);
|
||||
|
||||
void
|
||||
coarse_sys_compute_fs_flux(struct UnstructuredGrid *g,
|
||||
struct coarse_topology *ct,
|
||||
struct coarse_sys *sys,
|
||||
const int *b2c_pos,
|
||||
const int *b2c,
|
||||
const double *v_c,
|
||||
double *flux,
|
||||
double *work);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_COARSE_SYS_HEADER_INCLUDED */
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opm/core/pressure/msmfem/hash_set.h>
|
||||
|
||||
/* ======================================================================
|
||||
* Macros
|
||||
* ====================================================================== */
|
||||
#define GOLDEN_RAT (0.6180339887498949) /* (sqrt(5) - 1) / 2 */
|
||||
#define IS_POW2(x) (((x) & ((x) - 1)) == 0)
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
|
||||
/* Define a hash array size (1<<p) capable of holding a set of size 'm' */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static size_t
|
||||
hash_set_size(size_t m)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (m == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (IS_POW2(m)) {
|
||||
return m;
|
||||
}
|
||||
|
||||
/* General case. Use next power of two. */
|
||||
/* Algorithm due to
|
||||
*
|
||||
* Warren Jr., Henry S. (2002). Hacker's Delight.
|
||||
* Addison Wesley. pp. 48. ISBN 978-0201914658
|
||||
*
|
||||
* by way of Wikipedia. */
|
||||
m -= 1;
|
||||
for (i = 1; i < CHAR_BIT * sizeof m; i <<= 1) {
|
||||
m = m | (m >> i);
|
||||
}
|
||||
|
||||
return m + 1;
|
||||
}
|
||||
|
||||
|
||||
/* Hash element 'k' into table of size 'm' (multiplication method) */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static size_t
|
||||
hash_set_idx(int k, size_t m)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
double x = fmod(k * GOLDEN_RAT, 1.0);
|
||||
double y = floor(m * x);
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
/* Insert element 'k' into set 's' of size 'm'
|
||||
* (open addressing, double probing). */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static size_t
|
||||
hash_set_insert_core(int k, size_t m, int *s)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t h1, h2, i, j;
|
||||
|
||||
assert ((0 < m) && (m < (size_t)(-1)));
|
||||
assert (IS_POW2(m));
|
||||
|
||||
j = h1 = hash_set_idx(k, m);
|
||||
assert (h1 < m);
|
||||
|
||||
if (s[j] == -1) { s[j] = k; }
|
||||
if (s[j] == k) { return j; }
|
||||
|
||||
/* Double hash probing. h2 relatively prime to 'm' */
|
||||
h2 = 2 * hash_set_idx(k, MAX(m >> 1, 1)) + 1;
|
||||
|
||||
for (i = 1; (s[j] != -1) && (s[j] != k) && (i < m); i++) {
|
||||
j += h2;
|
||||
j &= m - 1; /* Modulo m since IS_POW2(m). */
|
||||
}
|
||||
|
||||
if ((s[j] == -1) || (s[j] == k)) {
|
||||
s[j] = k; /* Possibly no-op. */
|
||||
} else {
|
||||
j = m + 1; /* Invalid. Caveat emptor. */
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
/* Increase size of hash set 't' to hold 'm' elements whilst copying
|
||||
* existing elements. This is typically fairly expensive. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
hash_set_expand(size_t m, struct hash_set *t)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ret, *s, *p;
|
||||
size_t i;
|
||||
|
||||
assert (m > t->m);
|
||||
|
||||
s = malloc(m * sizeof *s);
|
||||
if (s != NULL) {
|
||||
for (i = 0; i < m; i++) { s[i] = -1; }
|
||||
|
||||
for (i = 0; i < t->m; i++) {
|
||||
ret = hash_set_insert_core(t->s[i], m, s);
|
||||
assert ((size_t) ret < m);
|
||||
}
|
||||
|
||||
p = t->s;
|
||||
t->s = s;
|
||||
t->m = m;
|
||||
|
||||
free(p);
|
||||
|
||||
ret = m;
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Release dynamic memory resources for hash set 't'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hash_set_deallocate(struct hash_set *t)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (t != NULL) {
|
||||
free(t->s);
|
||||
}
|
||||
|
||||
free(t);
|
||||
}
|
||||
|
||||
|
||||
/* Construct an emtpy hash set capable of holding 'm' elements */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct hash_set *
|
||||
hash_set_allocate(int m)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t i, sz;
|
||||
struct hash_set *new;
|
||||
|
||||
new = malloc(1 * sizeof *new);
|
||||
if (new != NULL) {
|
||||
sz = hash_set_size(m);
|
||||
new->s = malloc(sz * sizeof *new->s);
|
||||
|
||||
if (new->s == NULL) {
|
||||
hash_set_deallocate(new);
|
||||
|
||||
new = NULL;
|
||||
} else {
|
||||
for (i = 0; i < sz; i++) { new->s[i] = -1; }
|
||||
new->m = sz;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/* Insert element 'k' into hash set 't'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int
|
||||
hash_set_insert(int k, struct hash_set *t)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ret;
|
||||
size_t i;
|
||||
|
||||
assert (k >= 0);
|
||||
assert (t != NULL);
|
||||
assert (IS_POW2(t->m));
|
||||
|
||||
i = hash_set_insert_core(k, t->m, t->s);
|
||||
if (i == t->m + 1) {
|
||||
/* Table full. Preferable an infrequent occurrence. Expand
|
||||
* table and re-insert key (if possible). */
|
||||
ret = hash_set_expand(t->m << 1, t);
|
||||
|
||||
if (ret > 0) {
|
||||
i = hash_set_insert_core(k, t->m, t->s);
|
||||
assert (i < t->m);
|
||||
|
||||
ret = k;
|
||||
}
|
||||
} else {
|
||||
ret = k;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
size_t
|
||||
hash_set_count_elms(const struct hash_set *set)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
size_t i, n;
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < set->m; i++) {
|
||||
n += set->s[i] != -1;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
Copyright 2010 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_HASH_SET_HEADER_INCLUDED
|
||||
#define OPM_HASH_SET_HEADER_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Poor-man's unordered set (ind. key insert/all key extract only). */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct hash_set {
|
||||
size_t m; /* Table/set capacity (1<<p for some p) */
|
||||
int *s; /* Set representation */
|
||||
};
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Release dynamic memory resources for hash set 's'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
hash_set_deallocate(struct hash_set *s);
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Construct an emtpy hash set capable of holding 'm' elements */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct hash_set *
|
||||
hash_set_allocate(int m);
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Insert element 'k' into hash set 's'. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int
|
||||
hash_set_insert(int k, struct hash_set *s);
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Count number of valid keys in a hash set. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
size_t
|
||||
hash_set_count_elms(const struct hash_set *set);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_HASH_SET_HEADER_INCLUDED */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user