pycte 0.1.0__tar.gz
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.
- pycte-0.1.0/PKG-INFO +31 -0
- pycte-0.1.0/README.md +7 -0
- pycte-0.1.0/pycte/__init__.py +143 -0
- pycte-0.1.0/pycte/comsol.py +305 -0
- pycte-0.1.0/pycte/crossDepenciesManager.py +115 -0
- pycte-0.1.0/pycte/engine.py +775 -0
- pycte-0.1.0/pycte/extractDB.py +734 -0
- pycte-0.1.0/pycte/gems.py +267 -0
- pycte-0.1.0/pycte/nativeTransport.py +235 -0
- pycte-0.1.0/pycte/orchestra.py +263 -0
- pycte-0.1.0/pycte/parse_reactions.py +551 -0
- pycte-0.1.0/pycte/pflotran.py +323 -0
- pycte-0.1.0/pycte/phreeqc.py +662 -0
- pycte-0.1.0/pycte.egg-info/PKG-INFO +31 -0
- pycte-0.1.0/pycte.egg-info/SOURCES.txt +18 -0
- pycte-0.1.0/pycte.egg-info/dependency_links.txt +1 -0
- pycte-0.1.0/pycte.egg-info/requires.txt +24 -0
- pycte-0.1.0/pycte.egg-info/top_level.txt +1 -0
- pycte-0.1.0/pyproject.toml +39 -0
- pycte-0.1.0/setup.cfg +4 -0
pycte-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycte
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PYthon generiC inTerfacE
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: pandas
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Provides-Extra: extractdb
|
|
10
|
+
Requires-Dist: charset-normalizer; extra == "extractdb"
|
|
11
|
+
Provides-Extra: phreeqc
|
|
12
|
+
Requires-Dist: phreeqpy; extra == "phreeqc"
|
|
13
|
+
Requires-Dist: pycte[extractdb]; extra == "phreeqc"
|
|
14
|
+
Provides-Extra: pflotran
|
|
15
|
+
Requires-Dist: h5py; extra == "pflotran"
|
|
16
|
+
Provides-Extra: comsol
|
|
17
|
+
Requires-Dist: mph; extra == "comsol"
|
|
18
|
+
Provides-Extra: gems
|
|
19
|
+
Requires-Dist: pycte[extractdb]; extra == "gems"
|
|
20
|
+
Provides-Extra: orchestra
|
|
21
|
+
Requires-Dist: pycte[extractdb]; extra == "orchestra"
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: pycte[comsol,extractdb,pflotran,phreeqc]; extra == "all"
|
|
24
|
+
|
|
25
|
+
PICCTS (Python Interface Coupling generiC Transport and Speciation) is distributed under the Apache 2.0 -style license that can be found in the LICENSE file. By using, distributing, or contributing to this project, you agree to the terms and conditions of this license.
|
|
26
|
+
|
|
27
|
+
Three speciation solvers are currently coupled (PhreeqC, ORCHESTRA and xGEMS) with three transport solvers (COMSOL, PFLOTRAN, and a simple 1D transport code).
|
|
28
|
+
|
|
29
|
+
Please run first the test cases (CationExchange based on ex.11 of PhreeqC from Appelo and al. 2013, calciteDolomite from Azad et al. 2016, AldicarbInfiltration from comsol batch) depending on your speciation and transport solvers.
|
|
30
|
+
|
|
31
|
+
Do not hesitate to contact me ! (anatole.lafond [at] cea.fr)
|
pycte-0.1.0/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
PICCTS (Python Interface Coupling generiC Transport and Speciation) is distributed under the Apache 2.0 -style license that can be found in the LICENSE file. By using, distributing, or contributing to this project, you agree to the terms and conditions of this license.
|
|
2
|
+
|
|
3
|
+
Three speciation solvers are currently coupled (PhreeqC, ORCHESTRA and xGEMS) with three transport solvers (COMSOL, PFLOTRAN, and a simple 1D transport code).
|
|
4
|
+
|
|
5
|
+
Please run first the test cases (CationExchange based on ex.11 of PhreeqC from Appelo and al. 2013, calciteDolomite from Azad et al. 2016, AldicarbInfiltration from comsol batch) depending on your speciation and transport solvers.
|
|
6
|
+
|
|
7
|
+
Do not hesitate to contact me ! (anatole.lafond [at] cea.fr)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pycte — Python Interface Coupling generiC Transport and Speciation.
|
|
3
|
+
|
|
4
|
+
Public API
|
|
5
|
+
----------
|
|
6
|
+
One dedicated function per setting pycte actually reads (generated
|
|
7
|
+
from every `getattr(config, 'name', default)` call in engine.py — see
|
|
8
|
+
_RECOGNIZED_SETTINGS below). Call the ones you need, in any order,
|
|
9
|
+
before pycte.run():
|
|
10
|
+
|
|
11
|
+
import pycte
|
|
12
|
+
|
|
13
|
+
pycte.chemModule("phreeqc")
|
|
14
|
+
pycte.trsptModule("nativeTransport")
|
|
15
|
+
pycte.maxTime(72000)
|
|
16
|
+
pycte.timeStep(720)
|
|
17
|
+
pycte.dispersivity(0.002)
|
|
18
|
+
pycte.velocity(0.002 / 720)
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
pycte.run()
|
|
22
|
+
|
|
23
|
+
Only these functions exist on the `pycte` module — nothing else. A
|
|
24
|
+
typo (pycte.dispersivty(...)) fails immediately with AttributeError,
|
|
25
|
+
instead of silently being ignored. A setting you never call keeps its
|
|
26
|
+
documented default (same default as when it was an absent attribute on
|
|
27
|
+
an old-style input script).
|
|
28
|
+
|
|
29
|
+
run() : run using whatever was declared via the setting
|
|
30
|
+
functions above. If none were called at all,
|
|
31
|
+
falls back to reading the calling script's own
|
|
32
|
+
globals (the older `import pycte; ...;
|
|
33
|
+
pycte.run()` style, kept for scripts that
|
|
34
|
+
define chemModule/maxTime/... directly).
|
|
35
|
+
|
|
36
|
+
run(config) : advanced/escape hatch — pass an explicit config
|
|
37
|
+
object or dict directly, bypassing the setting
|
|
38
|
+
functions entirely.
|
|
39
|
+
|
|
40
|
+
reset() : clear every setting declared so far, without
|
|
41
|
+
running (rarely needed — run() already clears
|
|
42
|
+
after each call).
|
|
43
|
+
|
|
44
|
+
run_from_file(path) : legacy helper. Loads a standalone .py config
|
|
45
|
+
script (old copy/paste style) and runs it.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
import sys
|
|
49
|
+
import types
|
|
50
|
+
|
|
51
|
+
from . import engine
|
|
52
|
+
|
|
53
|
+
# Every keyword pycte.engine.main() actually reads, i.e. every name
|
|
54
|
+
# that appears as getattr(config, 'name', default) in engine.py.
|
|
55
|
+
# Keep this in sync with engine.py if settings are added/removed there.
|
|
56
|
+
_RECOGNIZED_SETTINGS = (
|
|
57
|
+
"maxTime", "timeStep", "stepReprise", "dtpycte", "speciesChargeGeometry",
|
|
58
|
+
"fixpH", "intermediateOutput", "operatorSplitting", "chemModule", "trsptModule",
|
|
59
|
+
"system", "PIDnbr", "PIDextract", "systemSpeciation", "timeUnit", "geometry",
|
|
60
|
+
"firstStepEquilibrium", "nonTrivialDecomposition", "preliminarEquilibrium",
|
|
61
|
+
"PIDchem", "PIDtrspt", "MultiCompoundTransport", "initialConditions",
|
|
62
|
+
"crossDependencies", "description",
|
|
63
|
+
"chemPath", "trsptPath", "phases",
|
|
64
|
+
|
|
65
|
+
"current", "SI", "mineralReversibility",
|
|
66
|
+
"cutoffAq", "cutoffPha", "cutoffEch", "cutoffSurf",
|
|
67
|
+
"masterSpecies", "solutionSpecies", "masterExchange", "exchangeSpecies",
|
|
68
|
+
"masterSurface", "surfaceSpecies", "kineticRates", "kinetics",
|
|
69
|
+
"userVarBool", "userVarList", "solMod", "step_divide", "speciesCharge",
|
|
70
|
+
"speciationCharge", "pHdefault", "tempDefault", "distAnodCathTotale",
|
|
71
|
+
"catholyte", "anolyte", "solModCharge", "acidicTrspt", "water",
|
|
72
|
+
"supplementarySolution", "surfaceCounterIons",
|
|
73
|
+
|
|
74
|
+
"attributes", "firstIC", "primarySpeciesAq", "primarySpeciesPha",
|
|
75
|
+
"primarySpeciesSurf", "speciationEch", "primarySpeciesPhantom", "nrThreads",
|
|
76
|
+
"waterMass", "primarySpeciesEchSorbed", "couplingFormalism",
|
|
77
|
+
"transportedSpecies", "inputVariableOrchestra", "outputVariableOrchestra",
|
|
78
|
+
|
|
79
|
+
"comsolComp", "comsolIntFonction", "comsolStudy", "comsolVTU",
|
|
80
|
+
"comsolCore", "outputComsol",
|
|
81
|
+
|
|
82
|
+
"velocity", "advection", "FickDiffusion", "porousTransport", "ADE",
|
|
83
|
+
"dispersivity", "firstBoundary", "secondBoundary", "boundaryConditions",
|
|
84
|
+
"porosity", "area", "diffCoeff", "meshing",
|
|
85
|
+
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
_settings = {}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _make_setter(_name):
|
|
92
|
+
def _setter(value):
|
|
93
|
+
_settings[_name] = value
|
|
94
|
+
_setter.__name__ = _name
|
|
95
|
+
_setter.__qualname__ = f"pycte.{_name}"
|
|
96
|
+
_setter.__doc__ = f"Set pycte' '{_name}' setting to `value` for the next pycte.run()."
|
|
97
|
+
return _setter
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
for _name in _RECOGNIZED_SETTINGS:
|
|
101
|
+
globals()[_name] = _make_setter(_name)
|
|
102
|
+
del _name, _make_setter
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def reset():
|
|
106
|
+
_settings.clear()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def run(config=None):
|
|
110
|
+
"""
|
|
111
|
+
- pycte.run() : pycte.<name>(value)
|
|
112
|
+
or
|
|
113
|
+
- pycte.run(config)
|
|
114
|
+
"""
|
|
115
|
+
if config is not None:
|
|
116
|
+
if isinstance(config, dict):
|
|
117
|
+
config = types.SimpleNamespace(**config)
|
|
118
|
+
return engine.main(config)
|
|
119
|
+
|
|
120
|
+
if _settings:
|
|
121
|
+
cfg = types.SimpleNamespace(**_settings)
|
|
122
|
+
try:
|
|
123
|
+
return engine.main(cfg)
|
|
124
|
+
finally:
|
|
125
|
+
_settings.clear()
|
|
126
|
+
|
|
127
|
+
return engine.main(sys.modules['__main__'])
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def run_from_file(path):
|
|
131
|
+
"""
|
|
132
|
+
Load a config from a standalone .py file (old style: a script that
|
|
133
|
+
just defines chemModule/trsptModule/maxTime/... as globals) and run
|
|
134
|
+
it. Useful while migrating existing scripts to `import pycte`.
|
|
135
|
+
"""
|
|
136
|
+
import importlib.util
|
|
137
|
+
import os
|
|
138
|
+
|
|
139
|
+
module_name = os.path.splitext(os.path.basename(path))[0]
|
|
140
|
+
spec = importlib.util.spec_from_file_location(module_name, path)
|
|
141
|
+
loaded = importlib.util.module_from_spec(spec)
|
|
142
|
+
spec.loader.exec_module(loaded)
|
|
143
|
+
return run(loaded)
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import sys
|
|
4
|
+
import importlib.util
|
|
5
|
+
import os
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
def writeTime(tps, arr=2):
|
|
9
|
+
if tps >= 3600 * 24:
|
|
10
|
+
return f"{tps / (3600 * 24):.{arr}f} d"
|
|
11
|
+
elif tps >= 3600:
|
|
12
|
+
return f"{tps / 3600:.{arr}f} h"
|
|
13
|
+
elif tps >= 60:
|
|
14
|
+
return f"{tps / 60:.{arr}f} min"
|
|
15
|
+
elif tps < 1:
|
|
16
|
+
return f"{tps*1000 :.{arr}f} msec"
|
|
17
|
+
else:
|
|
18
|
+
return f"{tps:.{arr}f} sec"
|
|
19
|
+
|
|
20
|
+
def Convert_Species_PhreeqC_to_COMSOL(entry_list):
|
|
21
|
+
exit_list = []
|
|
22
|
+
for name in entry_list:
|
|
23
|
+
if name.startswith("("):
|
|
24
|
+
name = name.replace("(","")
|
|
25
|
+
|
|
26
|
+
name = name.replace("(", "_").replace(")", "_").replace(",", "_").replace(":", "_").replace('.','_')
|
|
27
|
+
name = name.replace("-7", "minus7")
|
|
28
|
+
name = name.replace("-6", "minus6")
|
|
29
|
+
name = name.replace("-5", "minus5")
|
|
30
|
+
name = name.replace("-4", "minus4")
|
|
31
|
+
name = name.replace("-3", "minus3")
|
|
32
|
+
name = name.replace("-2", "minus2")
|
|
33
|
+
name = name.replace("-", "minus")
|
|
34
|
+
|
|
35
|
+
name = name.replace("+7", "plus7")
|
|
36
|
+
name = name.replace("+6", "plus6")
|
|
37
|
+
name = name.replace("+5", "plus5")
|
|
38
|
+
name = name.replace("+4", "plus4")
|
|
39
|
+
name = name.replace("+3", "plus3")
|
|
40
|
+
name = name.replace("+2", "plus2")
|
|
41
|
+
name = name.replace("+", "plus")
|
|
42
|
+
exit_list.append(name)
|
|
43
|
+
return exit_list
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def initComsol(comsolDict):
|
|
47
|
+
"""
|
|
48
|
+
Démarre le client COMSOL et charge le modèle UNE SEULE FOIS.
|
|
49
|
+
|
|
50
|
+
Le client / modèle / javamodel sont stockés dans comsolDict
|
|
51
|
+
('comsolClient', 'comsolModel', 'comsolJavaModel') afin que
|
|
52
|
+
transportComsol() les réutilise à chaque pas de temps sans jamais
|
|
53
|
+
relancer COMSOL ni recharger le modèle. Cette fonction contient donc
|
|
54
|
+
toute la logique d'attente de licence, qui ne s'exécute plus qu'une
|
|
55
|
+
seule fois pour l'ensemble de la simulation.
|
|
56
|
+
"""
|
|
57
|
+
import mph
|
|
58
|
+
|
|
59
|
+
scriptWarning = ""
|
|
60
|
+
warning = 0
|
|
61
|
+
waitingLicence = 0
|
|
62
|
+
init = 0
|
|
63
|
+
|
|
64
|
+
inputPath = os.path.join(comsolDict['inputPath'], 'inputTransport.txt')
|
|
65
|
+
|
|
66
|
+
licenceTaken = False
|
|
67
|
+
licence = False
|
|
68
|
+
startWaiting = 0
|
|
69
|
+
while not licence:
|
|
70
|
+
ref = time.perf_counter()
|
|
71
|
+
try:
|
|
72
|
+
client = mph.start()
|
|
73
|
+
if comsolDict['comsolCore']: client = mph.Client(cores=comsolDict['comsolCore'])
|
|
74
|
+
model = client.load(f"{comsolDict['trsptPath']}")
|
|
75
|
+
javamodel = model.java
|
|
76
|
+
javamodel.component(f"{comsolDict['comsolTags'][0]}").func(f"{comsolDict['comsolTags'][1]}").set("filename", f"{inputPath}")
|
|
77
|
+
init += time.perf_counter() - ref
|
|
78
|
+
licence = True
|
|
79
|
+
if licenceTaken:
|
|
80
|
+
waitingLicence = time.perf_counter() - startWaiting
|
|
81
|
+
print("Licence released :)", end=" ", flush=True)
|
|
82
|
+
|
|
83
|
+
except Exception as r:
|
|
84
|
+
if not licenceTaken:
|
|
85
|
+
startWaiting = time.time()
|
|
86
|
+
print(r)
|
|
87
|
+
print("Waiting for a licence ? ...", flush=True)
|
|
88
|
+
licenceTaken = True
|
|
89
|
+
scriptWarning += f"COMSOL waiting licence : {writeTime(waitingLicence)}\n"
|
|
90
|
+
if licenceTaken: time.sleep(3)
|
|
91
|
+
|
|
92
|
+
if licenceTaken:
|
|
93
|
+
warning += 1
|
|
94
|
+
print(f"({writeTime(waitingLicence)} of waiting)", flush=True)
|
|
95
|
+
scriptWarning += f"COMSOL init : {writeTime(waitingLicence)} of waiting \n"
|
|
96
|
+
|
|
97
|
+
comsolDict['comsolClient'] = client
|
|
98
|
+
comsolDict['comsolModel'] = model
|
|
99
|
+
comsolDict['comsolJavaModel'] = javamodel
|
|
100
|
+
|
|
101
|
+
return comsolDict, warning, scriptWarning, waitingLicence, init
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def closeComsol(comsolDict):
|
|
105
|
+
"""
|
|
106
|
+
A appeler UNE SEULE FOIS, à la toute fin de la simulation (après le
|
|
107
|
+
dernier pas de temps), pour libérer le modèle et rendre la licence
|
|
108
|
+
COMSOL. Ne pas appeler entre deux pas de temps : ça obligerait à tout
|
|
109
|
+
recharger au pas suivant.
|
|
110
|
+
"""
|
|
111
|
+
client = comsolDict.get('comsolClient')
|
|
112
|
+
if client is not None:
|
|
113
|
+
try:
|
|
114
|
+
client.clear()
|
|
115
|
+
except Exception as r:
|
|
116
|
+
print(f"Warning while closing COMSOL client: {r}")
|
|
117
|
+
|
|
118
|
+
comsolDict.pop('comsolClient', None)
|
|
119
|
+
comsolDict.pop('comsolModel', None)
|
|
120
|
+
comsolDict.pop('comsolJavaModel', None)
|
|
121
|
+
|
|
122
|
+
return comsolDict
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def transportComsol(comsolDict):
|
|
126
|
+
|
|
127
|
+
scriptWarning = ""
|
|
128
|
+
warning = 0
|
|
129
|
+
abort = False
|
|
130
|
+
calcTime = 0
|
|
131
|
+
init = 0
|
|
132
|
+
waitingLicence = 0
|
|
133
|
+
|
|
134
|
+
inputPath = os.path.join(comsolDict['inputPath'], 'inputTransport.txt')
|
|
135
|
+
|
|
136
|
+
comsolDict['commMtrx'].to_csv(inputPath, index=False, header=False, sep='\t')
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if comsolDict.get('comsolClient') is None:
|
|
140
|
+
comsolDict, initWarning, initScriptWarning, waitingLicence, initTime = initComsol(comsolDict)
|
|
141
|
+
warning += initWarning
|
|
142
|
+
scriptWarning += initScriptWarning
|
|
143
|
+
init += initTime
|
|
144
|
+
|
|
145
|
+
client = comsolDict['comsolClient']
|
|
146
|
+
model = comsolDict['comsolModel']
|
|
147
|
+
javamodel = comsolDict['comsolJavaModel']
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
ref = time.perf_counter()
|
|
151
|
+
javamodel.component(f"{comsolDict['comsolTags'][0]}").func(f"{comsolDict['comsolTags'][1]}").set("filename", f"{inputPath}")
|
|
152
|
+
javamodel.component(f"{comsolDict['comsolTags'][0]}").func(f"{comsolDict['comsolTags'][1]}").refresh()
|
|
153
|
+
init += time.perf_counter() - ref
|
|
154
|
+
|
|
155
|
+
if comsolDict['timeUnit'] == 'y': timeUnit = 'a'
|
|
156
|
+
else: timeUnit = comsolDict['timeUnit']
|
|
157
|
+
|
|
158
|
+
ref = time.perf_counter()
|
|
159
|
+
javamodel.study(f"{comsolDict['comsolTags'][2]}").feature("time").set("tunit", f"{timeUnit}")
|
|
160
|
+
javamodel.study(f"{comsolDict['comsolTags'][2]}").feature("time").set("tlist", f"range(0,{comsolDict['dtStep']},{comsolDict['dtStep']})")
|
|
161
|
+
init += time.perf_counter() - ref
|
|
162
|
+
except Exception as r:
|
|
163
|
+
print(r)
|
|
164
|
+
warning += 1
|
|
165
|
+
scriptWarning += f"COMSOL, time = {comsolDict['tStep']}{comsolDict['timeUnit']}, time-step n° {comsolDict['lStep']+1} : Fatal error occured : \n{r}\n"
|
|
166
|
+
abort = True
|
|
167
|
+
|
|
168
|
+
if not abort:
|
|
169
|
+
try:
|
|
170
|
+
ref = time.perf_counter()
|
|
171
|
+
model.solve()
|
|
172
|
+
calcTime += time.perf_counter() - ref
|
|
173
|
+
except Exception as r:
|
|
174
|
+
print(r)
|
|
175
|
+
with open("warning.log", "a") as warningLog: warningLog.write(f"COMSOL, t={comsolDict['tStep']}{comsolDict['timeUnit']}, time-step n° {comsolDict['lStep']} : {r}\n")
|
|
176
|
+
abort = True
|
|
177
|
+
|
|
178
|
+
if not abort:
|
|
179
|
+
|
|
180
|
+
#### appears to not work all the times ...
|
|
181
|
+
# spc = [s if s in comsolDict['transportedSpecies'] else s+'i' for s in comsolDict['systemSpeciation']]
|
|
182
|
+
# coord_values = model.evaluate(['x', 'y', 'z'][:comsolDict['geometry']],inner='last')
|
|
183
|
+
# coord = pd.DataFrame(
|
|
184
|
+
# {name: values for name, values in zip(['x', 'y', 'z'], coord_values)})
|
|
185
|
+
|
|
186
|
+
# values = model.evaluate(Convert_Species_PhreeqC_to_COMSOL(spc), inner="last")
|
|
187
|
+
# commMtrx_Comsol = pd.DataFrame(
|
|
188
|
+
# np.array(values).T/1000, # if you want to correct with the density, here it is
|
|
189
|
+
# columns=comsolDict['systemSpeciation']
|
|
190
|
+
# )
|
|
191
|
+
# commMtrx_Comsol = pd.concat([coord,commMtrx_Comsol], axis=1)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
outputPath = os.path.join(comsolDict['inputPath'], 'outputTransport.txt')
|
|
195
|
+
ref = time.perf_counter()
|
|
196
|
+
javamodel.result().export("data1").setIndex("looplevelinput", "last", 0);
|
|
197
|
+
javamodel.result().export("data1").set("exporttype", "text");
|
|
198
|
+
javamodel.result().export("data1").set("filename", f"{outputPath}");
|
|
199
|
+
javamodel.result().export("data1").run();
|
|
200
|
+
init += time.perf_counter() - ref
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
if comsolDict['outputComsol']:
|
|
204
|
+
for i,tag in enumerate(comsolDict['outputComsol']): # user defined variables ..
|
|
205
|
+
ref = time.perf_counter()
|
|
206
|
+
path = os.path.join(comsolDict['paths'][f'Transport{tag}'], f'COMSOL_{comsolDict["lStep"]+1}.txt')
|
|
207
|
+
javamodel.result().export(f"{tag}").setIndex("looplevelinput", "last", 0);
|
|
208
|
+
javamodel.result().export(f"{tag}").set("exporttype", "text");
|
|
209
|
+
javamodel.result().export(f"{tag}").set("filename", f"{path}");
|
|
210
|
+
javamodel.result().export(f"{tag}").run();
|
|
211
|
+
init += time.perf_counter() - ref
|
|
212
|
+
if comsolDict['lStep'] == 0:
|
|
213
|
+
path = os.path.join(comsolDict['paths'][f'Transport{tag}'],'COMSOL_0.txt')
|
|
214
|
+
ref = time.perf_counter()
|
|
215
|
+
javamodel.result().export(f"{tag}").setIndex("looplevelinput", "first", 0);
|
|
216
|
+
javamodel.result().export(f"{tag}").set("exporttype", "text");
|
|
217
|
+
javamodel.result().export(f"{tag}").set("filename", f"{path}");
|
|
218
|
+
javamodel.result().export(f"{tag}").run();
|
|
219
|
+
init += time.perf_counter() - ref
|
|
220
|
+
if comsolDict['comsolVTU']:
|
|
221
|
+
path = os.path.join(comsolDict['paths'][f'TransportVTU{tag}'],f'COMSOL_{comsolDict["lStep"]+1}.vtu')
|
|
222
|
+
ref = time.perf_counter()
|
|
223
|
+
javamodel.result().export(f"{tag}").setIndex("looplevelinput", "last", 0);
|
|
224
|
+
javamodel.result().export(f"{tag}").set("exporttype", "vtu");
|
|
225
|
+
javamodel.result().export(f"{tag}").set("filename", f"{path}");
|
|
226
|
+
javamodel.result().export(f"{tag}").run();
|
|
227
|
+
init += time.perf_counter() - ref
|
|
228
|
+
if comsolDict['lStep'] == 0:
|
|
229
|
+
path = os.path.join(comsolDict['paths'][f'TransportVTU{tag}'],'COMSOL_0.vtu')
|
|
230
|
+
ref = time.perf_counter()
|
|
231
|
+
javamodel.result().export(f"{tag}").setIndex("looplevelinput", "first", 0);
|
|
232
|
+
javamodel.result().export(f"{tag}").set("exporttype", "vtu");
|
|
233
|
+
javamodel.result().export(f"{tag}").set("filename", f"{path}");
|
|
234
|
+
javamodel.result().export(f"{tag}").run();
|
|
235
|
+
init += time.perf_counter() - ref
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# col = comsolDict["coord"] + comsolDict['systemSpeciation'] + (comsolDict['crossDependencies']['speciation']['total'] if comsolDict['crossDependencies'] and comsolDict['crossDependencies'].get('speciation') else [])
|
|
240
|
+
commMtrx_Comsol = pd.read_csv(outputPath,sep=r"\s+",comment="%",header=None, names=list(comsolDict['commMtrx'].columns))
|
|
241
|
+
# results = [commMtrx_Comsol, warning, scriptWarning,waitingLicence, abort,calcTime]
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
return commMtrx_Comsol, warning, scriptWarning,waitingLicence, abort,calcTime, init
|
|
247
|
+
|
|
248
|
+
else:
|
|
249
|
+
return pd.DataFrame(), warning, scriptWarning, waitingLicence, abort, 0, init
|
|
250
|
+
|
|
251
|
+
def trspt(centralDict):
|
|
252
|
+
print("COMSOL", end=" ", flush=True)
|
|
253
|
+
startComsol = time.time()
|
|
254
|
+
|
|
255
|
+
comm, warning, scriptWarning,waitingLicence, abort, calcTime, init = transportComsol(centralDict)
|
|
256
|
+
|
|
257
|
+
if warning:
|
|
258
|
+
with open("warning.log", "a") as warningLog:
|
|
259
|
+
warningLog.write(f"COMSOL, time = {centralDict['tStep']}{centralDict['timeUnit']}, time step n°{centralDict['lStep']+1} : the {warning} following warnings occured ...\n {scriptWarning}")
|
|
260
|
+
if abort:
|
|
261
|
+
with open("warning.log", "a") as warningLog:
|
|
262
|
+
warningLog.write(f"COMSOL, time = {centralDict['tStep']}{centralDict['timeUnit']}, time step n°{centralDict['lStep']+1} : Fatal COMSOL error. Aborting run.")
|
|
263
|
+
print('Fatal COMSOL error. Aborting run.')
|
|
264
|
+
sys.exit()
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
assert len(comm) == len(centralDict['commMtrx'])
|
|
268
|
+
except:
|
|
269
|
+
print('Mtrx post transport length different from input: post trspt: ', len(comm), ' input: ', len(centralDict['commMtrx']))
|
|
270
|
+
print('2nd try')
|
|
271
|
+
comm, warning, scriptWarning,waitingLicence, abort, calcTime, init = transportComsol(centralDict)
|
|
272
|
+
|
|
273
|
+
if warning:
|
|
274
|
+
with open("warning.log", "a") as warningLog:
|
|
275
|
+
warningLog.write(f"COMSOL, time = {centralDict['tStep']}{centralDict['timeUnit']}, time step n°{centralDict['lStep']+1} : the {warning} following warnings occured ...\n {scriptWarning}")
|
|
276
|
+
if abort:
|
|
277
|
+
with open("warning.log", "a") as warningLog:
|
|
278
|
+
warningLog.write(f"COMSOL, time = {centralDict['tStep']}{centralDict['timeUnit']}, time step n°{centralDict['lStep']+1} : Fatal COMSOL error. Aborting run.")
|
|
279
|
+
print('Fatal COMSOL error. Aborting run.')
|
|
280
|
+
sys.exit()
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
assert len(comm) == len(centralDict['commMtrx'])
|
|
284
|
+
except:
|
|
285
|
+
|
|
286
|
+
print('Mtrx post transport length different from input: post trspt: ', len(comm), ' input: ', len(centralDict['commMtrx']))
|
|
287
|
+
print('aborting')
|
|
288
|
+
sys.exit()
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
centralDict.update({
|
|
293
|
+
"commMtrx": comm,
|
|
294
|
+
"waitingTime" : centralDict['waitingTime'] + waitingLicence,
|
|
295
|
+
"COMSOLCalcTime_WallClock": centralDict["COMSOLCalcTime_WallClock"] + calcTime,
|
|
296
|
+
"COMSOLCalcTime_ProcessorTime": centralDict["COMSOLCalcTime_ProcessorTime"] + calcTime, # not correct but i dont have access to that info (to my knowledge)
|
|
297
|
+
"COMSOLInterfTime_WallClock": centralDict['COMSOLInterfTime_WallClock'] + time.time() - startComsol - calcTime - init,
|
|
298
|
+
"COMSOLInitTime" : centralDict["COMSOLInitTime"] + init,
|
|
299
|
+
"COMSOLTotalTime" : centralDict["COMSOLTotalTime"] + time.time() - startComsol,
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
print(f"({writeTime((time.time()-startComsol))})")
|
|
304
|
+
|
|
305
|
+
return centralDict
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from itertools import product
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def crossDep(centralDict, crossDependencies):
|
|
5
|
+
'link cross dependencies (CD) with respect to coupled modules'
|
|
6
|
+
'we need to differentiate input CD and output CD'
|
|
7
|
+
|
|
8
|
+
def fillCrossDepDic(coupledParam, inputOrOuput, crossDepDict, CD):
|
|
9
|
+
for cpld in coupledParam:
|
|
10
|
+
if isinstance(cpld, dict):
|
|
11
|
+
for key in cpld.keys():
|
|
12
|
+
for cells in CD[inputOrOuput]:
|
|
13
|
+
if key in cells:
|
|
14
|
+
val = cpld[key]
|
|
15
|
+
if isinstance(val, str):
|
|
16
|
+
crossDepDict[inputOrOuput] += [f"{cells[0]}('{val}')"]
|
|
17
|
+
elif any(isinstance(v, list) for v in val):
|
|
18
|
+
val_normalized = [v if isinstance(v, list) else [v] for v in val]
|
|
19
|
+
crossDepDict[inputOrOuput] += [
|
|
20
|
+
f'{cells[0]}(' + ','.join(f"'{item}'" for item in combo) + ')'
|
|
21
|
+
for combo in product(*val_normalized) ]
|
|
22
|
+
else:
|
|
23
|
+
crossDepDict[inputOrOuput] += [f"{cells[0]}('{v}')" for v in val]
|
|
24
|
+
break
|
|
25
|
+
else:
|
|
26
|
+
for cells in CD[inputOrOuput]:
|
|
27
|
+
if cpld in cells:
|
|
28
|
+
crossDepDict[inputOrOuput] += [cells[0]]
|
|
29
|
+
break
|
|
30
|
+
return crossDepDict
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
transportCD = {}
|
|
34
|
+
speciationCD = {}
|
|
35
|
+
|
|
36
|
+
if centralDict["couplingInfo"][1] == 'PhreeqC':
|
|
37
|
+
'first cell of each CD is the phreeqpy command'
|
|
38
|
+
'second cell is the name of the CD variable'
|
|
39
|
+
'CD shall carry the same name across solvers ...'
|
|
40
|
+
|
|
41
|
+
speciationCD = {
|
|
42
|
+
'input': [
|
|
43
|
+
['tk','temperature(K)','t','temp','tempK','tK','temperatureK','temperature'],
|
|
44
|
+
['tc','temperature(C)','tC','tempC','temperatureC'],
|
|
45
|
+
['DebyeLength', 'DebyeLength(m)', 'debyeLength'],
|
|
46
|
+
['-la("H+")','ph','pH'],
|
|
47
|
+
['-la("e-")','pe','Eh','eh']
|
|
48
|
+
],
|
|
49
|
+
|
|
50
|
+
"output" : [
|
|
51
|
+
['tk','temperature(K)','t','temp','tempK','tK','temperatureK','temperature'],
|
|
52
|
+
['tc','temperature(C)','tC','tempC','temperatureC'],
|
|
53
|
+
['viscos','viscosity(mPa·s)','v','visc'],
|
|
54
|
+
['mu','ionicStrength(mol/kgw)','IS','is'],
|
|
55
|
+
['EDL','EDL_','edl'],
|
|
56
|
+
['TOT','TotAq_','tot_aq'],
|
|
57
|
+
['-la("H+")','ph','pH'],
|
|
58
|
+
['-la("e-")','pe','Eh','eh']
|
|
59
|
+
]},
|
|
60
|
+
|
|
61
|
+
elif centralDict["couplingInfo"][1] == 'xGEMS':
|
|
62
|
+
speciationCD = {
|
|
63
|
+
'input': [
|
|
64
|
+
['temperature(K)','tk','t','temp','tempK','tK','temperatureK','temperature'],
|
|
65
|
+
['temperature(C)','tc','tC','tempC','temperatureC'],
|
|
66
|
+
['pressure','p','P'],
|
|
67
|
+
],
|
|
68
|
+
|
|
69
|
+
"output" : [
|
|
70
|
+
['temperature(K)','tk','t','temp','tempK','tK','temperatureK','temperature'],
|
|
71
|
+
['temperature(C)','tc','tC','tempC','temperatureC'],
|
|
72
|
+
['pressure','p','P'],
|
|
73
|
+
]}
|
|
74
|
+
|
|
75
|
+
if centralDict["couplingInfo"][2] == 'COMSOL':
|
|
76
|
+
transportCD = {'input' : [['dV']],
|
|
77
|
+
'output' : [['dV']]}
|
|
78
|
+
|
|
79
|
+
elif centralDict["couplingInfo"][2] == 'PFLOTRAN':
|
|
80
|
+
transportCD = {
|
|
81
|
+
'input': [
|
|
82
|
+
['temperature(K)','tk','t','temp','tempK','tK','temperatureK','temperature'],
|
|
83
|
+
['temperature(C)','tc','tC','tempC','temperatureC'],
|
|
84
|
+
['pressure','p','P'],
|
|
85
|
+
['porosity'],
|
|
86
|
+
],
|
|
87
|
+
|
|
88
|
+
"output" : [
|
|
89
|
+
['temperature(K)','tk','t','temp','tempK','tK','temperatureK','temperature'],
|
|
90
|
+
['temperature(C)','tc','tC','tempC','temperatureC'],
|
|
91
|
+
['pressure','p','P'],
|
|
92
|
+
['porosity'],
|
|
93
|
+
]}
|
|
94
|
+
|
|
95
|
+
crossDepSpc = {'input' : [], 'output' : []}
|
|
96
|
+
crossDepTrspt = {'input' : [], 'output' : []}
|
|
97
|
+
|
|
98
|
+
crossDepSpc = fillCrossDepDic(crossDependencies.get('speciation', []), 'input', crossDepSpc, speciationCD)
|
|
99
|
+
crossDepSpc = fillCrossDepDic(crossDependencies.get('speciation', []), 'output', crossDepSpc, speciationCD)
|
|
100
|
+
|
|
101
|
+
crossDepTrspt = fillCrossDepDic(crossDependencies.get('transport', []), 'input', crossDepTrspt, transportCD)
|
|
102
|
+
crossDepTrspt = fillCrossDepDic(crossDependencies.get('transport', []), 'output', crossDepTrspt, transportCD)
|
|
103
|
+
|
|
104
|
+
test = {'speciation' : {
|
|
105
|
+
'input' : list(dict.fromkeys(crossDepSpc['input'])),
|
|
106
|
+
'output': list(dict.fromkeys(crossDepSpc['output'])),
|
|
107
|
+
'total' : list(dict.fromkeys(crossDepSpc['input'] + crossDepSpc['output']))},
|
|
108
|
+
'transport': {
|
|
109
|
+
'input': list(dict.fromkeys(crossDepTrspt['input'])),
|
|
110
|
+
'output': list(dict.fromkeys(crossDepTrspt['output'])),
|
|
111
|
+
'total': list(dict.fromkeys(crossDepTrspt['output']+crossDepTrspt['input'])) },
|
|
112
|
+
'totalCrossDep' : list(dict.fromkeys(crossDepTrspt['output']+crossDepTrspt['input']+crossDepSpc['input'] + crossDepSpc['output']))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return test
|