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,555 @@
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 mineral-related modules.
16
+ """
17
+
18
+ # PACKAGES
19
+ import numpy as np
20
+ import scipy, re
21
+
22
+ # MODULES
23
+ from ..chemistry.geochemistry import MineralChemistry
24
+ from ..physics.geophysics import WellLog as wg
25
+
26
+ # CODE
27
+ class GeophysicalProperties:
28
+ def __init__(self):
29
+ pass
30
+
31
+ def calculate_elastic_properties(self, bulk_mod, shear_mod):
32
+ E = (9*bulk_mod*shear_mod)/(3*bulk_mod + shear_mod) # Young's modulus
33
+ nu = (3*bulk_mod - 2*shear_mod)/(2*(3*bulk_mod + shear_mod)) # Poisson's ratio
34
+ return E, nu
35
+
36
+ def calculate_seismic_velocities(self, bulk_mod, shear_mod, rho):
37
+ vPvS = ((bulk_mod + 4/3*shear_mod)/shear_mod)**0.5 # vP/vS
38
+ vP = ((bulk_mod + 4/3*shear_mod)/rho)**0.5 # P-wave velocity vP
39
+ vS = (shear_mod/rho)**0.5 # S-wave velocity vS
40
+ return vPvS, vP, vS
41
+
42
+ def calculate_radiation_properties(self, constr_radiation, rho_electron):
43
+ gamma_ray = constr_radiation.calculate_gr() # Gamma ray
44
+ pe = constr_radiation.calculate_pe() # Photoelectricity Pe
45
+ U = pe*rho_electron*10**(-3) # Photoelectricity U
46
+ return gamma_ray, pe, U
47
+
48
+ class CrystallographicProperties:
49
+ def __init__(self):
50
+ pass
51
+
52
+ def calculate_molar_volume(self, constr_volume, constr_molar_volume, cell_z):
53
+ dataV = constr_volume
54
+ V = dataV.calculate_volume() # Cell volume
55
+ V_m = constr_molar_volume.calculate_molar_volume(volume_cell=V, z=cell_z) # Molar volume
56
+ return V, V_m
57
+
58
+ def calculate_mineral_density(self, constr_density):
59
+ rho = constr_density.calculate_bulk_density() # Density
60
+ return rho
61
+
62
+ def calculate_electron_density(self, constr_electron_density):
63
+ rho_e = constr_electron_density.calculate_electron_density() # Electron density
64
+ return rho_e
65
+
66
+ class CrystalPhysics:
67
+ def __init__(self, properties):
68
+ self.properties = properties
69
+ self.avogadro = scipy.constants.Avogadro
70
+
71
+ def calculate_bulk_density(self):
72
+ # properties = [ molar mass, formula unit, unit cell volume ]
73
+ M = self.properties[0] # in g/mol
74
+ Z = self.properties[1]
75
+ V = self.properties[2] # in cm^3
76
+
77
+ # density rho in kg/m^3
78
+ rho = (Z*M)/(V*self.avogadro)*1000
79
+
80
+ return rho
81
+
82
+ def calculate_electron_density(self):
83
+ # properties = [ elements, amounts, bulk density ]
84
+ Z = np.sum([self.properties[1][i]*self.properties[0][i][1] for i in range(len(self.properties[0]))])/np.sum(
85
+ self.properties[1])
86
+ A = np.sum([self.properties[1][i]*self.properties[0][i][2] for i in range(len(self.properties[0]))])/np.sum(
87
+ self.properties[1])
88
+
89
+ rho_b = self.properties[2]
90
+ rho_e = 2*Z/A * rho_b
91
+
92
+ return rho_e
93
+
94
+ def calculate_volume(self):
95
+ # properties = [ list of lattice lengths, list of lattice angles, crystal system ]
96
+ lenghts = self.properties[0] # in angstrom
97
+ angles = self.properties[1] # in degree
98
+ crystalsystem = self.properties[2]
99
+
100
+ if crystalsystem == "cubic":
101
+ a = lenghts[0]*10**(-8)
102
+ V = a**3
103
+ return V
104
+ elif crystalsystem == "tetragonal":
105
+ a = lenghts[0]*10**(-8)
106
+ c = lenghts[1]*10**(-8)
107
+ V = a**2 * c
108
+ return V
109
+ elif crystalsystem in ["hexagonal", "trigonal"]:
110
+ a = lenghts[0]*10**(-8)
111
+ c = lenghts[1]*10**(-8)
112
+ angle = 60
113
+ V = (a**2 * c)*np.sin(angle*np.pi/180)
114
+ return V
115
+ elif crystalsystem == "orthorhombic":
116
+ a = lenghts[0]*10**(-8)
117
+ b = lenghts[1]*10**(-8)
118
+ c = lenghts[2]*10**(-8)
119
+ V = a * b * c
120
+ return V
121
+ elif crystalsystem == "monoclinic":
122
+ a = lenghts[0]*10**(-8)
123
+ b = lenghts[1]*10**(-8)
124
+ c = lenghts[2]*10**(-8)
125
+ beta = angles[0]
126
+ V = (a * b * c)*np.sin(beta*np.pi/180)
127
+ return V
128
+ elif crystalsystem == "triclinic":
129
+ a = lenghts[0]*10**(-8)
130
+ b = lenghts[1]*10**(-8)
131
+ c = lenghts[2]*10**(-8)
132
+ alpha = angles[0]
133
+ beta = angles[1]
134
+ gamma = angles[2]
135
+ V = (a * b * c)*(1 - np.cos(alpha*np.pi/180)**2 - np.cos(beta*np.pi/180)**2 - np.cos(gamma*np.pi/180)**2 +
136
+ 2*(abs(np.cos(alpha*np.pi/180)*np.cos(beta*np.pi/180)*np.cos(gamma*np.pi/180))))**(0.5)
137
+ return V
138
+
139
+ class OxideComposition:
140
+ """
141
+ This class calculates the oxide composition of a certain mineral.
142
+ """
143
+ def __init__(self):
144
+ pass
145
+
146
+ def _get_cation_element(self, oxide: str) -> str:
147
+ first = oxide[0]
148
+ if len(oxide) > 1 and oxide[1].islower():
149
+ return oxide[:2]
150
+
151
+ return first
152
+
153
+ def _parse_formula(self, formula: str):
154
+ pattern = r"([A-Z][a-z]?)(\d*)"
155
+ matches = re.findall(pattern, formula)
156
+
157
+ composition = {}
158
+ for elem, amount in matches:
159
+ amount = int(amount) if amount else 1
160
+ composition[elem] = composition.get(elem, 0) + amount
161
+
162
+ return composition
163
+
164
+ def _determine_oxide_conversion_factors(self, elements):
165
+ list_oxides = [
166
+ "H2O", "CO", "CO2", "Na2O", "MgO", "Al2O3", "SiO2", "Cl2O", "K2O", "CaO", "MnO", "Mn2O3", "MnO2", "MnO3",
167
+ "Mn2O7", "FeO", "Fe2O3", "FeO3", "NiO", "Ni2O3", "TiO2", "Ti2O3", "VO", "V2O3", "VO2", "V2O10", "CrO",
168
+ "Cr2O3", "CrO3", "CoO", "Co2O3", "Cu2O", "CuO", "ZnO", "GeO2", "As2O3", "As2O10", "ZrO2", "Nb2O3", "Nb2O10",
169
+ "MoO", "Mo2O3", "MoO2", "Mo2O10", "MoO3", "CdO", "SnO", "SnO2", "Sb2O3", "Sb2O10", "TeO2", "TeO3", "Ta2O10",
170
+ "WO", "W2O3", "WO2", "W2O10", "WO3", "Au2O", "Au2O3", "Tl2O", "Tl2O3", "PbO", "PbO2", "Bi2O3", "Bi2O10",
171
+ "U2O3", "UO2", "U2O10", "UO3"]
172
+ mass_oxygen = elements["O"][2]
173
+ _conversion_factors = {}
174
+ for oxide in list_oxides:
175
+ _conversion_factors[oxide] = self._parse_formula(formula=oxide)
176
+ cation = self._get_cation_element(oxide=oxide)
177
+ if cation in elements:
178
+ mass_cation = elements[cation][2]
179
+ _conversion_factors[oxide]["factor"] = (_conversion_factors[oxide][cation]*mass_cation +
180
+ _conversion_factors[oxide]["O"]*mass_oxygen)/(
181
+ _conversion_factors[oxide][cation]*mass_cation)
182
+ else:
183
+ pass
184
+ #print(self.name, ": cation", cation, "not found in chemical container.")
185
+
186
+ return _conversion_factors
187
+
188
+ def _element_amounts_as_dict(self, amounts):
189
+ helper_dict = {}
190
+ for item in amounts:
191
+ helper_dict[item[0]] = item[2]
192
+
193
+ return helper_dict
194
+
195
+ class MineralGeneration:
196
+ """
197
+ This class controls the mineral data generation for minerals with fixed composition.
198
+ """
199
+ def __init__(self, name, yaml_data, elements, cache, geophysical_properties, rounding):
200
+ self.name = name
201
+ self.yaml_data = yaml_data
202
+ self.elements = elements
203
+ self.cache = cache
204
+ self.geophysical_properties = geophysical_properties
205
+ self.rounding = rounding
206
+ self.conversion_factors = self._determine_oxide_conversion_factors()
207
+
208
+ def _get_value(self, data: dict, path: list[str], default=None):
209
+ """Safely extract a float or string value from nested YAML data."""
210
+ try:
211
+ for key in path:
212
+ data = data[key]
213
+ if isinstance(data, dict) and "value" in data:
214
+ return float(data["value"])
215
+ else:
216
+ return data # kann z.B. ein String oder eine Zahl sein
217
+ except (KeyError, TypeError):
218
+ return default
219
+
220
+ def _determine_volume_constructor(self, vals):
221
+ val_a = vals["a"]
222
+ val_system = vals["crystal_system"]
223
+ if val_system in ["isometric", "cubic"]:
224
+ constr_vol = CrystalPhysics([[val_a], [], val_system])
225
+ elif val_system in ["tetragonal", "hexagonal", "trigonal"]:
226
+ val_c = vals["c"]
227
+ constr_vol = CrystalPhysics([[val_a, val_c], [], val_system])
228
+ elif val_system in ["orthorhombic"]:
229
+ val_b = vals["b"]
230
+ val_c = vals["c"]
231
+ constr_vol = CrystalPhysics([[val_a, val_b, val_c], [], val_system])
232
+ elif val_system in ["monoclinic"]:
233
+ val_b = vals["b"]
234
+ val_c = vals["c"]
235
+ val_beta = vals["beta"]
236
+ constr_vol = CrystalPhysics([[val_a, val_b, val_c], [val_beta], val_system])
237
+ elif val_system in ["triclinic"]:
238
+ val_b = vals["b"]
239
+ val_c = vals["c"]
240
+ val_alpha = vals["alpha"]
241
+ val_beta = vals["beta"]
242
+ val_gamma = vals["gamma"]
243
+ constr_vol = CrystalPhysics([[val_a, val_b, val_c], [val_alpha, val_beta, val_gamma], val_system])
244
+ return constr_vol
245
+
246
+ def _parse_formula(self, formula: str):
247
+ pattern = r"([A-Z][a-z]?)(\d*)"
248
+ matches = re.findall(pattern, formula)
249
+
250
+ composition = {}
251
+ for elem, amount in matches:
252
+ amount = int(amount) if amount else 1
253
+ composition[elem] = composition.get(elem, 0) + amount
254
+
255
+ return composition
256
+
257
+ def _get_cation_element(self, oxide: str) -> str:
258
+ first = oxide[0]
259
+ if len(oxide) > 1 and oxide[1].islower():
260
+ return oxide[:2]
261
+
262
+ return first
263
+
264
+ def _determine_sulfide_conversion_factors(self):
265
+ list_sulfides = ["FeS", "FeS2"]
266
+ mass_sulfur = self.elements["S"][2]
267
+ _conversion_factors = {}
268
+ for sulfide in list_sulfides:
269
+ _conversion_factors[sulfide] = self._parse_formula(formula=sulfide)
270
+ cation = self._get_cation_element(oxide=sulfide)
271
+ if cation in self.elements:
272
+ mass_cation = self.elements[cation][2]
273
+ _conversion_factors[sulfide]["factor"] = (_conversion_factors[sulfide][cation]*mass_cation +
274
+ _conversion_factors[sulfide]["S"]*mass_sulfur)/(
275
+ _conversion_factors[sulfide][cation]*mass_cation)
276
+ else:
277
+ pass
278
+ #print(self.name, ": cation", cation, "not found in chemical container.")
279
+
280
+ return _conversion_factors
281
+
282
+ def _determine_oxide_conversion_factors(self):
283
+ list_oxides = [
284
+ "H2O", "CO", "CO2", "Na2O", "MgO", "Al2O3", "SiO2", "Cl2O", "K2O", "CaO", "MnO", "Mn2O3", "MnO2", "MnO3",
285
+ "Mn2O7", "FeO", "Fe2O3", "FeO3", "NiO", "Ni2O3", "TiO2", "Ti2O3", "VO", "V2O3", "VO2", "V2O10", "CrO",
286
+ "Cr2O3", "CrO3", "CoO", "Co2O3", "Cu2O", "CuO", "ZnO", "GeO2", "As2O3", "As2O10", "ZrO2", "Nb2O3", "Nb2O10",
287
+ "MoO", "Mo2O3", "MoO2", "Mo2O10", "MoO3", "CdO", "SnO", "SnO2", "Sb2O3", "Sb2O10", "TeO2", "TeO3", "Ta2O10",
288
+ "WO", "W2O3", "WO2", "W2O10", "WO3", "Au2O", "Au2O3", "Tl2O", "Tl2O3", "PbO", "PbO2", "Bi2O3", "Bi2O10",
289
+ "U2O3", "UO2", "U2O10", "UO3", "Nb2O5", "Ta2O5"]
290
+ mass_oxygen = self.elements["O"][2]
291
+ _conversion_factors = {}
292
+ for oxide in list_oxides:
293
+ _conversion_factors[oxide] = self._parse_formula(formula=oxide)
294
+ cation = self._get_cation_element(oxide=oxide)
295
+ if cation in self.elements:
296
+ mass_cation = self.elements[cation][2]
297
+ _conversion_factors[oxide]["factor"] = (_conversion_factors[oxide][cation]*mass_cation +
298
+ _conversion_factors[oxide]["O"]*mass_oxygen)/(
299
+ _conversion_factors[oxide][cation]*mass_cation)
300
+ else:
301
+ pass
302
+ #print(self.name, ": cation", cation, "not found in chemical container.")
303
+
304
+ return _conversion_factors
305
+
306
+ def _element_amounts_as_dict(self, amounts):
307
+ helper_dict = {}
308
+ for item in amounts:
309
+ helper_dict[item[0]] = item[2]
310
+
311
+ return helper_dict
312
+
313
+ def _determine_majors_data(self):
314
+ majors_data = []
315
+ molar_mass_pure = 0
316
+ for element, amount in self.yaml_data["chemistry"].items():
317
+ n_order = int(self.elements[element][1])
318
+ val_amount = float(amount)
319
+ molar_mass = float(self.elements[element][2])
320
+ majors_data.append([element, n_order, val_amount, molar_mass])
321
+ molar_mass_pure += val_amount*molar_mass
322
+ majors_data.sort(key=lambda x: x[1])
323
+ return majors_data, molar_mass_pure
324
+
325
+ def _extract_values_from_yaml(self):
326
+ vals = {}
327
+ # Physical parameters
328
+ for key in ["K", "G", "a_K", "b_K", "a_G", "b_G"]:
329
+ if key in self.yaml_data.get("physical_properties", {}):
330
+ vals[key] = float(self.yaml_data["physical_properties"][key]["value"])
331
+ # Cell parameters
332
+ for key in ["a", "b", "c", "alpha", "beta", "gamma", "Z"]:
333
+ if key in self.yaml_data.get("cell_data", {}):
334
+ vals[key] = float(self.yaml_data["cell_data"][key]["value"])
335
+ # Meta data
336
+ vals["key"] = self.yaml_data["metadata"]["key"]
337
+ vals["crystal_system"] = self.yaml_data["metadata"]["crystal_system"]
338
+
339
+ return vals
340
+
341
+ def create_mineral_data_fixed_composition(self):
342
+ """
343
+ Synthetic mineral data generation for an user-selected mineral.
344
+ All mechanical properties (K, G, E) are stored in Pascals internally.
345
+ For output, they are converted to GPa.
346
+ """
347
+ name_lower = self.name.lower()
348
+ # Chemistry
349
+ val_state = "fixed"
350
+ traces_data = []
351
+ # Molar mass, elemental amounts
352
+ majors_data, molar_mass_pure = self._determine_majors_data()
353
+ if "oxides" in self.yaml_data:
354
+ oxides_data = {}
355
+ for oxide, amount in self.yaml_data["oxides"].items():
356
+ cation = self._get_cation_element(oxide=oxide)
357
+ oxides_data[oxide] = [cation, None, amount]
358
+ elif "sulfides" in self.yaml_data:
359
+ self.conversion_factors = self._determine_sulfide_conversion_factors()
360
+ oxides_data = {}
361
+ for oxide, amount in self.yaml_data["sulfides"].items():
362
+ cation = self._get_cation_element(oxide=oxide)
363
+ oxides_data[oxide] = [cation, None, amount]
364
+
365
+ if name_lower not in self.cache:
366
+ vals = self._extract_values_from_yaml()
367
+ constr_minchem = MineralChemistry(w_traces=traces_data, molar_mass_pure=molar_mass_pure, majors=majors_data)
368
+ self.cache[name_lower] = {
369
+ "majors_data": majors_data,
370
+ "molar_mass_pure": molar_mass_pure,
371
+ "constants": vals,
372
+ "MineralChemistry": constr_minchem
373
+ }
374
+ else:
375
+ vals = self.cache[name_lower]["constants"]
376
+ constr_minchem = self.cache[name_lower]["MineralChemistry"]
377
+ constr_electr_density = self.cache[name_lower]["const_electron_density"]
378
+ constr_vol = self.cache[name_lower]["constr_volume"]
379
+ constr_density = self.cache[name_lower]["constr_density"]
380
+ constr_radiation = self.cache[name_lower]["constr_radiation"]
381
+
382
+ # Reading and assigning the mineral-specific information from the YAML file
383
+ val_key = vals["key"]
384
+ val_Z = vals["Z"]
385
+ if "K" in vals:
386
+ val_K = vals["K"]
387
+ val_G = vals["G"]
388
+ else:
389
+ val_a_K = float(vals["a_K"])
390
+ val_b_K = float(vals["b_K"])
391
+ val_a_G = float(vals["a_G"])
392
+ val_b_G = float(vals["b_G"])
393
+
394
+ molar_mass, amounts = constr_minchem.calculate_molar_mass()
395
+ amounts_dict = self._element_amounts_as_dict(amounts=amounts)
396
+ element = [self.elements[name] for name, *_ in amounts]
397
+ for oxide in oxides_data.keys():
398
+ cation = self._get_cation_element(oxide=oxide)
399
+ weight = oxides_data[oxide][2]
400
+ if weight != 1:
401
+ value = weight
402
+ else:
403
+ try:
404
+ value = amounts_dict[cation]*self.conversion_factors[oxide]["factor"]
405
+ except:
406
+ value = 0
407
+ oxides_data[oxide][1] = value
408
+ # (Molar) Volume
409
+ if "constr_volume" not in self.cache[name_lower]:
410
+ constr_vol = self._determine_volume_constructor(vals=vals)
411
+ self.cache[name_lower]["constr_volume"] = constr_vol
412
+
413
+ V, V_m = CrystallographicProperties().calculate_molar_volume(
414
+ constr_volume=constr_vol, constr_molar_volume=constr_minchem, cell_z=val_Z)
415
+ # Density
416
+ if "constr_density" not in self.cache[name_lower]:
417
+ constr_density = CrystalPhysics([molar_mass, val_Z, V])
418
+ self.cache[name_lower]["constr_density"] = constr_density
419
+
420
+ rho = CrystallographicProperties().calculate_mineral_density(constr_density=constr_density)
421
+
422
+ if "const_electron_density" not in self.cache[name_lower]:
423
+ constr_electr_density = wg(amounts=amounts, elements=element, rho_b=rho)
424
+ self.cache[name_lower]["const_electron_density"] = constr_electr_density
425
+
426
+ rho_e = CrystallographicProperties().calculate_electron_density(constr_electron_density=constr_electr_density)
427
+ # Elastic properties
428
+ if "K" not in vals:
429
+ val_K = (val_a_K*rho + val_b_K)*10**9
430
+ val_G = (val_a_G*rho + val_b_G)*10**9
431
+ E, nu = self.geophysical_properties.calculate_elastic_properties(bulk_mod=val_K, shear_mod=val_G)
432
+ # Seismic properties
433
+ vPvS, vP, vS = self.geophysical_properties.calculate_seismic_velocities(
434
+ bulk_mod=val_K, shear_mod=val_G, rho=rho)
435
+ # Radiation properties
436
+ if "constr_radiation" not in self.cache[name_lower]:
437
+ constr_radiation = wg(amounts=amounts, elements=element)
438
+ self.cache[name_lower]["constr_radiation"] = constr_radiation
439
+
440
+ gamma_ray, pe, U = self.geophysical_properties.calculate_radiation_properties(
441
+ constr_radiation=constr_radiation, rho_electron=rho_e)
442
+ # Electrical resistivity
443
+ p = None
444
+ # Results
445
+ results = {
446
+ "mineral": val_key, "state": val_state, "M": round(molar_mass, self.rounding),
447
+ "chemistry": {name: round(val[1], 6) for name, *val in amounts}, "rho": round(rho, self.rounding),
448
+ "rho_e": round(rho_e, self.rounding), "V": round(V_m, self.rounding), "vP": round(vP, self.rounding),
449
+ "vS": round(vS, self.rounding), "vP/vS": round(vPvS, self.rounding),
450
+ "K": round(val_K*10**(-9), self.rounding), "G": round(val_G*10**(-9), self.rounding),
451
+ "E": round(E*10**(-9), self.rounding), "nu": round(nu, 6), "GR": round(gamma_ray, self.rounding),
452
+ "PE": round(pe, self.rounding), "U": round(U, self.rounding), "p": p}
453
+ if "oxides" in self.yaml_data:
454
+ results["compounds"] = {name: round(val[1], 6) for name, val in oxides_data.items()}
455
+ elif "sulfides" in self.yaml_data:
456
+ results["compounds"] = {name: round(val[1], 6) for name, val in oxides_data.items()}
457
+
458
+ return results
459
+
460
+ def create_mineral_data_endmember_series(self, endmember_series, var_class, current_seed, rng):
461
+ """
462
+ Synthetic mineral data generation for an user-selected mineral.
463
+ All mechanical properties (K, G, E) are stored in Pascals internally.
464
+ For output, they are converted to GPa.
465
+ """
466
+ val_state = "variable"
467
+
468
+ if not hasattr(self, "cache"):
469
+ self.cache = {}
470
+
471
+ name_lower = endmember_series[self.name]["name_lower"]
472
+ val_key = endmember_series[self.name]["key"]
473
+ endmember = endmember_series[self.name]["endmembers"]
474
+ oxides_data = {}
475
+ for oxide in endmember_series[self.name]["oxides"]:
476
+ cation = self._get_cation_element(oxide=oxide)
477
+ oxides_data[oxide] = [cation, None]
478
+
479
+ if "endmembers" not in self.cache:
480
+ self.cache["endmembers"] = {}
481
+
482
+ if name_lower not in self.cache:
483
+ endmember_data = {}
484
+ list_elements = []
485
+ for mineral in endmember:
486
+ if mineral not in self.cache["endmembers"]:
487
+ mineral_data = var_class(name=mineral, random_seed=current_seed).generate_dataset(
488
+ number=1)
489
+ self.cache["endmembers"][mineral] = mineral_data
490
+ endmember_data[mineral] = self.cache["endmembers"][mineral]
491
+ mineral_data = endmember_data[mineral]
492
+ for element in mineral_data["chemistry"]:
493
+ if element not in list_elements:
494
+ list_elements.append(element)
495
+ constr_OxComp = OxideComposition()
496
+
497
+ self.cache[name_lower] = {
498
+ "endmember_data": endmember_data, "list_elements": list_elements, "OxComp": constr_OxComp}
499
+ else:
500
+ endmember_data = self.cache[name_lower]["endmember_data"]
501
+ list_elements = self.cache[name_lower]["list_elements"]
502
+ constr_OxComp = self.cache[name_lower]["OxComp"]
503
+ weights = rng.dirichlet(np.ones(len(endmember)))
504
+ fraction_endmember = dict(zip(endmember, weights))
505
+
506
+ properties = ["M", "rho", "rho_e", "V", "K", "G"]
507
+ helper_results = {
508
+ prop: sum(fraction_endmember[m]*endmember_data[m][prop][0] for m in endmember)
509
+ for prop in properties
510
+ }
511
+ # Amounts
512
+ amounts = []
513
+ for element in list_elements:
514
+ amount = sum(fraction_endmember[mineral]*endmember_data[mineral]["chemistry"].get(element, [0])[0]
515
+ for mineral in endmember)
516
+ amounts.append([element, self.elements[element][1], amount])
517
+ element = [self.elements[name] for name, *_ in amounts]
518
+ # Oxide amounts
519
+ amounts_dict = constr_OxComp._element_amounts_as_dict(amounts=amounts)
520
+ try:
521
+ for oxide in oxides_data.keys():
522
+ cation = constr_OxComp._get_cation_element(oxide=oxide)
523
+ value = amounts_dict[cation]*self.conversion_factors[oxide]["factor"]
524
+ oxides_data[oxide][1] = value
525
+ except:
526
+ print("No oxide data available!")
527
+ # Elastic properties
528
+ val_K = helper_results["K"]*10**9
529
+ val_G = helper_results["G"]*10**9
530
+ rho = helper_results["rho"]
531
+ rho_e = helper_results["rho_e"]
532
+ E, nu = self.geophysical_properties.calculate_elastic_properties(bulk_mod=val_K, shear_mod=val_G)
533
+ # Seismic properties
534
+ vPvS, vP, vS = self.geophysical_properties.calculate_seismic_velocities(
535
+ bulk_mod=val_K, shear_mod=val_G, rho=rho)
536
+ # Radiation properties
537
+ constr_radiation = wg(amounts=amounts, elements=element)
538
+ gamma_ray, pe, U = self.geophysical_properties.calculate_radiation_properties(
539
+ constr_radiation=constr_radiation, rho_electron=rho_e)
540
+ # Electrical resistivity
541
+ p = None
542
+ # Results
543
+ results = {
544
+ "mineral": val_key, "state": val_state, "M": round(helper_results["M"], self.rounding),
545
+ "chemistry": {name: round(val[1], 6) for name, *val in amounts}, "rho": round(rho, self.rounding),
546
+ "rho_e": round(rho_e, self.rounding), "V": round(helper_results["V"], self.rounding),
547
+ "vP": round(vP, self.rounding), "vS": round(vS, self.rounding),
548
+ "vP/vS": round(vPvS, self.rounding), "K": round(val_K*10**(-9), self.rounding),
549
+ "G": round(val_G*10**(-9), self.rounding), "E": round(E*10**(-9), self.rounding), "nu": round(nu, 6),
550
+ "GR": round(gamma_ray, self.rounding), "PE": round(pe, self.rounding), "U": round(U, self.rounding), "p": p}
551
+ try:
552
+ results["compounds"] = {name: round(val[1], 6) for name, val in oxides_data.items()}
553
+ except:
554
+ print("No oxide/sulfide data available!")
555
+ return results
@@ -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: 16.10.2025
10
+
11
+ #-----------------------------------------------
12
+
13
+ """
14
+ Module: config.py
15
+ Defines configuration parameters for synthetic mineral data generation.
16
+ Used by analysis and synthesis modules within gebpy.core.minerals.
17
+ """
18
+
19
+ # PACKAGES
20
+ from typing import Optional
21
+
22
+ # CODE
23
+ class MineralConfiguration:
24
+ """
25
+ Configuration of the mineral data generation.
26
+ - name: mineral name (e.g., 'Olivine')
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"<MineralConfiguration 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("Mineral name must not be empty. Please assign a valid mineral 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("Mineral name must not be empty. Please assign a valid mineral 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 = MineralConfiguration(name="Olivine", n_datapoints=10)
File without changes
File without changes
File without changes
File without changes
File without changes