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