pycte 0.1.0__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.
pycte/__init__.py ADDED
@@ -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)
pycte/comsol.py ADDED
@@ -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