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.
- gebpy/__init__.py +55 -0
- gebpy/__pycache__/__init__.cpython-310.pyc +0 -0
- gebpy/adapters/__init__.py +0 -0
- gebpy/cli/__init__.py +0 -0
- gebpy/core/__init__.py +0 -0
- gebpy/core/chemistry/__init__.py +0 -0
- gebpy/core/chemistry/common.py +1369 -0
- gebpy/core/chemistry/elements.py +317 -0
- gebpy/core/chemistry/geochemistry.py +1728 -0
- gebpy/core/fluids/__init__.py +0 -0
- gebpy/core/io/__init__.py +0 -0
- gebpy/core/mathematics/__init__.py +0 -0
- gebpy/core/minerals/__init__.py +0 -0
- gebpy/core/minerals/carbonates.py +412 -0
- gebpy/core/minerals/common.py +555 -0
- gebpy/core/minerals/config.py +77 -0
- gebpy/core/minerals/cyclosilicates.py +0 -0
- gebpy/core/minerals/halides.py +0 -0
- gebpy/core/minerals/inosilicates.py +0 -0
- gebpy/core/minerals/nesosilicates.py +0 -0
- gebpy/core/minerals/organics.py +0 -0
- gebpy/core/minerals/oxides.py +589 -0
- gebpy/core/minerals/phosphates.py +0 -0
- gebpy/core/minerals/phospides.py +0 -0
- gebpy/core/minerals/phyllosilicates.py +436 -0
- gebpy/core/minerals/sorosilicates.py +0 -0
- gebpy/core/minerals/sulfates.py +0 -0
- gebpy/core/minerals/sulfides.py +459 -0
- gebpy/core/minerals/synthesis.py +201 -0
- gebpy/core/minerals/tectosilicates.py +433 -0
- gebpy/core/physics/__init__.py +0 -0
- gebpy/core/physics/common.py +53 -0
- gebpy/core/physics/geophysics.py +351 -0
- gebpy/core/rocks/__init__.py +0 -0
- gebpy/core/rocks/anisotropic_rocks.py +395 -0
- gebpy/core/rocks/common.py +95 -0
- gebpy/core/rocks/config.py +77 -0
- gebpy/core/rocks/isotropic_rocks.py +395 -0
- gebpy/core/rocks/sedimentary.py +385 -0
- gebpy/core/subsurface/__init__.py +0 -0
- gebpy/data_minerals/__init__.py +0 -0
- gebpy/data_minerals/albite.yaml +59 -0
- gebpy/data_minerals/anatase.yaml +43 -0
- gebpy/data_minerals/ankerite.yaml +47 -0
- gebpy/data_minerals/annite.yaml +57 -0
- gebpy/data_minerals/anorthite.yaml +59 -0
- gebpy/data_minerals/antigorite.yaml +53 -0
- gebpy/data_minerals/aragonite.yaml +48 -0
- gebpy/data_minerals/argutite.yaml +43 -0
- gebpy/data_minerals/arsenolite.yaml +40 -0
- gebpy/data_minerals/au3oxide.yaml +46 -0
- gebpy/data_minerals/avicennite.yaml +40 -0
- gebpy/data_minerals/azurite.yaml +53 -0
- gebpy/data_minerals/baddeleyite.yaml +49 -0
- gebpy/data_minerals/bismite.yaml +49 -0
- gebpy/data_minerals/boehmite.yaml +48 -0
- gebpy/data_minerals/brookite.yaml +46 -0
- gebpy/data_minerals/brucite.yaml +45 -0
- gebpy/data_minerals/bunsenite.yaml +40 -0
- gebpy/data_minerals/calcite.yaml +45 -0
- gebpy/data_minerals/cassiterite.yaml +43 -0
- gebpy/data_minerals/cerussite.yaml +48 -0
- gebpy/data_minerals/chamosite.yaml +56 -0
- gebpy/data_minerals/chlorite.yaml +75 -0
- gebpy/data_minerals/chromite.yaml +42 -0
- gebpy/data_minerals/chrysotile.yaml +53 -0
- gebpy/data_minerals/claudetite.yaml +49 -0
- gebpy/data_minerals/clinochlore.yaml +55 -0
- gebpy/data_minerals/cochromite.yaml +42 -0
- gebpy/data_minerals/corundum.yaml +43 -0
- gebpy/data_minerals/crocoite.yaml +51 -0
- gebpy/data_minerals/cuprite.yaml +40 -0
- gebpy/data_minerals/cuprospinel.yaml +42 -0
- gebpy/data_minerals/diaspore.yaml +48 -0
- gebpy/data_minerals/dolomite.yaml +47 -0
- gebpy/data_minerals/eastonite.yaml +57 -0
- gebpy/data_minerals/eskolaite.yaml +43 -0
- gebpy/data_minerals/fechlorite.yaml +61 -0
- gebpy/data_minerals/fecolumbite.yaml +48 -0
- gebpy/data_minerals/ferberite.yaml +51 -0
- gebpy/data_minerals/fetantalite.yaml +48 -0
- gebpy/data_minerals/franklinite.yaml +42 -0
- gebpy/data_minerals/gahnite.yaml +42 -0
- gebpy/data_minerals/galaxite.yaml +42 -0
- gebpy/data_minerals/geikielite.yaml +45 -0
- gebpy/data_minerals/gibbsite.yaml +51 -0
- gebpy/data_minerals/glauconite.yaml +69 -0
- gebpy/data_minerals/goethite.yaml +48 -0
- gebpy/data_minerals/groutite.yaml +48 -0
- gebpy/data_minerals/hematite.yaml +43 -0
- gebpy/data_minerals/hercynite.yaml +42 -0
- gebpy/data_minerals/huebnerite.yaml +51 -0
- gebpy/data_minerals/ikaite.yaml +53 -0
- gebpy/data_minerals/illite.yaml +55 -0
- gebpy/data_minerals/ilmenite.yaml +45 -0
- gebpy/data_minerals/jacobsite.yaml +42 -0
- gebpy/data_minerals/kalsilite.yaml +47 -0
- gebpy/data_minerals/kaolinite.yaml +59 -0
- gebpy/data_minerals/karelianite.yaml +43 -0
- gebpy/data_minerals/lime.yaml +40 -0
- gebpy/data_minerals/litharge.yaml +43 -0
- gebpy/data_minerals/magnesiochromite.yaml +42 -0
- gebpy/data_minerals/magnesioferrite.yaml +42 -0
- gebpy/data_minerals/magnesite.yaml +45 -0
- gebpy/data_minerals/magnetite.yaml +41 -0
- gebpy/data_minerals/malachite.yaml +53 -0
- gebpy/data_minerals/manganite.yaml +51 -0
- gebpy/data_minerals/manganochromite.yaml +42 -0
- gebpy/data_minerals/manganosite.yaml +40 -0
- gebpy/data_minerals/marialite.yaml +49 -0
- gebpy/data_minerals/massicot.yaml +46 -0
- gebpy/data_minerals/meionite.yaml +49 -0
- gebpy/data_minerals/mgchlorite.yaml +61 -0
- gebpy/data_minerals/mgcolumbite.yaml +48 -0
- gebpy/data_minerals/mgtantalite.yaml +48 -0
- gebpy/data_minerals/microcline.yaml +59 -0
- gebpy/data_minerals/minium.yaml +44 -0
- gebpy/data_minerals/mnchlorite.yaml +61 -0
- gebpy/data_minerals/mncolumbite.yaml +48 -0
- gebpy/data_minerals/mntantalite.yaml +48 -0
- gebpy/data_minerals/monteponite.yaml +40 -0
- gebpy/data_minerals/montmorillonite.yaml +77 -0
- gebpy/data_minerals/muscovite.yaml +55 -0
- gebpy/data_minerals/nanepheline.yaml +47 -0
- gebpy/data_minerals/nichlorite.yaml +61 -0
- gebpy/data_minerals/nichromite.yaml +42 -0
- gebpy/data_minerals/nimite.yaml +55 -0
- gebpy/data_minerals/nontronite.yaml +73 -0
- gebpy/data_minerals/orthoclase.yaml +53 -0
- gebpy/data_minerals/paratellurite.yaml +43 -0
- gebpy/data_minerals/pennantite.yaml +61 -0
- gebpy/data_minerals/periclase.yaml +40 -0
- gebpy/data_minerals/phlogopite.yaml +57 -0
- gebpy/data_minerals/plattnerite.yaml +43 -0
- gebpy/data_minerals/powellite.yaml +45 -0
- gebpy/data_minerals/pyrite.yaml +40 -0
- gebpy/data_minerals/pyrolusite.yaml +43 -0
- gebpy/data_minerals/pyrophanite.yaml +45 -0
- gebpy/data_minerals/pyrophyllite.yaml +59 -0
- gebpy/data_minerals/quartz.yaml +43 -0
- gebpy/data_minerals/rhodochrosite.yaml +45 -0
- gebpy/data_minerals/rutile.yaml +43 -0
- gebpy/data_minerals/saponite.yaml +77 -0
- gebpy/data_minerals/scheelite.yaml +45 -0
- gebpy/data_minerals/scrutinyite.yaml +46 -0
- gebpy/data_minerals/senarmontite.yaml +40 -0
- gebpy/data_minerals/siderite.yaml +45 -0
- gebpy/data_minerals/siderophyllite.yaml +57 -0
- gebpy/data_minerals/smithsonite.yaml +45 -0
- gebpy/data_minerals/spinel.yaml +42 -0
- gebpy/data_minerals/stishovite.yaml +43 -0
- gebpy/data_minerals/stolzite.yaml +45 -0
- gebpy/data_minerals/talc.yaml +53 -0
- gebpy/data_minerals/tistarite.yaml +43 -0
- gebpy/data_minerals/trevorite.yaml +42 -0
- gebpy/data_minerals/ulvoespinel.yaml +42 -0
- gebpy/data_minerals/uraninite.yaml +40 -0
- gebpy/data_minerals/valentinite.yaml +46 -0
- gebpy/data_minerals/vermiculite.yaml +69 -0
- gebpy/data_minerals/wulfenite.yaml +45 -0
- gebpy/data_minerals/wustite.yaml +40 -0
- gebpy/data_minerals/zincite.yaml +43 -0
- gebpy/data_minerals/zincochromite.yaml +42 -0
- gebpy/data_rocks/__init__.py +0 -0
- gebpy/data_rocks/dolostone.yaml +40 -0
- gebpy/data_rocks/limestone.yaml +40 -0
- gebpy/data_rocks/marl.yaml +50 -0
- gebpy/data_rocks/sandstone.yaml +39 -0
- gebpy/data_rocks/shale.yaml +50 -0
- gebpy/gebpy_app.py +8732 -0
- gebpy/gui/__init__.py +0 -0
- gebpy/lib/images/GebPy_Header.png +0 -0
- gebpy/lib/images/GebPy_Icon.png +0 -0
- gebpy/lib/images/GebPy_Logo.png +0 -0
- gebpy/main.py +29 -0
- gebpy/modules/__init__.py +0 -0
- gebpy/modules/__pycache__/__init__.cpython-310.pyc +0 -0
- gebpy/modules/__pycache__/metamorphics.cpython-310.pyc +0 -0
- gebpy/modules/__pycache__/silicates.cpython-310.pyc +0 -0
- gebpy/modules/carbonates.py +2658 -0
- gebpy/modules/chemistry.py +1369 -0
- gebpy/modules/core.py +1805 -0
- gebpy/modules/elements.py +317 -0
- gebpy/modules/evaporites.py +1299 -0
- gebpy/modules/exploration.py +1145 -0
- gebpy/modules/fluids.py +339 -0
- gebpy/modules/geochemistry.py +1727 -0
- gebpy/modules/geophysics.py +351 -0
- gebpy/modules/gui.py +9093 -0
- gebpy/modules/gui_elements.py +145 -0
- gebpy/modules/halides.py +485 -0
- gebpy/modules/igneous.py +2241 -0
- gebpy/modules/metamorphics.py +3222 -0
- gebpy/modules/mineralogy.py +442 -0
- gebpy/modules/minerals.py +7954 -0
- gebpy/modules/ore.py +1648 -0
- gebpy/modules/organics.py +530 -0
- gebpy/modules/oxides.py +9057 -0
- gebpy/modules/petrophysics.py +98 -0
- gebpy/modules/phosphates.py +589 -0
- gebpy/modules/phospides.py +194 -0
- gebpy/modules/plotting.py +619 -0
- gebpy/modules/pyllosilicates.py +380 -0
- gebpy/modules/sedimentary_rocks.py +908 -0
- gebpy/modules/sequences.py +2166 -0
- gebpy/modules/series.py +1625 -0
- gebpy/modules/silicates.py +11102 -0
- gebpy/modules/siliciclastics.py +1846 -0
- gebpy/modules/subsurface_2d.py +179 -0
- gebpy/modules/sulfates.py +1629 -0
- gebpy/modules/sulfides.py +4786 -0
- gebpy/plotting/__init__.py +0 -0
- gebpy/ui_nb/__init__.py +0 -0
- gebpy/user_data/.gitkeep +0 -0
- gebpy-1.1.3.dist-info/LICENSE +165 -0
- gebpy-1.1.3.dist-info/METADATA +207 -0
- gebpy-1.1.3.dist-info/RECORD +254 -0
- gebpy-1.1.3.dist-info/WHEEL +5 -0
- gebpy-1.1.3.dist-info/entry_points.txt +2 -0
- gebpy-1.1.3.dist-info/top_level.txt +1 -0
- modules/__init__.py +0 -0
- modules/carbonates.py +2658 -0
- modules/chemistry.py +1369 -0
- modules/core.py +1805 -0
- modules/elements.py +317 -0
- modules/evaporites.py +1299 -0
- modules/exploration.py +765 -0
- modules/fluids.py +339 -0
- modules/geochemistry.py +1727 -0
- modules/geophysics.py +337 -0
- modules/gui.py +9093 -0
- modules/gui_elements.py +145 -0
- modules/halides.py +485 -0
- modules/igneous.py +2196 -0
- modules/metamorphics.py +2699 -0
- modules/mineralogy.py +442 -0
- modules/minerals.py +7954 -0
- modules/ore.py +1628 -0
- modules/organics.py +530 -0
- modules/oxides.py +9057 -0
- modules/petrophysics.py +98 -0
- modules/phosphates.py +589 -0
- modules/phospides.py +194 -0
- modules/plotting.py +619 -0
- modules/pyllosilicates.py +380 -0
- modules/sedimentary_rocks.py +908 -0
- modules/sequences.py +2166 -0
- modules/series.py +1625 -0
- modules/silicates.py +11102 -0
- modules/siliciclastics.py +1830 -0
- modules/subsurface_2d.py +179 -0
- modules/sulfates.py +1629 -0
- modules/sulfides.py +4786 -0
- notebooks/__init__.py +0 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*-coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
#-----------------------------------------------
|
|
5
|
+
|
|
6
|
+
# Name: sulfides.py
|
|
7
|
+
# Author: Maximilian A. Beeskow
|
|
8
|
+
# Version: 1.0
|
|
9
|
+
# Date: 15.12.2025
|
|
10
|
+
|
|
11
|
+
#-----------------------------------------------
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
Module: sulfides.py
|
|
15
|
+
This module controls the generation of the synthetic data of the sulfide 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 Sulfides:
|
|
42
|
+
_yaml_cache = {}
|
|
43
|
+
_formula_cache = {}
|
|
44
|
+
_minerals = {
|
|
45
|
+
"Acanthite", "Bornite", "Cattierite", "Chalcocite", "Chalcopyrite", "Cinnabar", "Cobaltite", "Covellite",
|
|
46
|
+
"Fahlore", "Galena", "Gallite", "Laforetite", "Lenaite", "Marcasite", "Marmatite", "Millerite", "Molybdenite",
|
|
47
|
+
"Orpiment", "Pentlandite", "Pyrite", "Pyrrhotite", "Realgar", "Roquesite", "Sphalerite", "Stibnite", "Vaesite"}
|
|
48
|
+
|
|
49
|
+
def __init__(self, name, random_seed, rounding=3) -> None:
|
|
50
|
+
self.name = name
|
|
51
|
+
self.random_seed = random_seed
|
|
52
|
+
self.rng = np.random.default_rng(random_seed)
|
|
53
|
+
self.current_seed = int(np.round(self.rng.uniform(0, 1000), 0))
|
|
54
|
+
self.data_path = DATA_PATH
|
|
55
|
+
self.rounding = rounding
|
|
56
|
+
self.ae = Interpreter()
|
|
57
|
+
self.cache = {}
|
|
58
|
+
|
|
59
|
+
# Chemistry
|
|
60
|
+
self.elements = {
|
|
61
|
+
"H": PeriodicSystem(name="H").get_data(),
|
|
62
|
+
"C": PeriodicSystem(name="C").get_data(),
|
|
63
|
+
"O": PeriodicSystem(name="O").get_data(),
|
|
64
|
+
"Mg": PeriodicSystem(name="Mg").get_data(),
|
|
65
|
+
"Ca": PeriodicSystem(name="Ca").get_data(),
|
|
66
|
+
"S": PeriodicSystem(name="S").get_data(),
|
|
67
|
+
"Mn": PeriodicSystem(name="Mn").get_data(),
|
|
68
|
+
"Fe": PeriodicSystem(name="Fe").get_data(),
|
|
69
|
+
"Cu": PeriodicSystem(name="Cu").get_data(),
|
|
70
|
+
"Zn": PeriodicSystem(name="Zn").get_data(),
|
|
71
|
+
"Pb": PeriodicSystem(name="Pb").get_data(),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Geophysics
|
|
75
|
+
self.geophysical_properties = GeophysicalProperties()
|
|
76
|
+
# Crystallography
|
|
77
|
+
self.crystallographic_properties = CrystallographicProperties()
|
|
78
|
+
|
|
79
|
+
# Mineral-specific data
|
|
80
|
+
if self.name in [
|
|
81
|
+
"Acanthite", "Bornite", "Cattierite", "Chalcocite", "Chalcopyrite", "Cinnabar", "Cobaltite", "Covellite",
|
|
82
|
+
"Fahlore", "Galena", "Gallite", "Laforetite", "Lenaite", "Marcasite", "Marmatite", "Millerite",
|
|
83
|
+
"Molybdenite", "Orpiment", "Pentlandite", "Pyrite", "Pyrrhotite", "Realgar", "Roquesite", "Sphalerite",
|
|
84
|
+
"Stibnite", "Vaesite"]:
|
|
85
|
+
self.yaml_data = self._load_yaml(self.name.lower())
|
|
86
|
+
|
|
87
|
+
def _load_yaml(self, mineral_name: str) -> dict:
|
|
88
|
+
# 1) Cache-Hit
|
|
89
|
+
if mineral_name in Sulfides._yaml_cache:
|
|
90
|
+
return Sulfides._yaml_cache[mineral_name]
|
|
91
|
+
|
|
92
|
+
# 2) Laden von Disk
|
|
93
|
+
yaml_file = self.data_path/f"{mineral_name}.yaml"
|
|
94
|
+
if not yaml_file.exists():
|
|
95
|
+
raise FileNotFoundError(f"No YAML file found for {mineral_name}.")
|
|
96
|
+
|
|
97
|
+
with open(yaml_file, "r") as f:
|
|
98
|
+
data = yaml.safe_load(f)
|
|
99
|
+
|
|
100
|
+
if "chemistry" in data and mineral_name not in Sulfides._formula_cache:
|
|
101
|
+
self._compile_chemistry_formulas(mineral_name, data["chemistry"])
|
|
102
|
+
|
|
103
|
+
# 3) Cache schreiben
|
|
104
|
+
Sulfides._yaml_cache[mineral_name] = data
|
|
105
|
+
|
|
106
|
+
return data
|
|
107
|
+
|
|
108
|
+
def _get_value(self, data: dict, path: list[str], default=None):
|
|
109
|
+
"""Safely extract a float or string value from nested YAML data."""
|
|
110
|
+
try:
|
|
111
|
+
for key in path:
|
|
112
|
+
data = data[key]
|
|
113
|
+
if isinstance(data, dict) and "value" in data:
|
|
114
|
+
return float(data["value"])
|
|
115
|
+
else:
|
|
116
|
+
return data # kann z.B. ein String oder eine Zahl sein
|
|
117
|
+
except (KeyError, TypeError):
|
|
118
|
+
return default
|
|
119
|
+
|
|
120
|
+
def _get_variables(self):
|
|
121
|
+
if "variables" not in self.yaml_data:
|
|
122
|
+
return {}
|
|
123
|
+
vars = {}
|
|
124
|
+
for k, v in self.yaml_data["variables"].items():
|
|
125
|
+
if isinstance(v[0], int):
|
|
126
|
+
vars[k] = self.rng.integers(v[0], v[1])
|
|
127
|
+
else:
|
|
128
|
+
vars[k] = round(self.rng.uniform(v[0], v[1]), 2)
|
|
129
|
+
return vars
|
|
130
|
+
|
|
131
|
+
def generate_dataset(self, number: int = 1, as_dataframe=False) -> None:
|
|
132
|
+
fixed = {
|
|
133
|
+
"Acanthite", "Bornite", "Cattierite", "Chalcocite", "Chalcopyrite", "Cinnabar", "Cobaltite", "Covellite",
|
|
134
|
+
"Fahlore", "Galena", "Gallite", "Laforetite", "Lenaite", "Marcasite", "Marmatite", "Millerite",
|
|
135
|
+
"Molybdenite", "Orpiment", "Pentlandite", "Pyrite", "Pyrrhotite", "Realgar", "Roquesite", "Sphalerite",
|
|
136
|
+
"Stibnite", "Vaesite"}
|
|
137
|
+
variable = {}
|
|
138
|
+
endmember = {}
|
|
139
|
+
generators = {
|
|
140
|
+
**{m: MinGen(
|
|
141
|
+
name=self.name, yaml_data=self.yaml_data, elements=self.elements, cache=self.cache,
|
|
142
|
+
geophysical_properties=self.geophysical_properties, rounding=self.rounding
|
|
143
|
+
).create_mineral_data_fixed_composition for m in fixed},
|
|
144
|
+
**{m: self.create_mineral_data_variable_composition for m in variable},
|
|
145
|
+
**{m: self.create_mineral_data_endmember_series for m in endmember},
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if self.name not in generators:
|
|
149
|
+
raise ValueError(f"Mineral '{self.name}' not recognized.")
|
|
150
|
+
|
|
151
|
+
dataset = {}
|
|
152
|
+
if self.name in fixed:
|
|
153
|
+
dataset = self._evaluate_mineral(index=1, generators=generators, dataset=dataset)
|
|
154
|
+
else:
|
|
155
|
+
for index in range(number):
|
|
156
|
+
dataset = self._evaluate_mineral(index=index, generators=generators, dataset=dataset)
|
|
157
|
+
|
|
158
|
+
if as_dataframe:
|
|
159
|
+
import pandas as pd
|
|
160
|
+
return pd.DataFrame(dataset)
|
|
161
|
+
else:
|
|
162
|
+
return dataset
|
|
163
|
+
|
|
164
|
+
def _evaluate_mineral(self, index, generators, dataset):
|
|
165
|
+
self.current_seed = np.uint32(self.random_seed + index)
|
|
166
|
+
data_mineral = generators[self.name]()
|
|
167
|
+
|
|
168
|
+
for key, value in data_mineral.items():
|
|
169
|
+
if key in ["M", "rho", "rho_e", "V", "vP", "vS", "vP/vS", "K", "G", "E", "nu", "GR", "PE", "U",
|
|
170
|
+
"p"]:
|
|
171
|
+
if key not in dataset:
|
|
172
|
+
dataset[key] = [value]
|
|
173
|
+
else:
|
|
174
|
+
dataset[key].append(value)
|
|
175
|
+
elif key in ["mineral", "state", "trace elements"] and key not in dataset:
|
|
176
|
+
dataset[key] = value
|
|
177
|
+
elif key in ["chemistry", "compounds"]:
|
|
178
|
+
if key not in dataset:
|
|
179
|
+
dataset[key] = {}
|
|
180
|
+
for key_2, value_2 in value.items():
|
|
181
|
+
dataset[key][key_2] = [value_2]
|
|
182
|
+
else:
|
|
183
|
+
for key_2, value_2 in value.items():
|
|
184
|
+
dataset[key][key_2].append(value_2)
|
|
185
|
+
return dataset
|
|
186
|
+
|
|
187
|
+
def _compile_chemistry_formulas(self, mineral_name: str, chemistry_dict: dict):
|
|
188
|
+
"""
|
|
189
|
+
Extracts and compiles all chemistry formulas from the YAML file.
|
|
190
|
+
Stores the compiled ASTs in the global formula cache.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
compiled = {}
|
|
194
|
+
|
|
195
|
+
for element, entry in chemistry_dict.items():
|
|
196
|
+
|
|
197
|
+
# Fall A: Einfache Zahl (z.B. 2, 3.5)
|
|
198
|
+
if isinstance(entry, (int, float)):
|
|
199
|
+
expr = str(entry)
|
|
200
|
+
# Fall B: dict mit 'formula'
|
|
201
|
+
elif isinstance(entry, dict) and "formula" in entry:
|
|
202
|
+
expr = str(entry["formula"])
|
|
203
|
+
# Fall C: Alles andere ist invalid
|
|
204
|
+
else:
|
|
205
|
+
raise ValueError(
|
|
206
|
+
f"Invalid chemistry entry for element '{element}' in {mineral_name}.yaml: {entry}")
|
|
207
|
+
# AST kompilieren
|
|
208
|
+
compiled[element] = self.ae.parse(expr)
|
|
209
|
+
# Cache schreiben
|
|
210
|
+
Sulfides._formula_cache[mineral_name] = compiled
|
|
211
|
+
|
|
212
|
+
def _evaluate_chemistry(self, chemistry_dict, **variables):
|
|
213
|
+
"""
|
|
214
|
+
Evaluates algebraic expressions of element amounts defined in the YAML file.
|
|
215
|
+
|
|
216
|
+
Parameters:
|
|
217
|
+
chemistry_dict (dict): Dictionary from YAML containing element formulas as strings.
|
|
218
|
+
**variables: Variable assignments (e.g. x=0.5, y=1, n=8)
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
dict: {element: calculated_amount}
|
|
222
|
+
"""
|
|
223
|
+
self.ae.symtable.clear()
|
|
224
|
+
|
|
225
|
+
# Variablen setzen
|
|
226
|
+
for k, v in variables.items():
|
|
227
|
+
self.ae.symtable[k] = v
|
|
228
|
+
|
|
229
|
+
results = {}
|
|
230
|
+
compiled = Sulfides._formula_cache[self.name.lower()]
|
|
231
|
+
|
|
232
|
+
for el in chemistry_dict:
|
|
233
|
+
results[el] = self.ae.run(compiled[el])
|
|
234
|
+
|
|
235
|
+
return results
|
|
236
|
+
|
|
237
|
+
def _extract_values_from_yaml(self):
|
|
238
|
+
vals = {}
|
|
239
|
+
# Physical parameters
|
|
240
|
+
for key in ["K", "G", "a_K", "b_K", "a_G", "b_G"]:
|
|
241
|
+
if key in self.yaml_data.get("physical_properties", {}):
|
|
242
|
+
vals[key] = float(self.yaml_data["physical_properties"][key]["value"])
|
|
243
|
+
# Cell parameters
|
|
244
|
+
for key in ["a", "b", "c", "alpha", "beta", "gamma", "Z"]:
|
|
245
|
+
if key in self.yaml_data.get("cell_data", {}):
|
|
246
|
+
vals[key] = float(self.yaml_data["cell_data"][key]["value"])
|
|
247
|
+
# Meta data
|
|
248
|
+
vals["key"] = self.yaml_data["metadata"]["key"]
|
|
249
|
+
vals["crystal_system"] = self.yaml_data["metadata"]["crystal_system"]
|
|
250
|
+
|
|
251
|
+
return vals
|
|
252
|
+
|
|
253
|
+
def _determine_majors_data(self):
|
|
254
|
+
majors_data = []
|
|
255
|
+
molar_mass_pure = 0
|
|
256
|
+
vars = self._get_variables()
|
|
257
|
+
amounts_elements = self._evaluate_chemistry(self.yaml_data["chemistry"], **vars)
|
|
258
|
+
for element, amount in amounts_elements.items():
|
|
259
|
+
n_order = int(self.elements[element][1])
|
|
260
|
+
val_amount = float(amount)
|
|
261
|
+
molar_mass = float(self.elements[element][2])
|
|
262
|
+
majors_data.append([element, n_order, val_amount, molar_mass])
|
|
263
|
+
molar_mass_pure += val_amount*molar_mass
|
|
264
|
+
majors_data.sort(key=lambda x: x[1])
|
|
265
|
+
return majors_data, amounts_elements, molar_mass_pure, vars
|
|
266
|
+
|
|
267
|
+
def _calculate_molar_mass_amounts(self, amounts_elements):
|
|
268
|
+
molar_mass = 0
|
|
269
|
+
for element, amount in amounts_elements.items():
|
|
270
|
+
molar_mass += amount*float(self.elements[element][2])
|
|
271
|
+
|
|
272
|
+
amounts = []
|
|
273
|
+
for element, amount in amounts_elements.items():
|
|
274
|
+
value = amount*float(self.elements[element][2])/molar_mass
|
|
275
|
+
amounts.append([element, self.elements[element][1], value])
|
|
276
|
+
element = [self.elements[name] for name, *_ in amounts]
|
|
277
|
+
return molar_mass, amounts, element
|
|
278
|
+
|
|
279
|
+
def _determine_volume_constructor(self, vals):
|
|
280
|
+
val_a = vals["a"]
|
|
281
|
+
val_system = vals["crystal_system"]
|
|
282
|
+
if val_system in ["isometric", "cubic"]:
|
|
283
|
+
constr_vol = CrystalPhysics([[val_a], [], val_system])
|
|
284
|
+
elif val_system in ["tetragonal", "hexagonal", "trigonal"]:
|
|
285
|
+
val_c = vals["c"]
|
|
286
|
+
constr_vol = CrystalPhysics([[val_a, val_c], [], val_system])
|
|
287
|
+
elif val_system in ["orthorhombic"]:
|
|
288
|
+
val_b = vals["b"]
|
|
289
|
+
val_c = vals["c"]
|
|
290
|
+
constr_vol = CrystalPhysics([[val_a, val_b, val_c], [], val_system])
|
|
291
|
+
elif val_system in ["monoclinic"]:
|
|
292
|
+
val_b = vals["b"]
|
|
293
|
+
val_c = vals["c"]
|
|
294
|
+
val_beta = vals["beta"]
|
|
295
|
+
constr_vol = CrystalPhysics([[val_a, val_b, val_c], [val_beta], val_system])
|
|
296
|
+
elif val_system in ["triclinic"]:
|
|
297
|
+
val_b = vals["b"]
|
|
298
|
+
val_c = vals["c"]
|
|
299
|
+
val_alpha = vals["alpha"]
|
|
300
|
+
val_beta = vals["beta"]
|
|
301
|
+
val_gamma = vals["gamma"]
|
|
302
|
+
constr_vol = CrystalPhysics([[val_a, val_b, val_c], [val_alpha, val_beta, val_gamma], val_system])
|
|
303
|
+
return constr_vol
|
|
304
|
+
|
|
305
|
+
def create_mineral_data_variable_composition(self):
|
|
306
|
+
"""
|
|
307
|
+
Synthetic mineral data generation for an user-selected mineral.
|
|
308
|
+
All mechanical properties (K, G, E) are stored in Pascals internally.
|
|
309
|
+
For output, they are converted to GPa.
|
|
310
|
+
"""
|
|
311
|
+
name_lower = self.name.lower()
|
|
312
|
+
# Chemistry
|
|
313
|
+
val_state = "variable"
|
|
314
|
+
traces_data = []
|
|
315
|
+
# Molar mass, elemental amounts
|
|
316
|
+
majors_data, amounts_elements, molar_mass_pure, vars = self._determine_majors_data()
|
|
317
|
+
|
|
318
|
+
if name_lower not in self.cache:
|
|
319
|
+
vals = self._extract_values_from_yaml()
|
|
320
|
+
self.cache[name_lower] = {"constants": vals}
|
|
321
|
+
else:
|
|
322
|
+
vals = self.cache[name_lower]["constants"]
|
|
323
|
+
constr_vol = self.cache[name_lower]["constr_vol"]
|
|
324
|
+
|
|
325
|
+
# Molar mass, element amounts
|
|
326
|
+
molar_mass, amounts, element = self._calculate_molar_mass_amounts(amounts_elements=amounts_elements)
|
|
327
|
+
|
|
328
|
+
# Reading and assigning the mineral-specific information from the YAML file
|
|
329
|
+
val_key = vals["key"]
|
|
330
|
+
val_Z = vals["Z"]
|
|
331
|
+
if "K" in vals:
|
|
332
|
+
val_K = vals["K"]
|
|
333
|
+
val_G = vals["G"]
|
|
334
|
+
else:
|
|
335
|
+
val_a_K = float(vals["a_K"])
|
|
336
|
+
val_b_K = float(vals["b_K"])
|
|
337
|
+
val_a_G = float(vals["a_G"])
|
|
338
|
+
val_b_G = float(vals["b_G"])
|
|
339
|
+
|
|
340
|
+
# (Molar) Volume
|
|
341
|
+
if "constr_vol" not in self.cache[name_lower]:
|
|
342
|
+
constr_vol = self._determine_volume_constructor(vals=vals)
|
|
343
|
+
self.cache[name_lower]["constr_vol"] = constr_vol
|
|
344
|
+
|
|
345
|
+
constr_minchem = MineralChemistry(w_traces=traces_data, molar_mass_pure=molar_mass_pure, majors=majors_data)
|
|
346
|
+
V, V_m = self.crystallographic_properties.calculate_molar_volume(
|
|
347
|
+
constr_volume=constr_vol, constr_molar_volume=constr_minchem, cell_z=val_Z)
|
|
348
|
+
# Density
|
|
349
|
+
constr_density = CrystalPhysics([molar_mass, val_Z, V])
|
|
350
|
+
rho = self.crystallographic_properties.calculate_mineral_density(constr_density=constr_density)
|
|
351
|
+
constr_electr_density = wg(amounts=amounts, elements=element, rho_b=rho)
|
|
352
|
+
rho_e = self.crystallographic_properties.calculate_electron_density(
|
|
353
|
+
constr_electron_density=constr_electr_density)
|
|
354
|
+
|
|
355
|
+
# Elastic properties
|
|
356
|
+
if "K" not in vals:
|
|
357
|
+
val_K = (val_a_K*rho + val_b_K)*10**9
|
|
358
|
+
val_G = (val_a_G*rho + val_b_G)*10**9
|
|
359
|
+
E, nu = self.geophysical_properties.calculate_elastic_properties(bulk_mod=val_K, shear_mod=val_G)
|
|
360
|
+
# Seismic properties
|
|
361
|
+
vPvS, vP, vS = self.geophysical_properties.calculate_seismic_velocities(
|
|
362
|
+
bulk_mod=val_K, shear_mod=val_G, rho=rho)
|
|
363
|
+
# Radiation properties
|
|
364
|
+
constr_radiation = wg(amounts=amounts, elements=element)
|
|
365
|
+
gamma_ray, pe, U = self.geophysical_properties.calculate_radiation_properties(
|
|
366
|
+
constr_radiation=constr_radiation, rho_electron=rho_e)
|
|
367
|
+
# Electrical resistivity
|
|
368
|
+
p = None
|
|
369
|
+
# Results
|
|
370
|
+
results = {
|
|
371
|
+
"mineral": val_key, "state": val_state, "M": round(molar_mass, self.rounding),
|
|
372
|
+
"chemistry": {name: round(val[1], 6) for name, *val in amounts}, "rho": round(rho, self.rounding),
|
|
373
|
+
"rho_e": round(rho_e, self.rounding), "V": round(V_m, self.rounding), "vP": round(vP, self.rounding),
|
|
374
|
+
"vS": round(vS, self.rounding), "vP/vS": round(vPvS, self.rounding),
|
|
375
|
+
"K": round(val_K*10**(-9), self.rounding), "G": round(val_G*10**(-9), self.rounding),
|
|
376
|
+
"E": round(E*10**(-9), self.rounding), "nu": round(nu, 6), "GR": round(gamma_ray, self.rounding),
|
|
377
|
+
"PE": round(pe, self.rounding), "U": round(U, self.rounding), "p": p}
|
|
378
|
+
return results
|
|
379
|
+
|
|
380
|
+
def create_mineral_data_endmember_series(self):
|
|
381
|
+
"""
|
|
382
|
+
Synthetic mineral data generation for a user-selected mineral.
|
|
383
|
+
All mechanical properties (K, G, E) are stored in Pascals internally.
|
|
384
|
+
For output, they are converted to GPa.
|
|
385
|
+
"""
|
|
386
|
+
val_state = "variable"
|
|
387
|
+
|
|
388
|
+
if self.name == "Calcite-Group":
|
|
389
|
+
name_lower = self.name.lower()
|
|
390
|
+
val_key = "Cal-group"
|
|
391
|
+
endmember = ["Calcite", "Magnesite", "Siderite", "Rhodochrosite", "Smithsonite"]
|
|
392
|
+
elif self.name == "Dolomite-Group":
|
|
393
|
+
name_lower = self.name.lower()
|
|
394
|
+
val_key = "Dol-group"
|
|
395
|
+
endmember = ["Dolomite", "Ankerite"]
|
|
396
|
+
|
|
397
|
+
if "endmembers" not in self.cache:
|
|
398
|
+
self.cache["endmembers"] = {}
|
|
399
|
+
|
|
400
|
+
endmember_data = {}
|
|
401
|
+
list_elements = []
|
|
402
|
+
for mineral in endmember:
|
|
403
|
+
if mineral not in self.cache["endmembers"]:
|
|
404
|
+
mineral_data = Sulfides(name=mineral, random_seed=self.current_seed).generate_dataset(number=1)
|
|
405
|
+
self.cache["endmembers"][mineral] = mineral_data
|
|
406
|
+
endmember_data[mineral] = self.cache["endmembers"][mineral]
|
|
407
|
+
mineral_data = endmember_data[mineral]
|
|
408
|
+
for element in mineral_data["chemistry"]:
|
|
409
|
+
if element not in list_elements:
|
|
410
|
+
list_elements.append(element)
|
|
411
|
+
weights = self.rng.dirichlet(np.ones(len(endmember)))
|
|
412
|
+
fraction_endmember = dict(zip(endmember, weights))
|
|
413
|
+
|
|
414
|
+
if name_lower not in self.cache:
|
|
415
|
+
self.cache[name_lower] = {
|
|
416
|
+
"endmember_data": endmember_data
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
properties = ["M", "rho", "rho_e", "V", "K", "G"]
|
|
420
|
+
helper_results = {
|
|
421
|
+
prop: sum(fraction_endmember[m]*endmember_data[m][prop][0] for m in endmember)
|
|
422
|
+
for prop in properties
|
|
423
|
+
}
|
|
424
|
+
# Amounts
|
|
425
|
+
amounts = []
|
|
426
|
+
for element in list_elements:
|
|
427
|
+
amount = sum(fraction_endmember[mineral]*endmember_data[mineral]["chemistry"].get(element, [0])[0]
|
|
428
|
+
for mineral in endmember)
|
|
429
|
+
amounts.append([element, self.elements[element][1], amount])
|
|
430
|
+
element = [self.elements[name] for name, *_ in amounts]
|
|
431
|
+
# Elastic properties
|
|
432
|
+
val_K = helper_results["K"]*10**9
|
|
433
|
+
val_G = helper_results["G"]*10**9
|
|
434
|
+
rho = helper_results["rho"]
|
|
435
|
+
rho_e = helper_results["rho_e"]
|
|
436
|
+
E, nu = self.geophysical_properties.calculate_elastic_properties(bulk_mod=val_K, shear_mod=val_G)
|
|
437
|
+
# Seismic properties
|
|
438
|
+
vPvS, vP, vS = self.geophysical_properties.calculate_seismic_velocities(
|
|
439
|
+
bulk_mod=val_K, shear_mod=val_G, rho=rho)
|
|
440
|
+
# Radiation properties
|
|
441
|
+
constr_radiation = wg(amounts=amounts, elements=element)
|
|
442
|
+
gamma_ray, pe, U = self.geophysical_properties.calculate_radiation_properties(
|
|
443
|
+
constr_radiation=constr_radiation, rho_electron=rho_e)
|
|
444
|
+
# Electrical resistivity
|
|
445
|
+
p = None
|
|
446
|
+
# Results
|
|
447
|
+
results = {
|
|
448
|
+
"mineral": val_key, "state": val_state, "M": round(helper_results["M"], self.rounding),
|
|
449
|
+
"chemistry": {name: round(val[1], 6) for name, *val in amounts}, "rho": round(rho, self.rounding),
|
|
450
|
+
"rho_e": round(rho_e, self.rounding), "V": round(helper_results["V"], self.rounding),
|
|
451
|
+
"vP": round(vP, self.rounding), "vS": round(vS, self.rounding), "vP/vS": round(vPvS, self.rounding),
|
|
452
|
+
"K": round(val_K*10**(-9), self.rounding), "G": round(val_G*10**(-9), self.rounding),
|
|
453
|
+
"E": round(E*10**(-9), self.rounding), "nu": round(nu, 6), "GR": round(gamma_ray, self.rounding), #
|
|
454
|
+
"PE": round(pe, self.rounding), "U": round(U, self.rounding), "p": p}
|
|
455
|
+
return results
|
|
456
|
+
|
|
457
|
+
# TEST
|
|
458
|
+
if __name__ == "__main__":
|
|
459
|
+
DEFAULT_DATA = Sulfides(name="Pyrite", random_seed=42).generate_dataset(number=10)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*-coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
#-----------------------------------------------
|
|
5
|
+
|
|
6
|
+
# Name: synthesis.py
|
|
7
|
+
# Author: Maximilian A. Beeskow
|
|
8
|
+
# Version: 1.0
|
|
9
|
+
# Date: 15.12.2025
|
|
10
|
+
|
|
11
|
+
#-----------------------------------------------
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
Module: synthesis.py
|
|
15
|
+
Generates the synthetic mineral data based on the settings of the previous configuration.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# PACKAGES
|
|
19
|
+
import pandas as pd
|
|
20
|
+
from typing import Optional, Union
|
|
21
|
+
from functools import cached_property
|
|
22
|
+
|
|
23
|
+
# MODULES
|
|
24
|
+
from ..minerals.config import DEFAULT_CONFIG
|
|
25
|
+
from ..minerals.carbonates import Carbonates
|
|
26
|
+
from ..minerals.oxides import Oxides
|
|
27
|
+
from ..minerals.phyllosilicates import Phyllosilicates
|
|
28
|
+
from ..minerals.sulfides import Sulfides
|
|
29
|
+
from ..minerals.tectosilicates import Tectosilicates
|
|
30
|
+
|
|
31
|
+
# CODE
|
|
32
|
+
class MineralDataGeneration:
|
|
33
|
+
"""
|
|
34
|
+
Generation of the synthetic/simulated mineral data.
|
|
35
|
+
- name: mineral name (e.g., 'Olivine')
|
|
36
|
+
- n_datapoints: Number of generated data points (> 0)
|
|
37
|
+
- random_seed: integer seed; if None, defaults to 42
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
list_carbonates = {
|
|
41
|
+
"Ankerite", "Aragonite", "Azurite", "Calcite", "Cerrusite", "Dolomite", "Ikaite", "Magnesite", "Malachite",
|
|
42
|
+
"Rhodochrosite", "Siderite", "Smithsonite"}
|
|
43
|
+
list_cyclosilicates = {
|
|
44
|
+
"Benitoite", "Beryl", "Cordierite", "Elbaite", "Liddicoatite", "Schorl", "Sekaninaite"}
|
|
45
|
+
list_halides = {"Carnallite", "Fluorite", "Halite", "Sylvite"}
|
|
46
|
+
list_inosilicates = {
|
|
47
|
+
"Actinolite", "Aegirine", "Arfvedsonite", "Augite", "Ca-Amphibole", "Ca-Pyroxene", "Clinopyroxene",
|
|
48
|
+
"Diopside", "Donpeacorite", "Enstatite", "Ferrosilite", "Glaucophane", "Jadeite", "Mg-Fe-Pyroxene",
|
|
49
|
+
"Na-Amphibole", "Na-Pyroxene", "Orthopyroxene", "Spodumene", "Tremolite", "Wollastonite"}
|
|
50
|
+
list_miscellaneous = {"Organic matter"}
|
|
51
|
+
list_nesosilicates = {
|
|
52
|
+
"Al-Garnet", "Almandine", "Andalusite", "Anhadrite", "Ca-Garnet", "Ca-Olivine", "Fayalite", "Forsterite",
|
|
53
|
+
"Grossular", "Kyanite", "Liebenbergite", "Olivine", "Pyrope", "Sillimanite", "Staurolite", "Tephroite",
|
|
54
|
+
"Thorite", "Titanite", "Topaz", "Uvarovite", "Zircon"}
|
|
55
|
+
list_oxides = Oxides._minerals
|
|
56
|
+
list_phosphates = {"Apatite", "Chloroapatite", "Fluoroapatite", "Hydroxyapatite"}
|
|
57
|
+
list_phospides = {"Allabogdanite"}
|
|
58
|
+
list_phyllosilicates = Phyllosilicates._minerals
|
|
59
|
+
list_sorosilicates = {"Epidote", "Gehlenite", "Zoisite"}
|
|
60
|
+
list_sulfates = {
|
|
61
|
+
"Alunite", "Anglesite", "Anhydrite", "Barite", "Celestite", "Chalcanthite", "Gypsum", "Hanksite",
|
|
62
|
+
"Hexahydrite", "Jarosite", "Kainite", "Kieserite", "Scheelite"}
|
|
63
|
+
list_sulfides = {
|
|
64
|
+
"Acanthite", "Bornite", "Cattierite", "Chalcocite", "Chalcopyrite", "Cinnabar", "Cobaltite", "Covellite",
|
|
65
|
+
"Fahlore", "Galena", "Gallite", "Laforetite", "Lenaite", "Marcasite", "Marmatite", "Millerite",
|
|
66
|
+
"Molybdenite", "Orpiment", "Pentlandite", "Pyrite", "Pyrrhotite", "Realgar", "Roquesite", "Sphalerite",
|
|
67
|
+
"Stibnite", "Vaesite"}
|
|
68
|
+
list_tectosilicates = Tectosilicates._minerals
|
|
69
|
+
|
|
70
|
+
mineral_groups = {
|
|
71
|
+
"carbonates": list_carbonates, "cyclosilicates": list_cyclosilicates, "halides": list_halides,
|
|
72
|
+
"inosilicates": list_inosilicates, "miscellaneous": list_miscellaneous, "nesosilicates": list_nesosilicates,
|
|
73
|
+
"oxides": list_oxides, "phosphates": list_phosphates, "phospides": list_phospides,
|
|
74
|
+
"phyllosilicates": list_phyllosilicates, "sorosilicates": list_sorosilicates, "sulfates": list_sulfates,
|
|
75
|
+
"sulfides": list_sulfides, "tectosilicates": list_tectosilicates}
|
|
76
|
+
|
|
77
|
+
def __init__(self,
|
|
78
|
+
name: Optional[str] = None,
|
|
79
|
+
n_datapoints: Optional[int] = None,
|
|
80
|
+
random_seed: Optional[int] = None,
|
|
81
|
+
trace_elements: Optional[list] = None
|
|
82
|
+
) -> None:
|
|
83
|
+
if name is None and n_datapoints is None and random_seed is None:
|
|
84
|
+
self.name = DEFAULT_CONFIG.name
|
|
85
|
+
self.n_datapoints = DEFAULT_CONFIG.n_datapoints
|
|
86
|
+
self.random_seed = DEFAULT_CONFIG.random_seed
|
|
87
|
+
self.trace_elements = []
|
|
88
|
+
else:
|
|
89
|
+
self.name = name
|
|
90
|
+
self.n_datapoints = n_datapoints
|
|
91
|
+
self.random_seed = 42 if random_seed is None else random_seed
|
|
92
|
+
self.trace_elements = [] if trace_elements is None else trace_elements
|
|
93
|
+
|
|
94
|
+
def __repr__(self) -> str:
|
|
95
|
+
return (
|
|
96
|
+
f"<MineralDataGeneration name={self.name!r}, "
|
|
97
|
+
f"n_datapoints={self.n_datapoints}, "
|
|
98
|
+
f"random_seed={self.random_seed}>"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
@cached_property
|
|
102
|
+
def mineral_map(self):
|
|
103
|
+
from gebpy_legacy.modules.halides import Halides
|
|
104
|
+
from gebpy_legacy.modules.silicates import (Cyclosilicates, Inosilicates, Nesosilicates, Sorosilicates)
|
|
105
|
+
from gebpy_legacy.modules.phosphates import Phosphates
|
|
106
|
+
from gebpy_legacy.modules.sulfates import Sulfates
|
|
107
|
+
from gebpy_legacy.modules.organics import Organics
|
|
108
|
+
from gebpy_legacy.modules.phospides import Phospides
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
"carbonates": Carbonates,
|
|
112
|
+
"cyclosilicates": Cyclosilicates,
|
|
113
|
+
"halides": Halides,
|
|
114
|
+
"inosilicates": Inosilicates,
|
|
115
|
+
"miscellaneous": Organics,
|
|
116
|
+
"nesosilicates": Nesosilicates,
|
|
117
|
+
"oxides": Oxides,
|
|
118
|
+
"phosphates": Phosphates,
|
|
119
|
+
"phospides": Phospides,
|
|
120
|
+
"phyllosilicates": Phyllosilicates,
|
|
121
|
+
"sorosilicates": Sorosilicates,
|
|
122
|
+
"sulfates": Sulfates,
|
|
123
|
+
"sulfides": Sulfides,
|
|
124
|
+
"tectosilicates": Tectosilicates
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
def get_all_minerals(self):
|
|
128
|
+
return self.mineral_groups
|
|
129
|
+
|
|
130
|
+
def assign_mineral_group(self,
|
|
131
|
+
name: Optional[str] = None,
|
|
132
|
+
n_datapoints: Optional[int] = None
|
|
133
|
+
) -> None:
|
|
134
|
+
if name is None:
|
|
135
|
+
name = self.name
|
|
136
|
+
n_datapoints = self.n_datapoints
|
|
137
|
+
|
|
138
|
+
for group, list_group in self.mineral_groups.items():
|
|
139
|
+
if name in list_group:
|
|
140
|
+
cls = self.mineral_map[group]
|
|
141
|
+
if cls.__name__ in ("Phyllosilicates", "Tectosilicates", "Oxides", "Carbonates", "Sulfides"):
|
|
142
|
+
#print(sorted(cls(name=name, random_seed=self.random_seed)._minerals))
|
|
143
|
+
return cls(name=name, random_seed=self.random_seed).generate_dataset(number=n_datapoints)
|
|
144
|
+
else:
|
|
145
|
+
try:
|
|
146
|
+
return cls(
|
|
147
|
+
mineral=name, data_type=True, traces_list=self.trace_elements,
|
|
148
|
+
random_seed=self.random_seed).generate_dataset(
|
|
149
|
+
number=n_datapoints)
|
|
150
|
+
except:
|
|
151
|
+
return cls(mineral=name, data_type=True, traces_list=self.trace_elements).generate_dataset(
|
|
152
|
+
number=n_datapoints)
|
|
153
|
+
raise ValueError(f"Unknown mineral '{name}'. Please check available groups.")
|
|
154
|
+
|
|
155
|
+
def generate_data(self, as_dataframe: bool = True) -> Union[pd.DataFrame, dict]:
|
|
156
|
+
data_dict = self.assign_mineral_group(self.name, self.n_datapoints)
|
|
157
|
+
if not as_dataframe:
|
|
158
|
+
return data_dict
|
|
159
|
+
|
|
160
|
+
data_mineral = self.dict_to_dataframe_fast(data_dict)
|
|
161
|
+
data_mineral = data_mineral.astype("float32", errors="ignore").round(5)
|
|
162
|
+
return data_mineral
|
|
163
|
+
|
|
164
|
+
def dict_to_dataframe_fast(self, data: dict) -> pd.DataFrame:
|
|
165
|
+
"""
|
|
166
|
+
Convert nested mineral dataset dictionaries (with lists and subdicts)
|
|
167
|
+
into a flat pandas DataFrame. Handles scalar values gracefully by
|
|
168
|
+
repeating them to match the maximum list length.
|
|
169
|
+
"""
|
|
170
|
+
columns = {}
|
|
171
|
+
|
|
172
|
+
# Schritt 1: maximale Listenlänge bestimmen
|
|
173
|
+
max_len = 1
|
|
174
|
+
for val in data.values():
|
|
175
|
+
if isinstance(val, dict):
|
|
176
|
+
for subval in val.values():
|
|
177
|
+
if isinstance(subval, list):
|
|
178
|
+
max_len = max(max_len, len(subval))
|
|
179
|
+
elif isinstance(val, list):
|
|
180
|
+
max_len = max(max_len, len(val))
|
|
181
|
+
|
|
182
|
+
# Schritt 2: flaches Dictionary aufbauen
|
|
183
|
+
for key, val in data.items():
|
|
184
|
+
if isinstance(val, dict):
|
|
185
|
+
for subkey, subval in val.items():
|
|
186
|
+
col_name = f"{key}.{subkey}"
|
|
187
|
+
if isinstance(subval, list):
|
|
188
|
+
columns[col_name] = subval
|
|
189
|
+
else:
|
|
190
|
+
columns[col_name] = [subval] * max_len
|
|
191
|
+
elif isinstance(val, list):
|
|
192
|
+
columns[key] = val
|
|
193
|
+
else:
|
|
194
|
+
columns[key] = [val] * max_len
|
|
195
|
+
|
|
196
|
+
return pd.DataFrame(columns)
|
|
197
|
+
|
|
198
|
+
# DEFAULT EXAMPLE
|
|
199
|
+
data_config = DEFAULT_CONFIG
|
|
200
|
+
data_default = MineralDataGeneration(name=data_config.name, n_datapoints=data_config.n_datapoints)
|
|
201
|
+
DEFAULT_DATA = data_default.generate_data()
|