gebpy 1.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. gebpy/__init__.py +55 -0
  2. gebpy/__pycache__/__init__.cpython-310.pyc +0 -0
  3. gebpy/adapters/__init__.py +0 -0
  4. gebpy/cli/__init__.py +0 -0
  5. gebpy/core/__init__.py +0 -0
  6. gebpy/core/chemistry/__init__.py +0 -0
  7. gebpy/core/chemistry/common.py +1369 -0
  8. gebpy/core/chemistry/elements.py +317 -0
  9. gebpy/core/chemistry/geochemistry.py +1728 -0
  10. gebpy/core/fluids/__init__.py +0 -0
  11. gebpy/core/io/__init__.py +0 -0
  12. gebpy/core/mathematics/__init__.py +0 -0
  13. gebpy/core/minerals/__init__.py +0 -0
  14. gebpy/core/minerals/carbonates.py +412 -0
  15. gebpy/core/minerals/common.py +555 -0
  16. gebpy/core/minerals/config.py +77 -0
  17. gebpy/core/minerals/cyclosilicates.py +0 -0
  18. gebpy/core/minerals/halides.py +0 -0
  19. gebpy/core/minerals/inosilicates.py +0 -0
  20. gebpy/core/minerals/nesosilicates.py +0 -0
  21. gebpy/core/minerals/organics.py +0 -0
  22. gebpy/core/minerals/oxides.py +589 -0
  23. gebpy/core/minerals/phosphates.py +0 -0
  24. gebpy/core/minerals/phospides.py +0 -0
  25. gebpy/core/minerals/phyllosilicates.py +436 -0
  26. gebpy/core/minerals/sorosilicates.py +0 -0
  27. gebpy/core/minerals/sulfates.py +0 -0
  28. gebpy/core/minerals/sulfides.py +459 -0
  29. gebpy/core/minerals/synthesis.py +201 -0
  30. gebpy/core/minerals/tectosilicates.py +433 -0
  31. gebpy/core/physics/__init__.py +0 -0
  32. gebpy/core/physics/common.py +53 -0
  33. gebpy/core/physics/geophysics.py +351 -0
  34. gebpy/core/rocks/__init__.py +0 -0
  35. gebpy/core/rocks/anisotropic_rocks.py +395 -0
  36. gebpy/core/rocks/common.py +95 -0
  37. gebpy/core/rocks/config.py +77 -0
  38. gebpy/core/rocks/isotropic_rocks.py +395 -0
  39. gebpy/core/rocks/sedimentary.py +385 -0
  40. gebpy/core/subsurface/__init__.py +0 -0
  41. gebpy/data_minerals/__init__.py +0 -0
  42. gebpy/data_minerals/albite.yaml +59 -0
  43. gebpy/data_minerals/anatase.yaml +43 -0
  44. gebpy/data_minerals/ankerite.yaml +47 -0
  45. gebpy/data_minerals/annite.yaml +57 -0
  46. gebpy/data_minerals/anorthite.yaml +59 -0
  47. gebpy/data_minerals/antigorite.yaml +53 -0
  48. gebpy/data_minerals/aragonite.yaml +48 -0
  49. gebpy/data_minerals/argutite.yaml +43 -0
  50. gebpy/data_minerals/arsenolite.yaml +40 -0
  51. gebpy/data_minerals/au3oxide.yaml +46 -0
  52. gebpy/data_minerals/avicennite.yaml +40 -0
  53. gebpy/data_minerals/azurite.yaml +53 -0
  54. gebpy/data_minerals/baddeleyite.yaml +49 -0
  55. gebpy/data_minerals/bismite.yaml +49 -0
  56. gebpy/data_minerals/boehmite.yaml +48 -0
  57. gebpy/data_minerals/brookite.yaml +46 -0
  58. gebpy/data_minerals/brucite.yaml +45 -0
  59. gebpy/data_minerals/bunsenite.yaml +40 -0
  60. gebpy/data_minerals/calcite.yaml +45 -0
  61. gebpy/data_minerals/cassiterite.yaml +43 -0
  62. gebpy/data_minerals/cerussite.yaml +48 -0
  63. gebpy/data_minerals/chamosite.yaml +56 -0
  64. gebpy/data_minerals/chlorite.yaml +75 -0
  65. gebpy/data_minerals/chromite.yaml +42 -0
  66. gebpy/data_minerals/chrysotile.yaml +53 -0
  67. gebpy/data_minerals/claudetite.yaml +49 -0
  68. gebpy/data_minerals/clinochlore.yaml +55 -0
  69. gebpy/data_minerals/cochromite.yaml +42 -0
  70. gebpy/data_minerals/corundum.yaml +43 -0
  71. gebpy/data_minerals/crocoite.yaml +51 -0
  72. gebpy/data_minerals/cuprite.yaml +40 -0
  73. gebpy/data_minerals/cuprospinel.yaml +42 -0
  74. gebpy/data_minerals/diaspore.yaml +48 -0
  75. gebpy/data_minerals/dolomite.yaml +47 -0
  76. gebpy/data_minerals/eastonite.yaml +57 -0
  77. gebpy/data_minerals/eskolaite.yaml +43 -0
  78. gebpy/data_minerals/fechlorite.yaml +61 -0
  79. gebpy/data_minerals/fecolumbite.yaml +48 -0
  80. gebpy/data_minerals/ferberite.yaml +51 -0
  81. gebpy/data_minerals/fetantalite.yaml +48 -0
  82. gebpy/data_minerals/franklinite.yaml +42 -0
  83. gebpy/data_minerals/gahnite.yaml +42 -0
  84. gebpy/data_minerals/galaxite.yaml +42 -0
  85. gebpy/data_minerals/geikielite.yaml +45 -0
  86. gebpy/data_minerals/gibbsite.yaml +51 -0
  87. gebpy/data_minerals/glauconite.yaml +69 -0
  88. gebpy/data_minerals/goethite.yaml +48 -0
  89. gebpy/data_minerals/groutite.yaml +48 -0
  90. gebpy/data_minerals/hematite.yaml +43 -0
  91. gebpy/data_minerals/hercynite.yaml +42 -0
  92. gebpy/data_minerals/huebnerite.yaml +51 -0
  93. gebpy/data_minerals/ikaite.yaml +53 -0
  94. gebpy/data_minerals/illite.yaml +55 -0
  95. gebpy/data_minerals/ilmenite.yaml +45 -0
  96. gebpy/data_minerals/jacobsite.yaml +42 -0
  97. gebpy/data_minerals/kalsilite.yaml +47 -0
  98. gebpy/data_minerals/kaolinite.yaml +59 -0
  99. gebpy/data_minerals/karelianite.yaml +43 -0
  100. gebpy/data_minerals/lime.yaml +40 -0
  101. gebpy/data_minerals/litharge.yaml +43 -0
  102. gebpy/data_minerals/magnesiochromite.yaml +42 -0
  103. gebpy/data_minerals/magnesioferrite.yaml +42 -0
  104. gebpy/data_minerals/magnesite.yaml +45 -0
  105. gebpy/data_minerals/magnetite.yaml +41 -0
  106. gebpy/data_minerals/malachite.yaml +53 -0
  107. gebpy/data_minerals/manganite.yaml +51 -0
  108. gebpy/data_minerals/manganochromite.yaml +42 -0
  109. gebpy/data_minerals/manganosite.yaml +40 -0
  110. gebpy/data_minerals/marialite.yaml +49 -0
  111. gebpy/data_minerals/massicot.yaml +46 -0
  112. gebpy/data_minerals/meionite.yaml +49 -0
  113. gebpy/data_minerals/mgchlorite.yaml +61 -0
  114. gebpy/data_minerals/mgcolumbite.yaml +48 -0
  115. gebpy/data_minerals/mgtantalite.yaml +48 -0
  116. gebpy/data_minerals/microcline.yaml +59 -0
  117. gebpy/data_minerals/minium.yaml +44 -0
  118. gebpy/data_minerals/mnchlorite.yaml +61 -0
  119. gebpy/data_minerals/mncolumbite.yaml +48 -0
  120. gebpy/data_minerals/mntantalite.yaml +48 -0
  121. gebpy/data_minerals/monteponite.yaml +40 -0
  122. gebpy/data_minerals/montmorillonite.yaml +77 -0
  123. gebpy/data_minerals/muscovite.yaml +55 -0
  124. gebpy/data_minerals/nanepheline.yaml +47 -0
  125. gebpy/data_minerals/nichlorite.yaml +61 -0
  126. gebpy/data_minerals/nichromite.yaml +42 -0
  127. gebpy/data_minerals/nimite.yaml +55 -0
  128. gebpy/data_minerals/nontronite.yaml +73 -0
  129. gebpy/data_minerals/orthoclase.yaml +53 -0
  130. gebpy/data_minerals/paratellurite.yaml +43 -0
  131. gebpy/data_minerals/pennantite.yaml +61 -0
  132. gebpy/data_minerals/periclase.yaml +40 -0
  133. gebpy/data_minerals/phlogopite.yaml +57 -0
  134. gebpy/data_minerals/plattnerite.yaml +43 -0
  135. gebpy/data_minerals/powellite.yaml +45 -0
  136. gebpy/data_minerals/pyrite.yaml +40 -0
  137. gebpy/data_minerals/pyrolusite.yaml +43 -0
  138. gebpy/data_minerals/pyrophanite.yaml +45 -0
  139. gebpy/data_minerals/pyrophyllite.yaml +59 -0
  140. gebpy/data_minerals/quartz.yaml +43 -0
  141. gebpy/data_minerals/rhodochrosite.yaml +45 -0
  142. gebpy/data_minerals/rutile.yaml +43 -0
  143. gebpy/data_minerals/saponite.yaml +77 -0
  144. gebpy/data_minerals/scheelite.yaml +45 -0
  145. gebpy/data_minerals/scrutinyite.yaml +46 -0
  146. gebpy/data_minerals/senarmontite.yaml +40 -0
  147. gebpy/data_minerals/siderite.yaml +45 -0
  148. gebpy/data_minerals/siderophyllite.yaml +57 -0
  149. gebpy/data_minerals/smithsonite.yaml +45 -0
  150. gebpy/data_minerals/spinel.yaml +42 -0
  151. gebpy/data_minerals/stishovite.yaml +43 -0
  152. gebpy/data_minerals/stolzite.yaml +45 -0
  153. gebpy/data_minerals/talc.yaml +53 -0
  154. gebpy/data_minerals/tistarite.yaml +43 -0
  155. gebpy/data_minerals/trevorite.yaml +42 -0
  156. gebpy/data_minerals/ulvoespinel.yaml +42 -0
  157. gebpy/data_minerals/uraninite.yaml +40 -0
  158. gebpy/data_minerals/valentinite.yaml +46 -0
  159. gebpy/data_minerals/vermiculite.yaml +69 -0
  160. gebpy/data_minerals/wulfenite.yaml +45 -0
  161. gebpy/data_minerals/wustite.yaml +40 -0
  162. gebpy/data_minerals/zincite.yaml +43 -0
  163. gebpy/data_minerals/zincochromite.yaml +42 -0
  164. gebpy/data_rocks/__init__.py +0 -0
  165. gebpy/data_rocks/dolostone.yaml +40 -0
  166. gebpy/data_rocks/limestone.yaml +40 -0
  167. gebpy/data_rocks/marl.yaml +50 -0
  168. gebpy/data_rocks/sandstone.yaml +39 -0
  169. gebpy/data_rocks/shale.yaml +50 -0
  170. gebpy/gebpy_app.py +8732 -0
  171. gebpy/gui/__init__.py +0 -0
  172. gebpy/lib/images/GebPy_Header.png +0 -0
  173. gebpy/lib/images/GebPy_Icon.png +0 -0
  174. gebpy/lib/images/GebPy_Logo.png +0 -0
  175. gebpy/main.py +29 -0
  176. gebpy/modules/__init__.py +0 -0
  177. gebpy/modules/__pycache__/__init__.cpython-310.pyc +0 -0
  178. gebpy/modules/__pycache__/metamorphics.cpython-310.pyc +0 -0
  179. gebpy/modules/__pycache__/silicates.cpython-310.pyc +0 -0
  180. gebpy/modules/carbonates.py +2658 -0
  181. gebpy/modules/chemistry.py +1369 -0
  182. gebpy/modules/core.py +1805 -0
  183. gebpy/modules/elements.py +317 -0
  184. gebpy/modules/evaporites.py +1299 -0
  185. gebpy/modules/exploration.py +1145 -0
  186. gebpy/modules/fluids.py +339 -0
  187. gebpy/modules/geochemistry.py +1727 -0
  188. gebpy/modules/geophysics.py +351 -0
  189. gebpy/modules/gui.py +9093 -0
  190. gebpy/modules/gui_elements.py +145 -0
  191. gebpy/modules/halides.py +485 -0
  192. gebpy/modules/igneous.py +2241 -0
  193. gebpy/modules/metamorphics.py +3222 -0
  194. gebpy/modules/mineralogy.py +442 -0
  195. gebpy/modules/minerals.py +7954 -0
  196. gebpy/modules/ore.py +1648 -0
  197. gebpy/modules/organics.py +530 -0
  198. gebpy/modules/oxides.py +9057 -0
  199. gebpy/modules/petrophysics.py +98 -0
  200. gebpy/modules/phosphates.py +589 -0
  201. gebpy/modules/phospides.py +194 -0
  202. gebpy/modules/plotting.py +619 -0
  203. gebpy/modules/pyllosilicates.py +380 -0
  204. gebpy/modules/sedimentary_rocks.py +908 -0
  205. gebpy/modules/sequences.py +2166 -0
  206. gebpy/modules/series.py +1625 -0
  207. gebpy/modules/silicates.py +11102 -0
  208. gebpy/modules/siliciclastics.py +1846 -0
  209. gebpy/modules/subsurface_2d.py +179 -0
  210. gebpy/modules/sulfates.py +1629 -0
  211. gebpy/modules/sulfides.py +4786 -0
  212. gebpy/plotting/__init__.py +0 -0
  213. gebpy/ui_nb/__init__.py +0 -0
  214. gebpy/user_data/.gitkeep +0 -0
  215. gebpy-1.1.3.dist-info/LICENSE +165 -0
  216. gebpy-1.1.3.dist-info/METADATA +207 -0
  217. gebpy-1.1.3.dist-info/RECORD +254 -0
  218. gebpy-1.1.3.dist-info/WHEEL +5 -0
  219. gebpy-1.1.3.dist-info/entry_points.txt +2 -0
  220. gebpy-1.1.3.dist-info/top_level.txt +1 -0
  221. modules/__init__.py +0 -0
  222. modules/carbonates.py +2658 -0
  223. modules/chemistry.py +1369 -0
  224. modules/core.py +1805 -0
  225. modules/elements.py +317 -0
  226. modules/evaporites.py +1299 -0
  227. modules/exploration.py +765 -0
  228. modules/fluids.py +339 -0
  229. modules/geochemistry.py +1727 -0
  230. modules/geophysics.py +337 -0
  231. modules/gui.py +9093 -0
  232. modules/gui_elements.py +145 -0
  233. modules/halides.py +485 -0
  234. modules/igneous.py +2196 -0
  235. modules/metamorphics.py +2699 -0
  236. modules/mineralogy.py +442 -0
  237. modules/minerals.py +7954 -0
  238. modules/ore.py +1628 -0
  239. modules/organics.py +530 -0
  240. modules/oxides.py +9057 -0
  241. modules/petrophysics.py +98 -0
  242. modules/phosphates.py +589 -0
  243. modules/phospides.py +194 -0
  244. modules/plotting.py +619 -0
  245. modules/pyllosilicates.py +380 -0
  246. modules/sedimentary_rocks.py +908 -0
  247. modules/sequences.py +2166 -0
  248. modules/series.py +1625 -0
  249. modules/silicates.py +11102 -0
  250. modules/siliciclastics.py +1830 -0
  251. modules/subsurface_2d.py +179 -0
  252. modules/sulfates.py +1629 -0
  253. modules/sulfides.py +4786 -0
  254. notebooks/__init__.py +0 -0
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env python
2
+ # -*-coding: utf-8 -*-
3
+
4
+ #-----------------------------------------------
5
+
6
+ # Name: anisotropic_rocks.py
7
+ # Author: Maximilian A. Beeskow
8
+ # Version: 1.0
9
+ # Date: 17.12.2025
10
+
11
+ #-----------------------------------------------
12
+
13
+ """
14
+ Module: anisotropic_rocks.py
15
+ This module controls the generation of synthetic data for anisotropic rocks.
16
+ """
17
+
18
+ # PACKAGES
19
+ import yaml
20
+ import numpy as np
21
+ import pandas as pd
22
+ from pathlib import Path
23
+
24
+ # MODULES
25
+ from ..minerals.synthesis import MineralDataGeneration
26
+ from ..rocks.common import RockGeneration
27
+ from ..physics.common import Geophysics
28
+
29
+ # Code
30
+ BASE_PATH = Path(__file__).resolve().parents[2]
31
+ DATA_PATH = BASE_PATH / "data_rocks"
32
+
33
+ class AnisotropicRocks:
34
+ _yaml_cache = {}
35
+ _mineralogy_cache = {}
36
+ _mineral_groups_cache = {}
37
+ _rocks = {"Shale"}
38
+
39
+ def __init__(self, name, random_seed) -> None:
40
+ self.name = name
41
+ self.random_seed = random_seed
42
+ self.rng = np.random.default_rng(random_seed)
43
+ self.current_seed = int(np.round(self.rng.uniform(0, 1000), 0))
44
+ self.data_path = DATA_PATH
45
+ self.rock_gen = RockGeneration()
46
+ self.geophysics = Geophysics()
47
+ self.conversion_factors = self.rock_gen._determine_oxide_conversion_factors()
48
+ self.cache = {}
49
+
50
+ def _load_yaml(self, rock_name: str) -> dict:
51
+ # 1) Cache-Hit
52
+ if rock_name in AnisotropicRocks._yaml_cache:
53
+ return AnisotropicRocks._yaml_cache[rock_name]
54
+
55
+ # 2) Laden von Disk
56
+ yaml_file = self.data_path/f"{rock_name}.yaml"
57
+ if not yaml_file.exists():
58
+ raise FileNotFoundError(f"No YAML file found for {rock_name}.")
59
+
60
+ with open(yaml_file, "r") as f:
61
+ data = yaml.safe_load(f)
62
+
63
+ if "mineralogy" in data and rock_name not in AnisotropicRocks._mineralogy_cache:
64
+ self._compile_mineralogy(rock_name, data["mineralogy"])
65
+
66
+ if "mineral_groups" in data:
67
+ self._compile_mineral_groups(rock_name, data["mineral_groups"])
68
+
69
+ # 3) Cache schreiben
70
+ AnisotropicRocks._yaml_cache[rock_name] = data
71
+
72
+ return data
73
+
74
+ def _compile_mineralogy(self, rock_name: str, mineralogy_dict: dict):
75
+ """
76
+ Extracts and compiles all chemistry formulas from the YAML file.
77
+ Stores the compiled ASTs in the global formula cache.
78
+ """
79
+ if rock_name not in AnisotropicRocks._mineralogy_cache:
80
+ AnisotropicRocks._mineralogy_cache[rock_name] = {}
81
+
82
+ for element, entry in mineralogy_dict.items():
83
+ mineral = element
84
+ interval = list(entry.values())
85
+ lower_limit = interval[0]
86
+ upper_limit = interval[1]
87
+ compiled = [lower_limit, upper_limit]
88
+ AnisotropicRocks._mineralogy_cache[rock_name][mineral] = compiled
89
+
90
+ def _compile_mineral_groups(self, rock_name: str, group_dict: dict):
91
+ if rock_name not in AnisotropicRocks._mineral_groups_cache:
92
+ AnisotropicRocks._mineral_groups_cache[rock_name] = {}
93
+
94
+ for group, entry in group_dict.items():
95
+ minerals = entry["minerals"]
96
+ min_val = entry["min"]
97
+ max_val = entry["max"]
98
+
99
+ AnisotropicRocks._mineral_groups_cache[rock_name][group] = {
100
+ "minerals": minerals,
101
+ "min": min_val,
102
+ "max": max_val
103
+ }
104
+
105
+ def _sample_mineralogy(self, rock_name: str, number: int):
106
+ has_groups = rock_name in AnisotropicRocks._mineral_groups_cache
107
+
108
+ if not has_groups:
109
+ # ---- Modus A: flach ----
110
+ mineral_limits = AnisotropicRocks._mineralogy_cache[rock_name]
111
+ mins = [v[0] for v in mineral_limits.values()]
112
+ maxs = [v[1] for v in mineral_limits.values()]
113
+ minerals = list(mineral_limits.keys())
114
+
115
+ comp = self._sample_bounded_simplex_batch(mins, maxs, number)
116
+ return minerals, comp
117
+
118
+ # ---- Modus B: gruppiert ----
119
+ groups = AnisotropicRocks._mineral_groups_cache[rock_name]
120
+ mineral_limits = AnisotropicRocks._mineralogy_cache[rock_name]
121
+
122
+ # 1️⃣ Gruppen + freie Minerale
123
+ group_names = list(groups.keys())
124
+ group_mins = [groups[g]["min"] for g in group_names]
125
+ group_maxs = [groups[g]["max"] for g in group_names]
126
+
127
+ free_minerals = [
128
+ m for m in mineral_limits
129
+ if not any(m in groups[g]["minerals"] for g in groups)
130
+ ]
131
+
132
+ free_mins = [mineral_limits[m][0] for m in free_minerals]
133
+ free_maxs = [mineral_limits[m][1] for m in free_minerals]
134
+
135
+ labels = group_names + free_minerals
136
+ mins = group_mins + free_mins
137
+ maxs = group_maxs + free_maxs
138
+
139
+ # 2️⃣ Top-Level-Sampling
140
+ top_comp = self._sample_bounded_simplex_batch(mins, maxs, number)
141
+
142
+ # 3️⃣ Gruppen intern auflösen
143
+ mineral_list = []
144
+ mineral_comp = []
145
+
146
+ for i, label in enumerate(labels):
147
+ frac = top_comp[:, i]
148
+
149
+ if label in groups:
150
+ minerals = groups[label]["minerals"]
151
+ n = len(minerals)
152
+ split = self.rng.dirichlet(np.ones(n), size=number)
153
+
154
+ for j, m in enumerate(minerals):
155
+ mineral_list.append(m)
156
+ mineral_comp.append(frac * split[:, j])
157
+ else:
158
+ mineral_list.append(label)
159
+ mineral_comp.append(frac)
160
+
161
+ comp = np.vstack(mineral_comp).T
162
+ return mineral_list, comp
163
+
164
+ def _sample_bounded_simplex_batch(self, min_vals, max_vals, number):
165
+ min_vals = np.asarray(min_vals, dtype=float)
166
+ max_vals = np.asarray(max_vals, dtype=float)
167
+ n = len(min_vals)
168
+
169
+ # --- Consistency ---
170
+ if min_vals.sum() > 1:
171
+ raise ValueError("Sum of minimum fractions > 1")
172
+
173
+ if max_vals.sum() < 1:
174
+ raise ValueError("Sum of maximum fractions < 1")
175
+
176
+ span = max_vals - min_vals
177
+ samples = np.zeros((number, n))
178
+
179
+ for i in range(number):
180
+ remaining = 1.0 - min_vals.sum()
181
+ order = np.arange(n)
182
+
183
+ self.rng.shuffle(order)
184
+ x = np.zeros(n)
185
+
186
+ for j in order[:-1]:
187
+ upper = min(span[j], remaining)
188
+ val = self.rng.uniform(0, upper)
189
+ x[j] = val
190
+ remaining -= val
191
+
192
+ x[order[-1]] = remaining
193
+ samples[i] = min_vals + x
194
+
195
+ return samples
196
+
197
+ def _collect_mineral_data(self, list_minerals, number):
198
+ _mineral_data = []
199
+ for index, mineral in enumerate(list_minerals):
200
+ data_init = MineralDataGeneration(mineral, number)
201
+ data_mineral = data_init.generate_data()
202
+ is_fixed = data_mineral.shape[0] == 1
203
+ if is_fixed and number > 1:
204
+ data_mineral = pd.concat([data_mineral]*number, ignore_index=True)
205
+ elif not is_fixed and data_mineral.shape[0] != number:
206
+ raise ValueError(
207
+ f"Mineral '{mineral}' returned {data_mineral.shape[0]} rows, "
208
+ f"but expected {number}.")
209
+ _mineral_data.append(data_mineral)
210
+
211
+ return _mineral_data
212
+
213
+ def _extract_mineral_property_data(self, list_minerals, data_mineral, property):
214
+ arrays = [data_mineral[i][property].to_numpy() for i in range(len(list_minerals))]
215
+ return np.vstack(arrays)
216
+
217
+ def _extract_element_data(self, data_minerals, list_elements):
218
+ seen = set(list_elements)
219
+ ordered = list(list_elements)
220
+
221
+ for dataset in data_minerals:
222
+ for key in dataset.keys():
223
+ if key.startswith("chemistry."):
224
+ element = key[len("chemistry."):]
225
+ if element not in seen:
226
+ seen.add(element)
227
+ ordered.append(element)
228
+
229
+ return ordered
230
+
231
+ def _extract_oxide_data(self, data_minerals, list_oxides):
232
+ seen = set(list_oxides)
233
+ ordered = list(list_oxides)
234
+
235
+ for dataset in data_minerals:
236
+ for key in dataset.keys():
237
+ if key.startswith("compounds."):
238
+ oxide = key[len("compounds."):]
239
+ if oxide not in seen:
240
+ seen.add(oxide)
241
+ ordered.append(oxide)
242
+
243
+ # Spezialfall Pyrit
244
+ if dataset["mineral"][0] == "Py":
245
+ for oxide in ("Fe2O3", "SO3"):
246
+ if oxide not in seen:
247
+ seen.add(oxide)
248
+ ordered.append(oxide)
249
+ if "FeS2" in seen:
250
+ seen.remove("FeS2")
251
+ ordered = [o for o in ordered if o != "FeS2"]
252
+
253
+ return ordered
254
+
255
+ def _update_chemistry_data(self, _bulk_data, data_minerals, data_composition, element, number):
256
+ n_minerals = len(data_minerals)
257
+ helper = np.zeros((n_minerals, number))
258
+ key_element = "chemistry." + element
259
+
260
+ for i, dataset in enumerate(data_minerals):
261
+ if key_element in dataset:
262
+ helper[i, :] = dataset[key_element].to_numpy()
263
+
264
+ bulk_values = np.sum(data_composition * helper.T, axis=1)
265
+ _bulk_data["w." + element] = bulk_values
266
+
267
+ return _bulk_data
268
+
269
+ def _update_oxide_data(self, bulk_data, list_oxides):
270
+ for oxide in list_oxides:
271
+ cation, anion = self.rock_gen._get_elements_of_compound(compound=oxide)
272
+ if anion == "O":
273
+ key_cation = "w." + cation
274
+ values = self.conversion_factors[oxide]["factor"]*bulk_data[key_cation]
275
+ key_oxide = "w." + oxide
276
+ bulk_data[key_oxide] = values
277
+ else:
278
+ print("There is a non-oxide compound part of the list.")
279
+
280
+ return bulk_data
281
+
282
+ def _assign_mineral_amounts(self, bulk_data, data_amounts):
283
+ for mineral, values in data_amounts.items():
284
+ key_mineral = "phi." + mineral
285
+ bulk_data[key_mineral] = values
286
+
287
+ return bulk_data
288
+
289
+ def collect_geophysical_properties(self, _helper_bulk_data, rho_f, n):
290
+ # Update bulk density data
291
+ (_helper_bulk_data["rho"], _helper_bulk_data["rho_s"],
292
+ _helper_bulk_data["rho_f"]) = self.geophysics.calculate_bulk_density_data(
293
+ v_phi=_helper_bulk_data["porosity"], val_rho=_helper_bulk_data["rho"], val_rho_f=rho_f,
294
+ val_n=n)
295
+ # Update bulk seismic velocity data
296
+ (_helper_bulk_data["vP"], _helper_bulk_data["vS"],
297
+ _helper_bulk_data["vP/vS"]) = self.geophysics.calculate_seismic_velocities(
298
+ val_K=_helper_bulk_data["K"], val_G=_helper_bulk_data["G"], val_rho=_helper_bulk_data["rho"])
299
+ # Update elastic parameter data
300
+ (_helper_bulk_data["E"], _helper_bulk_data["poisson"],
301
+ _helper_bulk_data["lame"]) = self.geophysics.calculate_elastic_parameter_data(
302
+ val_K=_helper_bulk_data["K"], val_G=_helper_bulk_data["G"])
303
+
304
+ return _helper_bulk_data
305
+
306
+ def collect_initial_compositional_data(self, list_minerals, n):
307
+ _helper_elements = []
308
+ _helper_oxides = []
309
+ _mineral_data = self._collect_mineral_data(list_minerals=list_minerals, number=n)
310
+ _helper_elements = self._extract_element_data(data_minerals=_mineral_data, list_elements=_helper_elements)
311
+ _helper_oxides = self._extract_oxide_data(data_minerals=_mineral_data, list_oxides=_helper_oxides)
312
+
313
+ return _mineral_data, _helper_elements, _helper_oxides
314
+
315
+ def collect_initial_bulk_data(self, list_minerals, _mineral_data, _helper_composition):
316
+ _helper_bulk_data = {}
317
+ for property in ["rho", "K", "G", "GR", "PE"]:
318
+ _helper_property = self._extract_mineral_property_data(
319
+ list_minerals=list_minerals, data_mineral=_mineral_data, property=property)
320
+ _helper_bulk_data[property] = np.sum(_helper_composition*_helper_property.T, axis=1)
321
+
322
+ return _helper_bulk_data
323
+
324
+ def update_compositional_bulk_data(
325
+ self, _helper_elements, _helper_bulk_data, _mineral_data, _helper_composition, _helper_oxides,
326
+ _helper_mineral_amounts, n):
327
+ for element in _helper_elements:
328
+ _helper_bulk_data = self._update_chemistry_data(
329
+ _bulk_data=_helper_bulk_data, data_minerals=_mineral_data, data_composition=_helper_composition,
330
+ element=element, number=n)
331
+ # Update oxide data
332
+ _helper_bulk_data = self._update_oxide_data(bulk_data=_helper_bulk_data, list_oxides=_helper_oxides)
333
+ # Update rock composition data
334
+ _helper_bulk_data = self._assign_mineral_amounts(
335
+ bulk_data=_helper_bulk_data, data_amounts=_helper_mineral_amounts)
336
+
337
+ return _helper_bulk_data
338
+
339
+ def generate_dataset(self, number: int = 1, fluid: str = "water", density_fluid=None) -> None:
340
+ if density_fluid is None:
341
+ if fluid == "water":
342
+ density_fluid = 1000
343
+ elif fluid == "oil":
344
+ density_fluid = 800
345
+ elif fluid == "natural gas":
346
+ density_fluid = 750
347
+
348
+ siliciclastics = {"Sandstone"}
349
+ data_yaml = self._load_yaml(rock_name=self.name)
350
+ min_porosity = data_yaml["physical_properties"]["porosity"]["min"]
351
+ max_porosity = data_yaml["physical_properties"]["porosity"]["max"]
352
+ porosity = self.rng.uniform(min_porosity, max_porosity, number)
353
+ mineral_limits = AnisotropicRocks._mineralogy_cache[self.name]
354
+ _limits = {"lower": [], "upper": []}
355
+
356
+ for mineral, values in mineral_limits.items():
357
+ _limits["lower"].append(values[0])
358
+ _limits["upper"].append(values[1])
359
+
360
+ list_minerals = list(AnisotropicRocks._mineralogy_cache[self.name].keys())
361
+ _properties = ["rho", "vP", "vS", "K", "G", "GR", "PE"]
362
+ _bulk_data = {}
363
+ # Collect mineralogical composition data
364
+ n_minerals = len(list_minerals)
365
+ _helper_composition = np.zeros((number, n_minerals))
366
+ _helper_mineral_amounts = {mineral: np.zeros(number) for mineral in list_minerals}
367
+
368
+ _helper_composition = self._sample_bounded_simplex_batch(
369
+ min_vals=_limits["lower"], max_vals=_limits["upper"], number=number)
370
+ _helper_mineral_amounts = {mineral: _helper_composition[:, j] for j, mineral in enumerate(list_minerals)}
371
+
372
+ # Collect mineral data
373
+ _mineral_data, _helper_elements, _helper_oxides = self.collect_initial_compositional_data(
374
+ list_minerals=list_minerals, n=number)
375
+ # Collect bulk data
376
+ _helper_bulk_data = self.collect_initial_bulk_data(
377
+ list_minerals=list_minerals, _mineral_data=_mineral_data, _helper_composition=_helper_composition)
378
+ # Assign porosity data
379
+ _helper_bulk_data["porosity"] = porosity
380
+ # Collect geophysical data
381
+ _helper_bulk_data = self.collect_geophysical_properties(
382
+ _helper_bulk_data=_helper_bulk_data, rho_f=density_fluid, n=number)
383
+ # Update chemistry data
384
+ _helper_bulk_data = self.update_compositional_bulk_data(
385
+ _helper_elements=_helper_elements, _helper_bulk_data=_helper_bulk_data, _mineral_data=_mineral_data,
386
+ _helper_composition=_helper_composition, _helper_oxides=_helper_oxides,
387
+ _helper_mineral_amounts=_helper_mineral_amounts, n=number)
388
+ # Conversion to a pandas dataframe object
389
+ _bulk_data = pd.DataFrame(_helper_bulk_data)
390
+
391
+ return _bulk_data
392
+
393
+ # TEST
394
+ if __name__ == "__main__":
395
+ DEFAULT_DATA = AnisotropicRocks(name="Shale", random_seed=42).generate_dataset(number=10)
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env python
2
+ # -*-coding: utf-8 -*-
3
+
4
+ #-----------------------------------------------
5
+
6
+ # Name: common.py
7
+ # Author: Maximilian A. Beeskow
8
+ # Version: 1.0
9
+ # Date: 15.12.2025
10
+
11
+ #-----------------------------------------------
12
+
13
+ """
14
+ Module: common.py
15
+ This module contains several routines that are commonly used by the different rock-related modules.
16
+ """
17
+
18
+ # PACKAGES
19
+ import re
20
+
21
+ # MODULES
22
+ from ..chemistry.common import PeriodicSystem
23
+
24
+ class RockGeneration:
25
+ def __init__(self):
26
+ self.elements = {
27
+ "H": PeriodicSystem(name="H").get_data(),
28
+ "C": PeriodicSystem(name="C").get_data(),
29
+ "O": PeriodicSystem(name="O").get_data(),
30
+ "Na": PeriodicSystem(name="Na").get_data(),
31
+ "Mg": PeriodicSystem(name="Mg").get_data(),
32
+ "Al": PeriodicSystem(name="Al").get_data(),
33
+ "Si": PeriodicSystem(name="Si").get_data(),
34
+ "S": PeriodicSystem(name="S").get_data(),
35
+ "Cl": PeriodicSystem(name="Cl").get_data(),
36
+ "K": PeriodicSystem(name="K").get_data(),
37
+ "Ca": PeriodicSystem(name="Ca").get_data(),
38
+ "Mn": PeriodicSystem(name="Mn").get_data(),
39
+ "Fe": PeriodicSystem(name="Fe").get_data(),
40
+ "Ni": PeriodicSystem(name="Ni").get_data(),
41
+ "U": PeriodicSystem(name="U").get_data()}
42
+
43
+ def _parse_formula(self, formula: str):
44
+ pattern = r"([A-Z][a-z]?)(\d*)"
45
+ matches = re.findall(pattern, formula)
46
+
47
+ composition = {}
48
+ for elem, amount in matches:
49
+ amount = int(amount) if amount else 1
50
+ composition[elem] = composition.get(elem, 0) + amount
51
+
52
+ return composition
53
+
54
+ def _get_elements_of_compound(self, compound: str) -> str:
55
+ elements = re.findall(r"[A-Z][a-z]?", compound)
56
+ first = elements[0]
57
+ last = elements[-1]
58
+
59
+ return first, last
60
+
61
+ def _get_cation_element(self, oxide: str) -> str:
62
+ first = oxide[0]
63
+ if len(oxide) > 1 and oxide[1].islower():
64
+ return oxide[:2]
65
+
66
+ return first
67
+
68
+ def _get_anion_element(self, compound: str) -> str:
69
+ elements = re.findall(r"[A-Z][a-z]?", compound)
70
+ last = elements[-1]
71
+ return last
72
+
73
+ def _determine_oxide_conversion_factors(self):
74
+ list_oxides = [
75
+ "H2O", "CO", "CO2", "Na2O", "MgO", "Al2O3", "SiO2", "Cl2O", "K2O", "CaO", "MnO", "Mn2O3", "MnO2", "MnO3",
76
+ "Mn2O7", "FeO", "Fe2O3", "FeO3", "NiO", "Ni2O3", "TiO2", "Ti2O3", "VO", "V2O3", "VO2", "V2O10", "CrO",
77
+ "Cr2O3", "CrO3", "CoO", "Co2O3", "Cu2O", "CuO", "ZnO", "GeO2", "As2O3", "As2O10", "ZrO2", "Nb2O3", "Nb2O10",
78
+ "MoO", "Mo2O3", "MoO2", "Mo2O10", "MoO3", "CdO", "SnO", "SnO2", "Sb2O3", "Sb2O10", "TeO2", "TeO3", "Ta2O10",
79
+ "WO", "W2O3", "WO2", "W2O10", "WO3", "Au2O", "Au2O3", "Tl2O", "Tl2O3", "PbO", "PbO2", "Bi2O3", "Bi2O10",
80
+ "U2O3", "UO2", "U2O10", "UO3", "Nb2O5", "Ta2O5", "SO", "SO2", "SO3"]
81
+ mass_oxygen = self.elements["O"][2]
82
+ _conversion_factors = {}
83
+ for oxide in list_oxides:
84
+ _conversion_factors[oxide] = self._parse_formula(formula=oxide)
85
+ cation = self._get_cation_element(oxide=oxide)
86
+ if cation in self.elements:
87
+ mass_cation = self.elements[cation][2]
88
+ _conversion_factors[oxide]["factor"] = (_conversion_factors[oxide][cation]*mass_cation +
89
+ _conversion_factors[oxide]["O"]*mass_oxygen)/(
90
+ _conversion_factors[oxide][cation]*mass_cation)
91
+ else:
92
+ pass
93
+ #print(self.name, ": cation", cation, "not found in chemical container.")
94
+
95
+ return _conversion_factors
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env python
2
+ # -*-coding: utf-8 -*-
3
+
4
+ #-----------------------------------------------
5
+
6
+ # Name: config.py
7
+ # Author: Maximilian A. Beeskow
8
+ # Version: 1.0
9
+ # Date: 03.12.2025
10
+
11
+ #-----------------------------------------------
12
+
13
+ """
14
+ Module: config.py
15
+ Defines configuration parameters for synthetic rock data generation.
16
+ Used by analysis and synthesis modules within gebpy.core.rocks.
17
+ """
18
+
19
+ # PACKAGES
20
+ from typing import Optional
21
+
22
+ # CODE
23
+ class RockConfiguration:
24
+ """
25
+ Configuration of the rock data generation.
26
+ - name: rock name (e.g., 'Sandstone')
27
+ - n_datapoints: Number of generated data points (> 0)
28
+ - random_seed: integer seed; if None, defaults to 42
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ name: str,
34
+ n_datapoints: int,
35
+ random_seed: Optional[int] = None
36
+ ) -> None:
37
+ self.name = name
38
+ self.n_datapoints = n_datapoints
39
+ self.random_seed = 42 if random_seed is None else random_seed
40
+ self._validate()
41
+
42
+ def __repr__(self) -> str:
43
+ return (
44
+ f"<RockConfiguration name={self.name!r}, "
45
+ f"n_datapoints={self.n_datapoints}, "
46
+ f"random_seed={self.random_seed}>"
47
+ )
48
+
49
+ def set_name(self, new_name: str) -> None:
50
+ if not new_name:
51
+ raise ValueError("Rock name must not be empty. Please assign a valid rock name.")
52
+ self.name = new_name
53
+
54
+ def set_number_of_datapoints(self, new_n_datapoints: int) -> None:
55
+ if not isinstance(new_n_datapoints, int):
56
+ raise TypeError("n_datapoints must be an integer.")
57
+ if new_n_datapoints <= 0:
58
+ raise ValueError("The number of data points must be positive.")
59
+ self.n_datapoints = new_n_datapoints
60
+
61
+ def set_random_seed(self, new_random_seed: Optional[int]) -> None:
62
+ self.random_seed = 42 if new_random_seed is None else new_random_seed
63
+ if self.random_seed < 0:
64
+ raise ValueError("The random number seed must be >= 0 or None.")
65
+
66
+ def _validate(self) -> None:
67
+ if not self.name:
68
+ raise ValueError("Rock name must not be empty. Please assign a valid rock name.")
69
+ if not isinstance(self.n_datapoints, int):
70
+ raise TypeError("n_datapoints must be an integer.")
71
+ if self.n_datapoints <= 0:
72
+ raise ValueError("The number of data points must be positive.")
73
+ if self.random_seed < 0:
74
+ raise ValueError("The random number seed must be >= 0 or None.")
75
+
76
+ # DEFAULT EXAMPLE
77
+ DEFAULT_CONFIG = RockConfiguration(name="Sandstone", n_datapoints=10)