fargopy 0.2.1__py3-none-any.whl → 0.3.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.
- fargopy/__init__.py +202 -87
- fargopy/fields.py +221 -0
- fargopy/simulation.py +845 -0
- fargopy/sys.py +41 -18
- fargopy/util.py +4 -0
- fargopy/version.py +1 -1
- {fargopy-0.2.1.dist-info → fargopy-0.3.0.dist-info}/LICENSE +0 -0
- fargopy-0.3.0.dist-info/METADATA +243 -0
- fargopy-0.3.0.dist-info/RECORD +13 -0
- {fargopy-0.2.1.dist-info → fargopy-0.3.0.dist-info}/WHEEL +1 -1
- fargopy/__cycle1.py +0 -880
- fargopy/conf.py +0 -20
- fargopy/fargo3d.py +0 -561
- fargopy-0.2.1.dist-info/METADATA +0 -470
- fargopy-0.2.1.dist-info/RECORD +0 -14
- {fargopy-0.2.1.data → fargopy-0.3.0.data}/scripts/ifargopy +0 -0
- {fargopy-0.2.1.dist-info → fargopy-0.3.0.dist-info}/entry_points.txt +0 -0
- {fargopy-0.2.1.dist-info → fargopy-0.3.0.dist-info}/top_level.txt +0 -0
fargopy/simulation.py
ADDED
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
###############################################################
|
|
2
|
+
# FARGOpy interdependencies
|
|
3
|
+
###############################################################
|
|
4
|
+
import fargopy
|
|
5
|
+
|
|
6
|
+
###############################################################
|
|
7
|
+
# Required packages
|
|
8
|
+
###############################################################
|
|
9
|
+
import os
|
|
10
|
+
import numpy as np
|
|
11
|
+
import re
|
|
12
|
+
import subprocess
|
|
13
|
+
import time
|
|
14
|
+
import gdown
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
###############################################################
|
|
18
|
+
# Constants
|
|
19
|
+
###############################################################
|
|
20
|
+
KB = 1.380650424e-16 # Boltzmann constant: erg/K, erg = g cm^2 / s^2
|
|
21
|
+
MP = 1.672623099e-24 # Mass of the proton, g
|
|
22
|
+
GCONST = 6.67259e-8 # Gravitational constant, cm^3/g/s^2
|
|
23
|
+
RGAS = 8.314472e7 # Gas constant, erg/K/mol
|
|
24
|
+
MSUN = 1.9891e33 # g
|
|
25
|
+
AU = 1.49598e13 # cm
|
|
26
|
+
YEAR = 31557600.0 # s
|
|
27
|
+
|
|
28
|
+
PRECOMPUTED_BASEURL = 'https://docs.google.com/uc?export=download&id='
|
|
29
|
+
PRECOMPUTED_SIMULATIONS = dict(
|
|
30
|
+
# Download link: https://drive.google.com/file/d/1YXLKlf9fCGHgLej2fSOHgStD05uFB2C3/view?usp=drive_link
|
|
31
|
+
fargo=dict(id='1YXLKlf9fCGHgLej2fSOHgStD05uFB2C3',size=55),
|
|
32
|
+
# Download link: https://drive.google.com/file/d/1KMp_82ylQn3ne_aNWEF1T9ElX2aWzYX6/view?usp=drive_link
|
|
33
|
+
p3diso=dict(id='1KMp_82ylQn3ne_aNWEF1T9ElX2aWzYX6',size=220),
|
|
34
|
+
# Download link: https://drive.google.com/file/d/14mL2KCcCtjptChiyISGyJxAEOl4KAanI/view?usp=drive_link
|
|
35
|
+
p3disof=dict(id='14mL2KCcCtjptChiyISGyJxAEOl4KAanI',size=440),
|
|
36
|
+
# Download link: https://drive.google.com/file/d/1KSQyxH_kbAqHQcsE30GQFRVgAPhMAcp7/view?usp=drive_link
|
|
37
|
+
fargo_multifluid=dict(id='1KSQyxH_kbAqHQcsE30GQFRVgAPhMAcp7',size=100),
|
|
38
|
+
# Download link: https://drive.google.com/file/d/12ZWoQS_9ISe6eDij5KWWbqR-bHyyVs2N/view?usp=drive_link
|
|
39
|
+
binary=dict(id='12ZWoQS_9ISe6eDij5KWWbqR-bHyyVs2N',size=140),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
###############################################################
|
|
43
|
+
# Classes
|
|
44
|
+
###############################################################
|
|
45
|
+
class Simulation(fargopy.Fargobj):
|
|
46
|
+
|
|
47
|
+
def __init__(self,**kwargs):
|
|
48
|
+
super().__init__(**kwargs)
|
|
49
|
+
|
|
50
|
+
# Load simulation configuration from a file
|
|
51
|
+
|
|
52
|
+
# Set units by default
|
|
53
|
+
self.set_units(UL=AU,UM=MSUN)
|
|
54
|
+
|
|
55
|
+
# Set properties
|
|
56
|
+
self.set_property('fargo3d_dir',
|
|
57
|
+
fargopy.Conf.FP_FARGO3D_DIR,
|
|
58
|
+
self.set_fargo3d_dir)
|
|
59
|
+
self.set_property('setup',
|
|
60
|
+
None,
|
|
61
|
+
self.set_setup)
|
|
62
|
+
self.set_property('output_dir',
|
|
63
|
+
None,
|
|
64
|
+
self.set_output_dir)
|
|
65
|
+
self.set_property('fargo3d_binary',
|
|
66
|
+
None)
|
|
67
|
+
self.set_property('fargo3d_compilation_options',
|
|
68
|
+
dict(parallel=0,gpu=0,options=''))
|
|
69
|
+
self.set_property('fargo3d_process',
|
|
70
|
+
None)
|
|
71
|
+
self.set_property('logfile',
|
|
72
|
+
None)
|
|
73
|
+
|
|
74
|
+
# ##########################################################################
|
|
75
|
+
# Set special properties
|
|
76
|
+
# ##########################################################################
|
|
77
|
+
def set_fargo3d_dir(self,dir=None):
|
|
78
|
+
"""Set fargo3d directory
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
dir: string, default = None:
|
|
82
|
+
Directory where FARGO3D is installed.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
True if the FARGO3D directory exists and the file
|
|
86
|
+
'src/<fargo_header>' is found. False otherwise.
|
|
87
|
+
"""
|
|
88
|
+
if dir is None:
|
|
89
|
+
return
|
|
90
|
+
if not os.path.isdir(dir):
|
|
91
|
+
print(f"FARGO3D directory '{dir}' does not exist.")
|
|
92
|
+
return
|
|
93
|
+
else:
|
|
94
|
+
fargo_header = f"{dir}/{fargopy.Conf.FP_FARGO3D_HEADER}".replace('//','/')
|
|
95
|
+
if not os.path.isfile(fargo_header):
|
|
96
|
+
print(f"No header file for FARGO3D found in '{fargo_header}'")
|
|
97
|
+
else:
|
|
98
|
+
print(f"Your simulation is now connected with '{dir}'")
|
|
99
|
+
|
|
100
|
+
# Set derivative dirs
|
|
101
|
+
self.fargo3d_dir = dir
|
|
102
|
+
self.outputs_dir = (self.fargo3d_dir + '/outputs').replace('//','/')
|
|
103
|
+
self.setups_dir = (self.fargo3d_dir + '/setups').replace('//','/')
|
|
104
|
+
|
|
105
|
+
def set_setup(self,setup):
|
|
106
|
+
"""Connect the simulation to a given setup.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
setup: string:
|
|
110
|
+
Name of the setup.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
True if the setup_dir <faro3d_dir>/setups/<setup> is found.
|
|
114
|
+
False otherwise.
|
|
115
|
+
"""
|
|
116
|
+
if setup is None:
|
|
117
|
+
self.setup_dir = None
|
|
118
|
+
return None
|
|
119
|
+
setup_dir = f"{self.setups_dir}/{setup}".replace('//','/')
|
|
120
|
+
if self.set_setup_dir(setup_dir):
|
|
121
|
+
self.setup = setup
|
|
122
|
+
return setup
|
|
123
|
+
|
|
124
|
+
def set_setup_dir(self,dir):
|
|
125
|
+
"""Set setup directory
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
dir: string:
|
|
129
|
+
Directory where setup is available.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
True if the FARGO3D directory exists and the file
|
|
133
|
+
<fargo3d_dir>/src/<fargo_header> is found. False otherwise.
|
|
134
|
+
"""
|
|
135
|
+
if dir is None:
|
|
136
|
+
return False
|
|
137
|
+
if not os.path.isdir(dir):
|
|
138
|
+
print(f"Setup directory '{dir}' does not exist.")
|
|
139
|
+
return False
|
|
140
|
+
else:
|
|
141
|
+
print(f"Now your simulation setup is at '{dir}'")
|
|
142
|
+
self.setup_dir = dir
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
def set_output_dir(self,dir):
|
|
146
|
+
if dir is None:
|
|
147
|
+
return
|
|
148
|
+
if not os.path.isdir(dir):
|
|
149
|
+
print(f"Output directory '{dir}' does not exist.")
|
|
150
|
+
return
|
|
151
|
+
else:
|
|
152
|
+
print(f"Now you are connected with output directory '{dir}'")
|
|
153
|
+
self.output_dir = dir
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
def set_units(self,UM=MSUN,UL=AU,G=1,mu=2.35):
|
|
157
|
+
"""Set units of the simulation
|
|
158
|
+
"""
|
|
159
|
+
# Basic
|
|
160
|
+
self.UM = UM
|
|
161
|
+
self.UL = UL
|
|
162
|
+
self.G = G
|
|
163
|
+
self.UT = (G*self.UL**3/(GCONST*self.UM))**0.5 # In seconds
|
|
164
|
+
|
|
165
|
+
# Thermodynamics
|
|
166
|
+
self.UTEMP = (GCONST*MP*mu/KB)*self.UM/self.UL # In K
|
|
167
|
+
|
|
168
|
+
# Derivative
|
|
169
|
+
self.USIGMA = self.UM/self.UL**2 # In g/cm^2
|
|
170
|
+
self.URHO = self.UM/self.UL**3 # In kg/m^3
|
|
171
|
+
self.UEPS = self.UM/(self.UL*self.UT**2) # In J/m^3
|
|
172
|
+
self.UV = self.UL/self.UT
|
|
173
|
+
|
|
174
|
+
# ##########################################################################
|
|
175
|
+
# Control methods
|
|
176
|
+
# ##########################################################################
|
|
177
|
+
def compile(self,setup=None,parallel=0,gpu=0,options='',force=False):
|
|
178
|
+
"""Compile FARGO3D binary
|
|
179
|
+
"""
|
|
180
|
+
if setup is not None:
|
|
181
|
+
if not self.set_setup(setup):
|
|
182
|
+
print("Failed")
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
# Clean directrory
|
|
186
|
+
if force:
|
|
187
|
+
print(f"Cleaning FARGO3D directory {self.fargo3d_dir}...")
|
|
188
|
+
cmd = f"make -C {self.fargo3d_dir} clean mrproper"
|
|
189
|
+
compl = f"rm -rf {self.fargo3d_dir}/fargo3d_*"
|
|
190
|
+
error,self.output_clean = fargopy.Sys.run(cmd + '&&' + compl)
|
|
191
|
+
|
|
192
|
+
# Prepare compilation
|
|
193
|
+
compile_options = f"SETUP={self.setup} PARALLEL={parallel} GPU={gpu} "+options
|
|
194
|
+
fargo3d_binary = f"fargo3d_{compile_options.replace(' ','_').replace('=','-').strip('_')}"
|
|
195
|
+
|
|
196
|
+
# Compile binary
|
|
197
|
+
print(f"Compiling {fargo3d_binary}...")
|
|
198
|
+
cmd = f"cd {self.fargo3d_dir};make {compile_options}"
|
|
199
|
+
compl = f"mv fargo3d {fargo3d_binary}"
|
|
200
|
+
error,self.output_compilation = fargopy.Sys.run(cmd+' && '+compl)
|
|
201
|
+
|
|
202
|
+
# Check compilation result
|
|
203
|
+
if os.path.isfile(f"{self.fargo3d_dir}/{fargo3d_binary}"):
|
|
204
|
+
self.fargo3d_binary = fargo3d_binary
|
|
205
|
+
print(f"Succesful compilation of FARGO3D binary {self.fargo3d_binary}")
|
|
206
|
+
self.fargo3d_compilation_options=dict(
|
|
207
|
+
parallel=parallel,
|
|
208
|
+
gpu=gpu,
|
|
209
|
+
options=options
|
|
210
|
+
)
|
|
211
|
+
return True
|
|
212
|
+
else:
|
|
213
|
+
print(f"Something failed when compiling FARGO3D. For details check Simulation.output_compilation")
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
def run(self,
|
|
217
|
+
mode='async',
|
|
218
|
+
options='-m',
|
|
219
|
+
mpioptions='-np 1',
|
|
220
|
+
resume=False,
|
|
221
|
+
cleanrun=False,
|
|
222
|
+
test=False):
|
|
223
|
+
|
|
224
|
+
if self.fargo3d_binary is None:
|
|
225
|
+
print("You must first compile your simulation with: <simulation>.compile(<option>).")
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
if self._is_running():
|
|
229
|
+
print(f"There is a running process. Please stop it before running/resuming")
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
# Mandatory options
|
|
233
|
+
options = options + " -t"
|
|
234
|
+
if 'run_options' not in self.__dict__.keys():
|
|
235
|
+
self.fargo3d_run_options = options
|
|
236
|
+
|
|
237
|
+
self.logfile = f"{self.setup_dir}/{self.setup}.log"
|
|
238
|
+
|
|
239
|
+
# Clean output if available
|
|
240
|
+
if cleanrun:
|
|
241
|
+
# Check if there is an output director
|
|
242
|
+
output_dir = f"{self.outputs_dir}/{self.setup}"
|
|
243
|
+
if os.path.isdir(output_dir):
|
|
244
|
+
self.output_dir = output_dir
|
|
245
|
+
self.clean_output()
|
|
246
|
+
else:
|
|
247
|
+
print(f"No output directory {output_dir} yet created.")
|
|
248
|
+
|
|
249
|
+
# Select command to run
|
|
250
|
+
precmd=''
|
|
251
|
+
if self.fargo3d_compilation_options['parallel']:
|
|
252
|
+
precmd = f"mpirun {mpioptions} "
|
|
253
|
+
|
|
254
|
+
# Preparing command
|
|
255
|
+
run_cmd = f"{precmd} ./{self.fargo3d_binary} {options} setups/{self.setup}/{self.setup}.par"
|
|
256
|
+
|
|
257
|
+
if mode == 'sync':
|
|
258
|
+
# Run synchronously
|
|
259
|
+
cmd = f"cd {self.fargo3d_dir};{run_cmd} |tee {self.logfile}"
|
|
260
|
+
print(f"Running synchronously: {cmd}")
|
|
261
|
+
fargopy.Sys.simple(cmd)
|
|
262
|
+
self.fargo3d_process = None
|
|
263
|
+
|
|
264
|
+
elif mode == 'async':
|
|
265
|
+
# Run asynchronously
|
|
266
|
+
|
|
267
|
+
# Select logfile mode accroding to if the process is resuming
|
|
268
|
+
logmode = 'a' if resume else 'w'
|
|
269
|
+
logfile_handler=open(self.logfile,logmode)
|
|
270
|
+
|
|
271
|
+
# Launch process
|
|
272
|
+
print(f"Running asynchronously (test = {test}): {run_cmd}")
|
|
273
|
+
if not test:
|
|
274
|
+
process = subprocess.Popen(run_cmd.split(),cwd=self.fargo3d_dir,
|
|
275
|
+
stdout=logfile_handler,stderr=logfile_handler)
|
|
276
|
+
# Introduce a short delay to verify if the process has failed
|
|
277
|
+
time.sleep(1.0)
|
|
278
|
+
|
|
279
|
+
if process.poll() is None:
|
|
280
|
+
# Check if program is effectively running
|
|
281
|
+
self.fargo3d_process = process
|
|
282
|
+
|
|
283
|
+
# Create a lock on fargopy with the process id
|
|
284
|
+
# fargopy.lock(self.frago3d_process.pid)
|
|
285
|
+
|
|
286
|
+
# Setup output directory
|
|
287
|
+
self.set_output_dir(f"{self.outputs_dir}/{self.setup}".replace('//','/'))
|
|
288
|
+
else:
|
|
289
|
+
print(f"Process running failed. Please check the logfile {self.logfile}")
|
|
290
|
+
|
|
291
|
+
def stop(self):
|
|
292
|
+
if not self._check_process():
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
poll = self.fargo3d_process.poll()
|
|
296
|
+
if poll is None:
|
|
297
|
+
print(f"Stopping FARGO3D process (pid = {self.fargo3d_process.pid})")
|
|
298
|
+
subprocess.Popen.kill(self.fargo3d_process)
|
|
299
|
+
del self.fargo3d_process
|
|
300
|
+
self.fargo3d_process = None
|
|
301
|
+
else:
|
|
302
|
+
print(f"The process has already finished. Check logfile {self.logfile}.")
|
|
303
|
+
|
|
304
|
+
def _save_simultation(self):
|
|
305
|
+
"""Save simulation configuration
|
|
306
|
+
"""
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
def status(self,mode='isrunning',verbose=True):
|
|
310
|
+
"""Check the status of the running process
|
|
311
|
+
|
|
312
|
+
Parameters:
|
|
313
|
+
mode: string, defaul='isrunning':
|
|
314
|
+
Available modes:
|
|
315
|
+
'isrunning': Just show if the process is running.
|
|
316
|
+
'logfile': Show the latest lines of the logfile
|
|
317
|
+
'outputs': Show (and return) a list of outputs
|
|
318
|
+
'snapshots': Show (and return) a list of snapshots
|
|
319
|
+
'progress': Show progress in realtime
|
|
320
|
+
|
|
321
|
+
"""
|
|
322
|
+
# Bar separating output
|
|
323
|
+
bar = f"\n{''.join(['#']*80)}\n"
|
|
324
|
+
|
|
325
|
+
# vprint
|
|
326
|
+
vprint = print if verbose else lambda x:x
|
|
327
|
+
|
|
328
|
+
if 'isrunning' in mode or mode=='all':
|
|
329
|
+
vprint(bar+"Running status of the process:")
|
|
330
|
+
if self.fargo3d_process:
|
|
331
|
+
poll = self.fargo3d_process.poll()
|
|
332
|
+
if poll is None:
|
|
333
|
+
vprint("\tThe process is running.")
|
|
334
|
+
else:
|
|
335
|
+
vprint(f"\tThe process has ended with termination code {poll}.")
|
|
336
|
+
else:
|
|
337
|
+
vprint(f"\tThe process is stopped.")
|
|
338
|
+
|
|
339
|
+
if 'logfile' in mode or mode=='all':
|
|
340
|
+
vprint(bar+"Logfile content:")
|
|
341
|
+
if 'logfile' in self.__dict__.keys() and os.path.isfile(self.logfile):
|
|
342
|
+
vprint("The latest 10 lines of the logfile:\n")
|
|
343
|
+
if verbose:
|
|
344
|
+
os.system(f"tail -n 10 {self.logfile}")
|
|
345
|
+
else:
|
|
346
|
+
vprint("No log file created yet")
|
|
347
|
+
|
|
348
|
+
if 'outputs' in mode or mode=='all':
|
|
349
|
+
vprint(bar+"Output content:")
|
|
350
|
+
error,output = fargopy.Sys.run(f"ls {self.output_dir}/*.dat")
|
|
351
|
+
if not error:
|
|
352
|
+
files = [file.split('/')[-1] for file in output[:-1]]
|
|
353
|
+
file_list = ""
|
|
354
|
+
for i,file in enumerate(files):
|
|
355
|
+
file_list += f"{file}, "
|
|
356
|
+
if ((i+1)%10) == 0:
|
|
357
|
+
file_list += "\n"
|
|
358
|
+
file_list = file_list.strip("\n,")
|
|
359
|
+
vprint(f"\n{len(files)} available datafiles:\n")
|
|
360
|
+
self.output_datafiles = files
|
|
361
|
+
vprint(file_list)
|
|
362
|
+
else:
|
|
363
|
+
vprint("No datafiles yet available")
|
|
364
|
+
|
|
365
|
+
if 'progress' in mode:
|
|
366
|
+
self._status_progress()
|
|
367
|
+
|
|
368
|
+
def _status_progress(self,minfreq=0.1,numstatus=5):
|
|
369
|
+
"""Show a progress of the execution
|
|
370
|
+
|
|
371
|
+
Parameters:
|
|
372
|
+
minfreq: float, default = 0.1:
|
|
373
|
+
Minimum amount of seconds between status check.
|
|
374
|
+
|
|
375
|
+
numstatus: int, default = 5:
|
|
376
|
+
Number of status shown before automatically stopping.
|
|
377
|
+
"""
|
|
378
|
+
# Prepare
|
|
379
|
+
frequency = minfreq
|
|
380
|
+
previous_output = ''
|
|
381
|
+
previous_resumable_snapshot = 1e100
|
|
382
|
+
time_previous = time.time()
|
|
383
|
+
|
|
384
|
+
# Infinite loop checking for output
|
|
385
|
+
i = 0
|
|
386
|
+
while True and (i<numstatus):
|
|
387
|
+
if not self._is_running():
|
|
388
|
+
print("The simulation is not running anymore")
|
|
389
|
+
return
|
|
390
|
+
error,output = fargopy.Sys.run(f"grep OUTPUT {self.logfile} |tail -n 1")
|
|
391
|
+
|
|
392
|
+
if not error:
|
|
393
|
+
# Get the latest output
|
|
394
|
+
latest_output = output[-2]
|
|
395
|
+
if latest_output != previous_output:
|
|
396
|
+
print(f"{latest_output} [output pace = {frequency:.1f} secs]")
|
|
397
|
+
# Fun the number of the output
|
|
398
|
+
find = re.findall(r'OUTPUTS\s+(\d+)',latest_output)
|
|
399
|
+
resumable_snapshot = int(find[0])
|
|
400
|
+
# Get the time elapsed since last status check
|
|
401
|
+
time_now = time.time()
|
|
402
|
+
frequency = max(time_now - time_previous,minfreq)/2
|
|
403
|
+
if (resumable_snapshot - previous_resumable_snapshot)>1:
|
|
404
|
+
# Reduce frequency if snapshots are accelerating
|
|
405
|
+
frequency = frequency/2
|
|
406
|
+
previous_resumable_snapshot = resumable_snapshot
|
|
407
|
+
time_previous = time_now
|
|
408
|
+
previous_output = latest_output
|
|
409
|
+
try:
|
|
410
|
+
time.sleep(frequency)
|
|
411
|
+
i += 1
|
|
412
|
+
except KeyboardInterrupt:
|
|
413
|
+
return
|
|
414
|
+
|
|
415
|
+
def resume(self,snapshot=-1,mpioptions='-np 1'):
|
|
416
|
+
latest_snapshot_resumable = self._is_resumable()
|
|
417
|
+
if latest_snapshot_resumable<0:
|
|
418
|
+
return
|
|
419
|
+
if self._is_running():
|
|
420
|
+
print(f"There is a running process. Please stop it before resuming")
|
|
421
|
+
return
|
|
422
|
+
if self._has_finished():
|
|
423
|
+
return
|
|
424
|
+
# Resume
|
|
425
|
+
if snapshot<0:
|
|
426
|
+
snapshot = latest_snapshot_resumable
|
|
427
|
+
print(f"Resuming from snapshot {snapshot}...")
|
|
428
|
+
self.run(mode='async',mpioptions=mpioptions,resume=True,
|
|
429
|
+
options=self.fargo3d_run_options+f' -S {snapshot}',test=False)
|
|
430
|
+
|
|
431
|
+
def _has_finished(self):
|
|
432
|
+
if self.fargo3d_process:
|
|
433
|
+
poll = self.fargo3d_process.poll()
|
|
434
|
+
if poll is None:
|
|
435
|
+
return False
|
|
436
|
+
else:
|
|
437
|
+
print(f"The process has ended with termination code {poll}.")
|
|
438
|
+
return True
|
|
439
|
+
|
|
440
|
+
def _is_resumable(self):
|
|
441
|
+
if self.logfile is None:
|
|
442
|
+
print(f"The simulation has not been ran yet. Run <simulation>.run() before resuming")
|
|
443
|
+
return -1
|
|
444
|
+
latest_snapshot_resumable = max(self._get_nsnaps() - 2, 0)
|
|
445
|
+
return latest_snapshot_resumable
|
|
446
|
+
|
|
447
|
+
def clean_output(self):
|
|
448
|
+
if self.output_dir is None:
|
|
449
|
+
print(f"Output directory has not been set.")
|
|
450
|
+
return
|
|
451
|
+
|
|
452
|
+
if self._is_running():
|
|
453
|
+
print(f"There is a running process. Please stop it before cleaning")
|
|
454
|
+
return
|
|
455
|
+
|
|
456
|
+
print(f"Cleaning output directory {self.output_dir}")
|
|
457
|
+
cmd = f"rm -rf {self.output_dir}/*"
|
|
458
|
+
error,output = fargopy.Sys.run(cmd)
|
|
459
|
+
|
|
460
|
+
def _is_running(self,verbose=False):
|
|
461
|
+
if self.fargo3d_process:
|
|
462
|
+
poll = self.fargo3d_process.poll()
|
|
463
|
+
if poll is None:
|
|
464
|
+
if verbose:
|
|
465
|
+
print(f"The process is already running with pid '{self.fargo3d_process.pid}'")
|
|
466
|
+
return True
|
|
467
|
+
else:
|
|
468
|
+
return False
|
|
469
|
+
else:
|
|
470
|
+
return False
|
|
471
|
+
|
|
472
|
+
def _check_process(self):
|
|
473
|
+
if self.fargo3d_process is None:
|
|
474
|
+
print(f"There is no FARGO3D process handler available.")
|
|
475
|
+
return False
|
|
476
|
+
return True
|
|
477
|
+
|
|
478
|
+
# ##########################################################################
|
|
479
|
+
# Operations on the FARGO3D directories
|
|
480
|
+
# ##########################################################################
|
|
481
|
+
def list_outputs(self,quiet=False):
|
|
482
|
+
if self.output_dir is None:
|
|
483
|
+
print(f"You have to set forst the outputs directory with <sim>.set_outputs('<directory>')")
|
|
484
|
+
else:
|
|
485
|
+
error,output = fargopy.Sys.run(f"ls {self.output_dir}")
|
|
486
|
+
if error == 0:
|
|
487
|
+
files = output[:-1]
|
|
488
|
+
print(f"{len(files)} files in output directory")
|
|
489
|
+
if not quiet:
|
|
490
|
+
file_list = ""
|
|
491
|
+
for i,file in enumerate(files):
|
|
492
|
+
file_list += f"{file}, "
|
|
493
|
+
if ((i+1)%10) == 0:
|
|
494
|
+
file_list += "\n"
|
|
495
|
+
print(file_list)
|
|
496
|
+
return files
|
|
497
|
+
|
|
498
|
+
def load_properties(self,quiet=False,
|
|
499
|
+
varfile='variables.par',
|
|
500
|
+
domain_prefix='domain_',
|
|
501
|
+
dimsfile='dims.dat'
|
|
502
|
+
):
|
|
503
|
+
if self.output_dir is None:
|
|
504
|
+
print(f"You have to set first the outputs directory with <sim>.set_outputs('<directory>')")
|
|
505
|
+
|
|
506
|
+
# Read variables
|
|
507
|
+
vars = self._load_variables(varfile)
|
|
508
|
+
print(f"Simulation in {vars.DIM} dimensions")
|
|
509
|
+
|
|
510
|
+
# Read domains
|
|
511
|
+
domains = self._load_domains(vars,domain_prefix)
|
|
512
|
+
|
|
513
|
+
# Store the variables in the object
|
|
514
|
+
self.vars = vars
|
|
515
|
+
self.domains = domains
|
|
516
|
+
|
|
517
|
+
# Optionally read dims
|
|
518
|
+
dims = self._load_dims(dimsfile)
|
|
519
|
+
if len(dims):
|
|
520
|
+
self.dims = dims
|
|
521
|
+
|
|
522
|
+
# Read the summary files
|
|
523
|
+
self.nsnaps = self._get_nsnaps()
|
|
524
|
+
print(f"Number of snapshots in output directory: {self.nsnaps}")
|
|
525
|
+
|
|
526
|
+
print("Configuration variables and domains load into the object. See e.g. <sim>.vars")
|
|
527
|
+
|
|
528
|
+
def _get_nsnaps(self):
|
|
529
|
+
"""Get the number of snapshots in an output directory
|
|
530
|
+
"""
|
|
531
|
+
error,output = fargopy.Sys.run(f"ls {self.output_dir}/summary[0-9]*.dat")
|
|
532
|
+
if error == 0:
|
|
533
|
+
files = output[:-1]
|
|
534
|
+
nsnaps = len(files)
|
|
535
|
+
return nsnaps
|
|
536
|
+
else:
|
|
537
|
+
print(f"No summary file in {self.output_dir}")
|
|
538
|
+
return 0
|
|
539
|
+
|
|
540
|
+
def _load_dims(self,dimsfile):
|
|
541
|
+
"""Parse the dim directory
|
|
542
|
+
"""
|
|
543
|
+
dimsfile = f"{self.output_dir}/{dimsfile}".replace('//','/')
|
|
544
|
+
if not os.path.isfile(dimsfile):
|
|
545
|
+
#print(f"No file with dimensions '{dimsfile}' found.")
|
|
546
|
+
return []
|
|
547
|
+
dims = np.loadtxt(dimsfile)
|
|
548
|
+
return dims
|
|
549
|
+
|
|
550
|
+
def _load_variables(self,varfile):
|
|
551
|
+
"""Parse the file with the variables
|
|
552
|
+
"""
|
|
553
|
+
|
|
554
|
+
varfile = f"{self.output_dir}/{varfile}".replace('//','/')
|
|
555
|
+
if not os.path.isfile(varfile):
|
|
556
|
+
print(f"No file with variables named '{varfile}' found.")
|
|
557
|
+
return
|
|
558
|
+
|
|
559
|
+
print(f"Loading variables")
|
|
560
|
+
variables = np.genfromtxt(
|
|
561
|
+
varfile,dtype={'names': ("parameters","values"),
|
|
562
|
+
'formats': ("|S30","|S300")}
|
|
563
|
+
).tolist()
|
|
564
|
+
|
|
565
|
+
vars = dict()
|
|
566
|
+
for posicion in variables:
|
|
567
|
+
str_value = posicion[1].decode("utf-8")
|
|
568
|
+
try:
|
|
569
|
+
value = int(str_value)
|
|
570
|
+
except:
|
|
571
|
+
try:
|
|
572
|
+
value = float(str_value)
|
|
573
|
+
except:
|
|
574
|
+
value = str_value
|
|
575
|
+
vars[posicion[0].decode("utf-8")] = value
|
|
576
|
+
|
|
577
|
+
vars = fargopy.Dictobj(dict=vars)
|
|
578
|
+
print(f"{len(vars.__dict__.keys())} variables loaded")
|
|
579
|
+
|
|
580
|
+
# Create additional variables
|
|
581
|
+
variables = ['x', 'y', 'z']
|
|
582
|
+
if vars.COORDINATES == 'cylindrical':
|
|
583
|
+
variables = ['phi', 'r', 'z']
|
|
584
|
+
elif vars.COORDINATES == 'spherical':
|
|
585
|
+
variables = ['phi', 'r', 'theta']
|
|
586
|
+
vars.VARIABLES = variables
|
|
587
|
+
|
|
588
|
+
vars.__dict__[f'N{variables[0].upper()}'] = vars.NX
|
|
589
|
+
vars.__dict__[f'N{variables[1].upper()}'] = vars.NY
|
|
590
|
+
vars.__dict__[f'N{variables[2].upper()}'] = vars.NZ
|
|
591
|
+
|
|
592
|
+
# Dimension of the domain
|
|
593
|
+
vars.DIM = 2 if vars.NZ == 1 else 3
|
|
594
|
+
|
|
595
|
+
return vars
|
|
596
|
+
|
|
597
|
+
def _load_domains(self,vars,domain_prefix,
|
|
598
|
+
borders=[[],[3,-3],[3,-3]],
|
|
599
|
+
middle=True):
|
|
600
|
+
|
|
601
|
+
# Coordinates
|
|
602
|
+
variable_suffixes = ['x', 'y', 'z']
|
|
603
|
+
print(f"Loading domain in {vars.COORDINATES} coordinates:")
|
|
604
|
+
|
|
605
|
+
# Correct dims in case of 2D
|
|
606
|
+
if vars.DIM == 2:
|
|
607
|
+
borders[-1] = []
|
|
608
|
+
|
|
609
|
+
# Load domains
|
|
610
|
+
domains = dict()
|
|
611
|
+
domains['extrema'] = dict()
|
|
612
|
+
|
|
613
|
+
for i,variable_suffix in enumerate(variable_suffixes):
|
|
614
|
+
domain_file = f"{self.output_dir}/{domain_prefix}{variable_suffix}.dat".replace('//','/')
|
|
615
|
+
if os.path.isfile(domain_file):
|
|
616
|
+
|
|
617
|
+
# Load data from file
|
|
618
|
+
domains[vars.VARIABLES[i]] = np.genfromtxt(domain_file)
|
|
619
|
+
|
|
620
|
+
if len(borders[i]) > 0:
|
|
621
|
+
# Drop the border of the domain
|
|
622
|
+
domains[vars.VARIABLES[i]] = domains[vars.VARIABLES[i]][borders[i][0]:borders[i][1]]
|
|
623
|
+
|
|
624
|
+
if middle:
|
|
625
|
+
# Average between domain cell coordinates
|
|
626
|
+
domains[vars.VARIABLES[i]] = 0.5*(domains[vars.VARIABLES[i]][:-1]+domains[vars.VARIABLES[i]][1:])
|
|
627
|
+
|
|
628
|
+
# Show indices and value map
|
|
629
|
+
domains['extrema'][vars.VARIABLES[i]] = [[0,domains[vars.VARIABLES[i]][0]],[-1,domains[vars.VARIABLES[i]][-1]]]
|
|
630
|
+
|
|
631
|
+
print(f"\tVariable {vars.VARIABLES[i]}: {len(domains[vars.VARIABLES[i]])} {domains['extrema'][vars.VARIABLES[i]]}")
|
|
632
|
+
else:
|
|
633
|
+
print(f"\tDomain file {domain_file} not found.")
|
|
634
|
+
domains = fargopy.Dictobj(dict=domains)
|
|
635
|
+
|
|
636
|
+
return domains
|
|
637
|
+
|
|
638
|
+
def load_field(self,field,snapshot=None,type='scalar'):
|
|
639
|
+
|
|
640
|
+
if not self.has('vars'):
|
|
641
|
+
# If the simulation has not loaded the variables
|
|
642
|
+
dims, vars, domains = self.load_properties()
|
|
643
|
+
|
|
644
|
+
# In case no snapshot has been provided use 0
|
|
645
|
+
snapshot = 0 if snapshot is None else snapshot
|
|
646
|
+
|
|
647
|
+
field_data = []
|
|
648
|
+
if type == 'scalar':
|
|
649
|
+
file_name = f"{field}{str(snapshot)}.dat"
|
|
650
|
+
file_field = f"{self.output_dir}/{file_name}".replace('//','/')
|
|
651
|
+
field_data = self._load_field_scalar(file_field)
|
|
652
|
+
elif type == 'vector':
|
|
653
|
+
field_data = []
|
|
654
|
+
variables = ['x','y']
|
|
655
|
+
if self.vars.DIM == 3:
|
|
656
|
+
variables += ['z']
|
|
657
|
+
for i,variable in enumerate(variables):
|
|
658
|
+
file_name = f"{field}{variable}{str(snapshot)}.dat"
|
|
659
|
+
file_field = f"{self.output_dir}/{file_name}".replace('//','/')
|
|
660
|
+
field_data += [self._load_field_scalar(file_field)]
|
|
661
|
+
else:
|
|
662
|
+
raise ValueError(f"fargopy.Field type '{type}' not recognized.")
|
|
663
|
+
|
|
664
|
+
field = fargopy.Field(data=np.array(field_data), coordinates=self.vars.COORDINATES, domains=self.domains, type=type)
|
|
665
|
+
return field
|
|
666
|
+
|
|
667
|
+
def _load_field_scalar(self,file):
|
|
668
|
+
"""Load scalar field from file a file.
|
|
669
|
+
"""
|
|
670
|
+
if os.path.isfile(file):
|
|
671
|
+
field_data = np.fromfile(file).reshape(int(self.vars.NZ),int(self.vars.NY),int(self.vars.NX))
|
|
672
|
+
"""
|
|
673
|
+
if self.vars.NZ > 1:
|
|
674
|
+
# 3D field
|
|
675
|
+
field_data = np.fromfile(file).reshape(int(self.vars.NZ),int(self.vars.NY),int(self.vars.NX))
|
|
676
|
+
else:
|
|
677
|
+
# 2D field
|
|
678
|
+
field_data = np.fromfile(file).reshape(int(self.vars.NY),int(self.vars.NX))
|
|
679
|
+
"""
|
|
680
|
+
return field_data
|
|
681
|
+
else:
|
|
682
|
+
raise AssertionError(f"File with field '{file}' not found")
|
|
683
|
+
|
|
684
|
+
def load_allfields(self,fluid,snapshot=None,type='scalar'):
|
|
685
|
+
"""Load all fields in the output
|
|
686
|
+
"""
|
|
687
|
+
qall = False
|
|
688
|
+
if snapshot is None:
|
|
689
|
+
qall = True
|
|
690
|
+
fields = fargopy.Dictobj()
|
|
691
|
+
else:
|
|
692
|
+
fields = fargopy.Dictobj()
|
|
693
|
+
|
|
694
|
+
# Search for field files
|
|
695
|
+
pattern = f"{self.output_dir}/{fluid}*.dat"
|
|
696
|
+
error,output = fargopy.Sys.run(f"ls {pattern}")
|
|
697
|
+
|
|
698
|
+
if not error:
|
|
699
|
+
size = 0
|
|
700
|
+
for file_field in output[:-1]:
|
|
701
|
+
comps = Simulation._parse_file_field(file_field)
|
|
702
|
+
if comps:
|
|
703
|
+
if qall:
|
|
704
|
+
# Store all snapshots
|
|
705
|
+
field_name = comps[0]
|
|
706
|
+
field_snap = int(comps[1])
|
|
707
|
+
|
|
708
|
+
if type == 'scalar':
|
|
709
|
+
field_data = self._load_field_scalar(file_field)
|
|
710
|
+
elif type == 'vector':
|
|
711
|
+
field_data = []
|
|
712
|
+
variables = ['x','y']
|
|
713
|
+
if self.vars.DIM == 3:
|
|
714
|
+
variables += ['z']
|
|
715
|
+
for i,variable in enumerate(variables):
|
|
716
|
+
file_name = f"{fluid}{variable}{str(field_snap)}.dat"
|
|
717
|
+
file_field = f"{self.output_dir}/{file_name}".replace('//','/')
|
|
718
|
+
field_data += [self._load_field_scalar(file_field)]
|
|
719
|
+
field_data = np.array(field_data)
|
|
720
|
+
field_name = field_name[:-1]
|
|
721
|
+
|
|
722
|
+
if str(field_snap) not in fields.keys():
|
|
723
|
+
fields.__dict__[str(field_snap)] = fargopy.Dictobj()
|
|
724
|
+
size += field_data.nbytes
|
|
725
|
+
(fields.__dict__[str(field_snap)]).__dict__[f"{field_name}"] = fargopy.Field(data=field_data, coordinates=self.vars.COORDINATES, domains=self.domains, type=type)
|
|
726
|
+
|
|
727
|
+
else:
|
|
728
|
+
# Store a specific snapshot
|
|
729
|
+
if int(comps[1]) == snapshot:
|
|
730
|
+
field_name = comps[0]
|
|
731
|
+
|
|
732
|
+
if type == 'scalar':
|
|
733
|
+
field_data = self._load_field_scalar(file_field)
|
|
734
|
+
elif type == 'vector':
|
|
735
|
+
field_data = []
|
|
736
|
+
variables = ['x','y']
|
|
737
|
+
if self.vars.DIM == 3:
|
|
738
|
+
variables += ['z']
|
|
739
|
+
for i,variable in enumerate(variables):
|
|
740
|
+
file_name = f"{fluid}{variable}{str(field_snap)}.dat"
|
|
741
|
+
file_field = f"{self.output_dir}/{file_name}".replace('//','/')
|
|
742
|
+
field_data += [self._load_field_scalar(file_field)]
|
|
743
|
+
field_data = np.array(field_data)
|
|
744
|
+
field_name = field_name[:-1]
|
|
745
|
+
|
|
746
|
+
size += field_data.nbytes
|
|
747
|
+
fields.__dict__[f"{field_name}"] = fargopy.Field(data=field_data, coordinates=self.vars.COORDINATES, domains=self.domains, type=type)
|
|
748
|
+
|
|
749
|
+
else:
|
|
750
|
+
raise ValueError(f"No field found with pattern '{pattern}'. Change the fluid")
|
|
751
|
+
|
|
752
|
+
if qall:
|
|
753
|
+
fields.snapshots = sorted([int(s) for s in fields.keys() if s != 'size'])
|
|
754
|
+
fields.size = size/1024**2
|
|
755
|
+
return fields
|
|
756
|
+
|
|
757
|
+
@staticmethod
|
|
758
|
+
def _parse_file_field(file_field):
|
|
759
|
+
basename = os.path.basename(file_field)
|
|
760
|
+
comps = None
|
|
761
|
+
match = re.match('([a-zA-Z]+)(\d+).dat',basename)
|
|
762
|
+
if match is not None:
|
|
763
|
+
comps = [match.group(i) for i in range(1,match.lastindex+1)]
|
|
764
|
+
return comps
|
|
765
|
+
|
|
766
|
+
def __repr__(self):
|
|
767
|
+
return self.__str__()
|
|
768
|
+
|
|
769
|
+
def __str__(self):
|
|
770
|
+
str = f"""Simulation information:
|
|
771
|
+
FARGO3D directory: {self.fargo3d_dir}
|
|
772
|
+
Outputs: {self.outputs_dir}
|
|
773
|
+
Setups: {self.setups_dir}
|
|
774
|
+
Units:
|
|
775
|
+
G = {self.G} UL^3/(UM UT^2)
|
|
776
|
+
UL, UM, UT = {self.UL} m, {self.UM} kg, {self.UT} s
|
|
777
|
+
UE = {self.UEPS} J/m^3
|
|
778
|
+
UV = {self.UV} m/s
|
|
779
|
+
URHO = {self.URHO} kg/m^3
|
|
780
|
+
USIGMA = {self.USIGMA} kg/m^2
|
|
781
|
+
Setup: {self.setup}
|
|
782
|
+
Setup directory: {self.setup_dir}
|
|
783
|
+
Output directory: {self.output_dir}
|
|
784
|
+
"""
|
|
785
|
+
return str
|
|
786
|
+
|
|
787
|
+
# ##########################################################################
|
|
788
|
+
# Static method
|
|
789
|
+
# ##########################################################################
|
|
790
|
+
@staticmethod
|
|
791
|
+
def download_precomputed(setup=None,download_dir='/tmp',quiet=True,clean=True):
|
|
792
|
+
"""Download a precomputed output from Google Drive FARGOpy public repository.
|
|
793
|
+
|
|
794
|
+
Args:
|
|
795
|
+
setup: string, default = None:
|
|
796
|
+
Name of the setup. For a list see fargopu.PRECOMPUTED_SIMULATIONS dictionary.
|
|
797
|
+
|
|
798
|
+
download_dir: string, default = '/tmp':
|
|
799
|
+
Directory where the output will be downloaded and uncompressed.
|
|
800
|
+
|
|
801
|
+
Optional args:
|
|
802
|
+
quiet: bool, default = True:
|
|
803
|
+
If True download quietly (no progress bar).
|
|
804
|
+
|
|
805
|
+
clean: bool, default = False:
|
|
806
|
+
If True remove the tgz file after uncompressing it.
|
|
807
|
+
|
|
808
|
+
Return:
|
|
809
|
+
If successful returns the output directory.
|
|
810
|
+
|
|
811
|
+
"""
|
|
812
|
+
if setup is None:
|
|
813
|
+
print(f"You must provide a setup name. Available setups: {list(PRECOMPUTED_SIMULATIONS.keys())}")
|
|
814
|
+
return ''
|
|
815
|
+
if not os.path.isdir(download_dir):
|
|
816
|
+
print(f"Download directory '{download_dir}' does not exist.")
|
|
817
|
+
return ''
|
|
818
|
+
if setup not in PRECOMPUTED_SIMULATIONS.keys():
|
|
819
|
+
print(f"Precomputed setup '{setup}' is not among the available setups: {list(PRECOMPUTED_SIMULATIONS.keys())}")
|
|
820
|
+
return ''
|
|
821
|
+
|
|
822
|
+
output_dir = (download_dir + '/' + setup).replace('//','/')
|
|
823
|
+
if os.path.isdir(output_dir):
|
|
824
|
+
print(f"Precomputed output directory '{output_dir}' already exist")
|
|
825
|
+
return output_dir
|
|
826
|
+
else:
|
|
827
|
+
filename = setup + '.tgz'
|
|
828
|
+
fileloc = download_dir + '/' + filename
|
|
829
|
+
if os.path.isfile(fileloc):
|
|
830
|
+
print(f"Precomputed file '{fileloc}' already downloaded")
|
|
831
|
+
else:
|
|
832
|
+
# Download the setups
|
|
833
|
+
print(f"Downloading {filename} from cloud (compressed size around {PRECOMPUTED_SIMULATIONS[setup]['size']} MB) into {download_dir}")
|
|
834
|
+
url = PRECOMPUTED_BASEURL + PRECOMPUTED_SIMULATIONS[setup]['id']
|
|
835
|
+
gdown.download(url,fileloc,quiet=quiet)
|
|
836
|
+
# Uncompress the setups
|
|
837
|
+
print(f"Uncompressing {filename} into {output_dir}")
|
|
838
|
+
fargopy.Sys.simple(f"cd {download_dir};tar zxf {filename}")
|
|
839
|
+
print(f"Done.")
|
|
840
|
+
fargopy.Sys.simple(f"cd {download_dir};rm -rf {filename}")
|
|
841
|
+
return output_dir
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
|