fargopy 0.2.0__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/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
+