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
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,412 @@
1
+ #!/usr/bin/env python
2
+ # -*-coding: utf-8 -*-
3
+
4
+ #-----------------------------------------------
5
+
6
+ # Name: carbonates.py
7
+ # Author: Maximilian A. Beeskow
8
+ # Version: 1.0
9
+ # Date: 15.12.2025
10
+
11
+ #-----------------------------------------------
12
+
13
+ """
14
+ Module: carbonates.py
15
+ This module controls the generation of the synthetic data of the carbonate minerals.
16
+ """
17
+
18
+ # PACKAGES
19
+ import yaml
20
+ import numpy as np
21
+ from pathlib import Path
22
+
23
+ from asteval import Interpreter
24
+
25
+ # MODULES
26
+ from ..chemistry.common import PeriodicSystem
27
+ from ..chemistry.geochemistry import MineralChemistry
28
+ from ..physics.geophysics import WellLog as wg
29
+
30
+ from .common import (
31
+ GeophysicalProperties,
32
+ CrystallographicProperties,
33
+ CrystalPhysics,
34
+ MineralGeneration as MinGen
35
+ )
36
+
37
+ # CODE
38
+ BASE_PATH = Path(__file__).resolve().parents[2]
39
+ DATA_PATH = BASE_PATH / "data_minerals"
40
+
41
+ class Carbonates:
42
+ _yaml_cache = {}
43
+ _formula_cache = {}
44
+ _minerals = {
45
+ "Calcite", "Dolomite", "Magnesite", "Siderite", "Rhodochrosite", "Aragonite", "Cerussite", "Ankerite",
46
+ "Azurite", "Malachite", "Ikaite", "Smithsonite"}
47
+
48
+ def __init__(self, name, random_seed, rounding=3) -> None:
49
+ self.name = name
50
+ self.random_seed = random_seed
51
+ self.rng = np.random.default_rng(random_seed)
52
+ self.current_seed = int(np.round(self.rng.uniform(0, 1000), 0))
53
+ self.data_path = DATA_PATH
54
+ self.rounding = rounding
55
+ self.ae = Interpreter()
56
+ self.cache = {}
57
+
58
+ # Chemistry
59
+ self.elements = {
60
+ "H": PeriodicSystem(name="H").get_data(),
61
+ "C": PeriodicSystem(name="C").get_data(),
62
+ "O": PeriodicSystem(name="O").get_data(),
63
+ "Mg": PeriodicSystem(name="Mg").get_data(),
64
+ "Ca": PeriodicSystem(name="Ca").get_data(),
65
+ "Mn": PeriodicSystem(name="Mn").get_data(),
66
+ "Fe": PeriodicSystem(name="Fe").get_data(),
67
+ "Cu": PeriodicSystem(name="Cu").get_data(),
68
+ "Zn": PeriodicSystem(name="Zn").get_data(),
69
+ "Pb": PeriodicSystem(name="Pb").get_data(),
70
+ }
71
+
72
+ # Geophysics
73
+ self.geophysical_properties = GeophysicalProperties()
74
+ # Crystallography
75
+ self.crystallographic_properties = CrystallographicProperties()
76
+
77
+ # Mineral-specific data
78
+ if self.name in [
79
+ "Calcite", "Dolomite", "Magnesite", "Siderite", "Rhodochrosite", "Aragonite", "Cerussite", "Ankerite",
80
+ "Azurite", "Malachite", "Ikaite", "Smithsonite"]:
81
+ self.yaml_data = self._load_yaml(self.name.lower())
82
+ if self.name == "Calcite-Group":
83
+ self.yaml_data = {
84
+ mineral.lower(): self._load_yaml(mineral.lower())
85
+ for mineral in ["Calcite", "Magnesite", "Siderite", "Rhodochrosite", "Smithsonite"]}
86
+ elif self.name == "Dolomite-Group":
87
+ self.yaml_data = {
88
+ mineral.lower(): self._load_yaml(mineral.lower())
89
+ for mineral in ["Dolomite", "Ankerite"]}
90
+
91
+ def _load_yaml(self, mineral_name: str) -> dict:
92
+ # 1) Cache-Hit
93
+ if mineral_name in Carbonates._yaml_cache:
94
+ return Carbonates._yaml_cache[mineral_name]
95
+
96
+ # 2) Laden von Disk
97
+ yaml_file = self.data_path/f"{mineral_name}.yaml"
98
+ if not yaml_file.exists():
99
+ raise FileNotFoundError(f"No YAML file found for {mineral_name}.")
100
+
101
+ with open(yaml_file, "r") as f:
102
+ data = yaml.safe_load(f)
103
+
104
+ if "chemistry" in data and mineral_name not in Carbonates._formula_cache:
105
+ self._compile_chemistry_formulas(mineral_name, data["chemistry"])
106
+
107
+ # 3) Cache schreiben
108
+ Carbonates._yaml_cache[mineral_name] = data
109
+
110
+ return data
111
+
112
+ def _get_value(self, data: dict, path: list[str], default=None):
113
+ """Safely extract a float or string value from nested YAML data."""
114
+ try:
115
+ for key in path:
116
+ data = data[key]
117
+ if isinstance(data, dict) and "value" in data:
118
+ return float(data["value"])
119
+ else:
120
+ return data # kann z.B. ein String oder eine Zahl sein
121
+ except (KeyError, TypeError):
122
+ return default
123
+
124
+ def _get_variables(self):
125
+ if "variables" not in self.yaml_data:
126
+ return {}
127
+ vars = {}
128
+ for k, v in self.yaml_data["variables"].items():
129
+ if isinstance(v[0], int):
130
+ vars[k] = self.rng.integers(v[0], v[1])
131
+ else:
132
+ vars[k] = round(self.rng.uniform(v[0], v[1]), 2)
133
+ return vars
134
+
135
+ def generate_dataset(self, number: int = 1, as_dataframe=False) -> None:
136
+ fixed = {
137
+ "Calcite", "Dolomite", "Magnesite", "Siderite", "Rhodochrosite", "Aragonite", "Cerussite", "Ankerite",
138
+ "Azurite", "Malachite", "Ikaite", "Smithsonite"}
139
+ variable = {}
140
+ endmember = {"Calcite-Group", "Dolomite-Group"}
141
+ generators = {
142
+ **{m: MinGen(
143
+ name=self.name, yaml_data=self.yaml_data, elements=self.elements, cache=self.cache,
144
+ geophysical_properties=self.geophysical_properties, rounding=self.rounding
145
+ ).create_mineral_data_fixed_composition for m in fixed},
146
+ **{m: self.create_mineral_data_variable_composition for m in variable},
147
+ **{m: self.create_mineral_data_endmember_series for m in endmember},
148
+ }
149
+
150
+ if self.name not in generators:
151
+ raise ValueError(f"Mineral '{self.name}' not recognized.")
152
+
153
+ dataset = {}
154
+ if self.name in fixed:
155
+ dataset = self._evaluate_mineral(index=1, generators=generators, dataset=dataset)
156
+ else:
157
+ for index in range(number):
158
+ dataset = self._evaluate_mineral(index=index, generators=generators, dataset=dataset)
159
+
160
+ if as_dataframe:
161
+ import pandas as pd
162
+ return pd.DataFrame(dataset)
163
+ else:
164
+ return dataset
165
+
166
+ def _evaluate_mineral(self, index, generators, dataset):
167
+ self.current_seed = np.uint32(self.random_seed + index)
168
+ data_mineral = generators[self.name]()
169
+
170
+ for key, value in data_mineral.items():
171
+ if key in ["M", "rho", "rho_e", "V", "vP", "vS", "vP/vS", "K", "G", "E", "nu", "GR", "PE", "U",
172
+ "p"]:
173
+ if key not in dataset:
174
+ dataset[key] = [value]
175
+ else:
176
+ dataset[key].append(value)
177
+ elif key in ["mineral", "state", "trace elements"] and key not in dataset:
178
+ dataset[key] = value
179
+ elif key in ["chemistry", "compounds"]:
180
+ if key not in dataset:
181
+ dataset[key] = {}
182
+ for key_2, value_2 in value.items():
183
+ dataset[key][key_2] = [value_2]
184
+ else:
185
+ for key_2, value_2 in value.items():
186
+ dataset[key][key_2].append(value_2)
187
+ return dataset
188
+
189
+ def _compile_chemistry_formulas(self, mineral_name: str, chemistry_dict: dict):
190
+ """
191
+ Extracts and compiles all chemistry formulas from the YAML file.
192
+ Stores the compiled ASTs in the global formula cache.
193
+ """
194
+
195
+ compiled = {}
196
+
197
+ for element, entry in chemistry_dict.items():
198
+
199
+ # Fall A: Einfache Zahl (z.B. 2, 3.5)
200
+ if isinstance(entry, (int, float)):
201
+ expr = str(entry)
202
+ # Fall B: dict mit 'formula'
203
+ elif isinstance(entry, dict) and "formula" in entry:
204
+ expr = str(entry["formula"])
205
+ # Fall C: Alles andere ist invalid
206
+ else:
207
+ raise ValueError(
208
+ f"Invalid chemistry entry for element '{element}' in {mineral_name}.yaml: {entry}")
209
+ # AST kompilieren
210
+ compiled[element] = self.ae.parse(expr)
211
+ # Cache schreiben
212
+ Carbonates._formula_cache[mineral_name] = compiled
213
+
214
+ def _evaluate_chemistry(self, chemistry_dict, **variables):
215
+ """
216
+ Evaluates algebraic expressions of element amounts defined in the YAML file.
217
+
218
+ Parameters:
219
+ chemistry_dict (dict): Dictionary from YAML containing element formulas as strings.
220
+ **variables: Variable assignments (e.g. x=0.5, y=1, n=8)
221
+
222
+ Returns:
223
+ dict: {element: calculated_amount}
224
+ """
225
+ self.ae.symtable.clear()
226
+
227
+ # Variablen setzen
228
+ for k, v in variables.items():
229
+ self.ae.symtable[k] = v
230
+
231
+ results = {}
232
+ compiled = Carbonates._formula_cache[self.name.lower()]
233
+
234
+ for el in chemistry_dict:
235
+ results[el] = self.ae.run(compiled[el])
236
+
237
+ return results
238
+
239
+ def _extract_values_from_yaml(self):
240
+ vals = {}
241
+ # Physical parameters
242
+ for key in ["K", "G", "a_K", "b_K", "a_G", "b_G"]:
243
+ if key in self.yaml_data.get("physical_properties", {}):
244
+ vals[key] = float(self.yaml_data["physical_properties"][key]["value"])
245
+ # Cell parameters
246
+ for key in ["a", "b", "c", "alpha", "beta", "gamma", "Z"]:
247
+ if key in self.yaml_data.get("cell_data", {}):
248
+ vals[key] = float(self.yaml_data["cell_data"][key]["value"])
249
+ # Meta data
250
+ vals["key"] = self.yaml_data["metadata"]["key"]
251
+ vals["crystal_system"] = self.yaml_data["metadata"]["crystal_system"]
252
+
253
+ return vals
254
+
255
+ def _determine_majors_data(self):
256
+ majors_data = []
257
+ molar_mass_pure = 0
258
+ vars = self._get_variables()
259
+ amounts_elements = self._evaluate_chemistry(self.yaml_data["chemistry"], **vars)
260
+ for element, amount in amounts_elements.items():
261
+ n_order = int(self.elements[element][1])
262
+ val_amount = float(amount)
263
+ molar_mass = float(self.elements[element][2])
264
+ majors_data.append([element, n_order, val_amount, molar_mass])
265
+ molar_mass_pure += val_amount*molar_mass
266
+ majors_data.sort(key=lambda x: x[1])
267
+ return majors_data, amounts_elements, molar_mass_pure, vars
268
+
269
+ def _calculate_molar_mass_amounts(self, amounts_elements):
270
+ molar_mass = 0
271
+ for element, amount in amounts_elements.items():
272
+ molar_mass += amount*float(self.elements[element][2])
273
+
274
+ amounts = []
275
+ for element, amount in amounts_elements.items():
276
+ value = amount*float(self.elements[element][2])/molar_mass
277
+ amounts.append([element, self.elements[element][1], value])
278
+ element = [self.elements[name] for name, *_ in amounts]
279
+ return molar_mass, amounts, element
280
+
281
+ def _determine_volume_constructor(self, vals):
282
+ val_a = vals["a"]
283
+ val_system = vals["crystal_system"]
284
+ if val_system in ["isometric", "cubic"]:
285
+ constr_vol = CrystalPhysics([[val_a], [], val_system])
286
+ elif val_system in ["tetragonal", "hexagonal", "trigonal"]:
287
+ val_c = vals["c"]
288
+ constr_vol = CrystalPhysics([[val_a, val_c], [], val_system])
289
+ elif val_system in ["orthorhombic"]:
290
+ val_b = vals["b"]
291
+ val_c = vals["c"]
292
+ constr_vol = CrystalPhysics([[val_a, val_b, val_c], [], val_system])
293
+ elif val_system in ["monoclinic"]:
294
+ val_b = vals["b"]
295
+ val_c = vals["c"]
296
+ val_beta = vals["beta"]
297
+ constr_vol = CrystalPhysics([[val_a, val_b, val_c], [val_beta], val_system])
298
+ elif val_system in ["triclinic"]:
299
+ val_b = vals["b"]
300
+ val_c = vals["c"]
301
+ val_alpha = vals["alpha"]
302
+ val_beta = vals["beta"]
303
+ val_gamma = vals["gamma"]
304
+ constr_vol = CrystalPhysics([[val_a, val_b, val_c], [val_alpha, val_beta, val_gamma], val_system])
305
+ return constr_vol
306
+
307
+ def create_mineral_data_variable_composition(self):
308
+ """
309
+ Synthetic mineral data generation for an user-selected mineral.
310
+ All mechanical properties (K, G, E) are stored in Pascals internally.
311
+ For output, they are converted to GPa.
312
+ """
313
+ name_lower = self.name.lower()
314
+ # Chemistry
315
+ val_state = "variable"
316
+ traces_data = []
317
+ # Molar mass, elemental amounts
318
+ majors_data, amounts_elements, molar_mass_pure, vars = self._determine_majors_data()
319
+
320
+ if name_lower not in self.cache:
321
+ vals = self._extract_values_from_yaml()
322
+ self.cache[name_lower] = {"constants": vals}
323
+ else:
324
+ vals = self.cache[name_lower]["constants"]
325
+ constr_vol = self.cache[name_lower]["constr_vol"]
326
+
327
+ # Molar mass, element amounts
328
+ molar_mass, amounts, element = self._calculate_molar_mass_amounts(amounts_elements=amounts_elements)
329
+
330
+ # Reading and assigning the mineral-specific information from the YAML file
331
+ val_key = vals["key"]
332
+ val_Z = vals["Z"]
333
+ if "K" in vals:
334
+ val_K = vals["K"]
335
+ val_G = vals["G"]
336
+ else:
337
+ val_a_K = float(vals["a_K"])
338
+ val_b_K = float(vals["b_K"])
339
+ val_a_G = float(vals["a_G"])
340
+ val_b_G = float(vals["b_G"])
341
+
342
+ # (Molar) Volume
343
+ if "constr_vol" not in self.cache[name_lower]:
344
+ constr_vol = self._determine_volume_constructor(vals=vals)
345
+ self.cache[name_lower]["constr_vol"] = constr_vol
346
+
347
+ constr_minchem = MineralChemistry(w_traces=traces_data, molar_mass_pure=molar_mass_pure, majors=majors_data)
348
+ V, V_m = self.crystallographic_properties.calculate_molar_volume(
349
+ constr_volume=constr_vol, constr_molar_volume=constr_minchem, cell_z=val_Z)
350
+ # Density
351
+ constr_density = CrystalPhysics([molar_mass, val_Z, V])
352
+ rho = self.crystallographic_properties.calculate_mineral_density(constr_density=constr_density)
353
+ constr_electr_density = wg(amounts=amounts, elements=element, rho_b=rho)
354
+ rho_e = self.crystallographic_properties.calculate_electron_density(
355
+ constr_electron_density=constr_electr_density)
356
+
357
+ # Elastic properties
358
+ if "K" not in vals:
359
+ val_K = (val_a_K*rho + val_b_K)*10**9
360
+ val_G = (val_a_G*rho + val_b_G)*10**9
361
+ E, nu = self.geophysical_properties.calculate_elastic_properties(bulk_mod=val_K, shear_mod=val_G)
362
+ # Seismic properties
363
+ vPvS, vP, vS = self.geophysical_properties.calculate_seismic_velocities(
364
+ bulk_mod=val_K, shear_mod=val_G, rho=rho)
365
+ # Radiation properties
366
+ constr_radiation = wg(amounts=amounts, elements=element)
367
+ gamma_ray, pe, U = self.geophysical_properties.calculate_radiation_properties(
368
+ constr_radiation=constr_radiation, rho_electron=rho_e)
369
+ # Electrical resistivity
370
+ p = None
371
+ # Results
372
+ results = {
373
+ "mineral": val_key, "state": val_state, "M": round(molar_mass, self.rounding),
374
+ "chemistry": {name: round(val[1], 6) for name, *val in amounts}, "rho": round(rho, self.rounding),
375
+ "rho_e": round(rho_e, self.rounding), "V": round(V_m, self.rounding), "vP": round(vP, self.rounding),
376
+ "vS": round(vS, self.rounding), "vP/vS": round(vPvS, self.rounding),
377
+ "K": round(val_K*10**(-9), self.rounding), "G": round(val_G*10**(-9), self.rounding),
378
+ "E": round(E*10**(-9), self.rounding), "nu": round(nu, 6), "GR": round(gamma_ray, self.rounding),
379
+ "PE": round(pe, self.rounding), "U": round(U, self.rounding), "p": p}
380
+ return results
381
+
382
+ def create_mineral_data_endmember_series(self):
383
+ """
384
+ Synthetic mineral data generation for a user-selected mineral.
385
+ All mechanical properties (K, G, E) are stored in Pascals internally.
386
+ For output, they are converted to GPa.
387
+ """
388
+ endmember_series = {
389
+ "Calcite-Group": {
390
+ "name_lower": "calcite-group",
391
+ "key": "Cal-group",
392
+ "endmembers": ["Calcite", "Magnesite", "Siderite", "Rhodochrosite", "Smithsonite"],
393
+ "oxides": ["CaO", "MgO", "FeO", "MnO", "ZnO", "CO2"]
394
+ },
395
+ "Dolomite-Group": {
396
+ "name_lower": "dolomite-group",
397
+ "key": "Dol-group",
398
+ "endmembers": ["Dolomite", "Ankerite"],
399
+ "oxides": ["CaO", "MgO", "FeO", "CO2"]
400
+ }
401
+ }
402
+ results = MinGen(
403
+ name=self.name, yaml_data=self.yaml_data, elements=self.elements, cache=self.cache,
404
+ geophysical_properties=self.geophysical_properties,
405
+ rounding=self.rounding).create_mineral_data_endmember_series(
406
+ endmember_series=endmember_series, var_class=Carbonates, current_seed=self.current_seed, rng=self.rng)
407
+
408
+ return results
409
+
410
+ # TEST
411
+ if __name__ == "__main__":
412
+ DEFAULT_DATA = Carbonates(name="Calcite", random_seed=42).generate_dataset(number=10)