"
+ ],
+ "text/plain": [
+ "\n",
+ "Dimensions: (name: 10, time: 1)\n",
+ "Coordinates:\n",
+ " * name (name) (tstop - tstart):
- warnings.warn("dt must be smaller than tstop-tstart")
- warnings.warn(f"Setting dt = {tstop - tstart} instead of {dt}")
+ msg = "dt must be smaller than tstop-tstart"
+ msg +=f"\nSetting dt = {tstop - tstart} instead of {dt}"
+ warnings.warn(msg,stacklevel=2)
dt = tstop - tstart
if dt is not None:
@@ -535,7 +557,7 @@ def set_simulation_time(self,
if istep_out is None and tstep_out is None:
istep_out = self.param.pop("ISTEP_OUT", None)
elif istep_out is not None and tstep_out is not None:
- warnings.warn("istep_out and tstep_out cannot both be set")
+ warnings.warn("istep_out and tstep_out cannot both be set",stacklevel=2)
return {}
else:
update_list.append("istep_out")
@@ -643,7 +665,7 @@ def set_parameter(self, verbose: bool = True, **kwargs):
default_arguments = {
"codename" : "Swiftest",
"integrator": "symba",
- "param_file": "param.in",
+ "param_file": Path.cwd() / ".swiftest" / "param.in",
"t0": 0.0,
"tstart": 0.0,
"tstop": None,
@@ -696,9 +718,17 @@ def set_parameter(self, verbose: bool = True, **kwargs):
param_file = kwargs.pop("param_file",None)
+ # Extract the simulation directory and create it if it doesn't exist
if param_file is not None:
- self.param_file = os.path.realpath(param_file)
- self.sim_dir = os.path.dirname(self.param_file)
+ self.param_file = Path.cwd() / param_file
+ self.sim_dir = self.param_file.parent
+ if self.sim_dir.exists():
+ if not self.sim_dir.is_dir():
+ msg = f"Cannot create the {self.sim_dir} directory: File exists."
+ msg += "\nDelete the file or change the location of param_file"
+ warnings.warn(msg,stacklevel=2)
+ else:
+ self.sim_dir.mkdir(parents=True, exist_ok=False)
# Setters returning parameter dictionary values
@@ -783,7 +813,7 @@ def set_integrator(self,
if codename is not None:
valid_codename = ["Swiftest", "Swifter", "Swift"]
if codename.title() not in valid_codename:
- warnings.warn(f"{codename} is not a valid codename. Valid options are ",",".join(valid_codename))
+ warnings.warn(f"{codename} is not a valid codename. Valid options are ",",".join(valid_codename),stacklevel=2)
try:
self.codename
except:
@@ -794,10 +824,10 @@ def set_integrator(self,
self.param['! VERSION'] = f"{self.codename} input file"
update_list.append("codename")
if self.codename == "Swiftest":
- self.binary_path = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(_pyfile)),os.pardir,os.pardir,os.pardir,"bin"))
- self.driver_executable = os.path.join(self.binary_path,"swiftest_driver")
- if not os.path.exists(self.driver_executable):
- warnings.warn(f"Cannot find the Swiftest driver in {self.binary_path}")
+ self.binary_path = Path(_pyfile).parent.parent.parent.parent / "bin"
+ self.driver_executable = self.binary_path / "swiftest_driver"
+ if not self.driver_executable.exists():
+ warnings.warn(f"Cannot find the Swiftest driver in {str(self.binary_path)}",stacklevel=2)
self.driver_executable = None
else:
self.binary_path = "NOT IMPLEMENTED FOR THIS CODE"
@@ -807,7 +837,7 @@ def set_integrator(self,
if integrator is not None:
valid_integrator = ["symba","rmvs","whm","helio"]
if integrator.lower() not in valid_integrator:
- warnings.warn(f"{integrator} is not a valid integrator. Valid options are ",",".join(valid_integrator))
+ warnings.warn(f"{integrator} is not a valid integrator. Valid options are ",",".join(valid_integrator),stacklevel=2)
try:
self.integrator
except:
@@ -818,9 +848,9 @@ def set_integrator(self,
if mtiny is not None or gmtiny is not None:
if self.integrator != "symba":
- warnings.warn("mtiny and gmtiny are only used by SyMBA.")
+ warnings.warn("mtiny and gmtiny are only used by SyMBA.",stacklevel=2)
if mtiny is not None and gmtiny is not None:
- warnings.warn("Only set mtiny or gmtiny, not both!")
+ warnings.warn("Only set mtiny or gmtiny, not both.",stacklevel=2)
elif gmtiny is not None:
self.param['GMTINY'] = gmtiny
update_list.append("gmtiny")
@@ -859,19 +889,19 @@ def get_integrator(self,arg_list: str | List[str] | None = None, verbose: bool |
valid_instance_vars = {"codename": self.codename,
"integrator": self.integrator,
- "param_file": self.param_file,
- "driver_executable": self.driver_executable}
+ "param_file": str(self.param_file),
+ "driver_executable": str(self.driver_executable)}
try:
self.integrator
except:
- warnings.warn(f"integrator is not set")
+ warnings.warn(f"integrator is not set",stacklevel=2)
return {}
try:
self.codename
except:
- warnings.warn(f"codename is not set")
+ warnings.warn(f"codename is not set",stacklevel=2)
return {}
if verbose is None:
@@ -1008,19 +1038,19 @@ def set_feature(self,
if fragmentation is not None:
if self.codename != "Swiftest" and self.integrator != "symba" and fragmentation:
- warnings.warn("Fragmentation is only available on Swiftest SyMBA.")
+ warnings.warn("Fragmentation is only available on Swiftest SyMBA.",stacklevel=2)
self.param['FRAGMENTATION'] = False
else:
self.param['FRAGMENTATION'] = fragmentation
update_list.append("fragmentation")
if fragmentation:
if "MIN_GMFRAG" not in self.param and minimum_fragment_mass is None and minimum_fragment_gmass is None:
- warnings.warn("Minimum fragment mass is not set. Set it using minimum_fragment_gmass or minimum_fragment_mass")
+ warnings.warn("Minimum fragment mass is not set. Set it using minimum_fragment_gmass or minimum_fragment_mass",stacklevel=2)
else:
update_list.append("minimum_fragment_gmass")
if minimum_fragment_gmass is not None and minimum_fragment_mass is not None:
- warnings.warn("Only set either minimum_fragment_mass or minimum_fragment_gmass, but not both!")
+ warnings.warn("Only set either minimum_fragment_mass or minimum_fragment_gmass, but not both!",stacklevel=2)
if minimum_fragment_gmass is not None:
self.param["MIN_GMFRAG"] = minimum_fragment_gmass
@@ -1065,8 +1095,9 @@ def set_feature(self,
if interaction_loops is not None:
valid_vals = ["TRIANGULAR", "FLAT", "ADAPTIVE"]
if interaction_loops not in valid_vals:
- warnings.warn(f"{interaction_loops} is not a valid option for interaction loops.")
- warnings.warn(f"Must be one of {valid_vals}")
+ msg = f"{interaction_loops} is not a valid option for interaction loops."
+ msg += f"\nMust be one of {valid_vals}"
+ warnings.warn(msg,stacklevel=2)
if "INTERACTION_LOOPS" not in self.param:
self.param["INTERACTION_LOOPS"] = valid_vals[0]
else:
@@ -1076,8 +1107,9 @@ def set_feature(self,
if encounter_check_loops is not None:
valid_vals = ["TRIANGULAR", "SORTSWEEP", "ADAPTIVE"]
if encounter_check_loops not in valid_vals:
- warnings.warn(f"{encounter_check_loops} is not a valid option for interaction loops.")
- warnings.warn(f"Must be one of {valid_vals}")
+ msg = f"{encounter_check_loops} is not a valid option for interaction loops."
+ msg += f"\nMust be one of {valid_vals}"
+ warnings.warn(msg,stacklevel=2)
if "ENCOUNTER_CHECK" not in self.param:
self.param["ENCOUNTER_CHECK"] = valid_vals[0]
else:
@@ -1208,13 +1240,14 @@ def set_init_cond_files(self,
return {}
def ascii_file_input_error_msg(codename):
- warnings.warn(f"in set_init_cond_files: init_cond_file_name must be a dictionary of the form: ")
- warnings.warn('{')
+ msg = f"in set_init_cond_files: init_cond_file_name must be a dictionary of the form: "
+ msg += "\n {"
if codename == "Swiftest":
- warnings.warn('"CB" : *path to central body initial conditions file*,')
- warnings.warn('"PL" : *path to massive body initial conditions file*,')
- warnings.warn('"TP" : *path to test particle initial conditions file*')
- warnings.warn('}')
+ msg += '\n"CB" : *path to central body initial conditions file*,'
+ msg += '\n"PL" : *path to massive body initial conditions file*,'
+ msg += '\n"TP" : *path to test particle initial conditions file*'
+ msg += '\n}'
+ warnings.warn(msg,stacklevel=2)
return {}
if init_cond_format is None:
@@ -1234,21 +1267,21 @@ def ascii_file_input_error_msg(codename):
else:
init_cond_keys = ["PL", "TP"]
if init_cond_file_type != "ASCII":
- warnings.warn(f"{init_cond_file_type} is not supported by {self.codename}. Using ASCII instead")
+ warnings.warn(f"{init_cond_file_type} is not supported by {self.codename}. Using ASCII instead",stacklevel=2)
init_cond_file_type = "ASCII"
if init_cond_format != "XV":
- warnings.warn(f"{init_cond_format} is not supported by {self.codename}. Using XV instead")
+ warnings.warn(f"{init_cond_format} is not supported by {self.codename}. Using XV instead",stacklevel=2)
init_cond_format = "XV"
valid_formats = {"EL", "XV"}
if init_cond_format not in valid_formats:
- warnings.warn(f"{init_cond_format} is not a valid input format")
+ warnings.warn(f"{init_cond_format} is not a valid input format",stacklevel=2)
else:
self.param['IN_FORM'] = init_cond_format
valid_types = {"NETCDF_DOUBLE", "NETCDF_FLOAT", "ASCII"}
if init_cond_file_type not in valid_types:
- warnings.warn(f"{init_cond_file_type} is not a valid input type")
+ warnings.warn(f"{init_cond_file_type} is not a valid input type",stackevel=2)
else:
self.param['IN_TYPE'] = init_cond_file_type
@@ -1275,7 +1308,7 @@ def ascii_file_input_error_msg(codename):
elif type(init_cond_file_name) is dict:
# Oops, accidentally passed a dictionary instead of the expected single string or path-like for NetCDF
# input type.
- warnings.warn(f"Only a single input file is used for NetCDF files")
+ warnings.warn(f"Only a single input file is used for NetCDF files",stacklevel=2)
else:
self.param["NC_IN"] = init_cond_file_name
@@ -1416,7 +1449,7 @@ def set_output_files(self,
if output_file_type is None:
output_file_type = "NETCDF_DOUBLE"
elif output_file_type not in ["NETCDF_DOUBLE", "NETCDF_FLOAT"]:
- warnings.warn(f"{output_file_type} is not compatible with Swiftest. Setting to NETCDF_DOUBLE")
+ warnings.warn(f"{output_file_type} is not compatible with Swiftest. Setting to NETCDF_DOUBLE",stacklevel=2)
output_file_type = "NETCDF_DOUBLE"
elif self.codename == "Swifter":
if output_file_type is None:
@@ -1424,7 +1457,7 @@ def set_output_files(self,
if output_file_type is None:
output_file_type = "REAL8"
elif output_file_type not in ["REAL4", "REAL8", "XDR4", "XDR8"]:
- warnings.warn(f"{output_file_type} is not compatible with Swifter. Setting to REAL8")
+ warnings.warn(f"{output_file_type} is not compatible with Swifter. Setting to REAL8",stacklevel=2)
output_file_type = "REAL8"
elif self.codename == "Swift":
if output_file_type is None:
@@ -1432,7 +1465,7 @@ def set_output_files(self,
if output_file_type is None:
output_file_type = "REAL4"
if output_file_type not in ["REAL4"]:
- warnings.warn(f"{output_file_type} is not compatible with Swift. Setting to REAL4")
+ warnings.warn(f"{output_file_type} is not compatible with Swift. Setting to REAL4",stacklevel=2)
output_file_type = "REAL4"
self.param['OUT_TYPE'] = output_file_type
@@ -1445,7 +1478,7 @@ def set_output_files(self,
self.param['BIN_OUT'] = output_file_name
if output_format != "XV" and self.codename != "Swiftest":
- warnings.warn(f"{output_format} is not compatible with {self.codename}. Setting to XV")
+ warnings.warn(f"{output_format} is not compatible with {self.codename}. Setting to XV",stacklevel=2)
output_format = "XV"
self.param["OUT_FORM"] = output_format
@@ -1620,7 +1653,7 @@ def set_unit_system(self,
self.param['MU2KG'] = 1000.0
self.MU_name = "g"
else:
- warnings.warn(f"{MU} not a recognized unit system. Using MSun as a default.")
+ warnings.warn(f"{MU} not a recognized unit system. Using MSun as a default.",stacklevel=2)
self.param['MU2KG'] = constants.MSun
self.MU_name = "MSun"
@@ -1643,7 +1676,7 @@ def set_unit_system(self,
self.param['DU2M'] = 100.0
self.DU_name = "cm"
else:
- warnings.warn(f"{DU} not a recognized unit system. Using AU as a default.")
+ warnings.warn(f"{DU} not a recognized unit system. Using AU as a default.",stacklevel=2)
self.param['DU2M'] = constants.AU2M
self.DU_name = "AU"
@@ -1663,7 +1696,7 @@ def set_unit_system(self,
self.param['TU2S'] = 1.0
self.TU_name = "s"
else:
- warnings.warn(f"{TU} not a recognized unit system. Using YR as a default.")
+ warnings.warn(f"{TU} not a recognized unit system. Using YR as a default.",stacklevel=2)
self.param['TU2S'] = constants.YR2S
self.TU_name = "y"
@@ -1846,7 +1879,7 @@ def set_distance_range(self,
if qmin_coord is not None:
valid_qmin_coord = ["HELIO","BARY"]
if qmin_coord.upper() not in valid_qmin_coord:
- warnings.warn(f"qmin_coord = {qmin_coord} is not a valid option. Must be one of",','.join(valid_qmin_coord))
+ warnings.warn(f"qmin_coord = {qmin_coord} is not a valid option. Must be one of",','.join(valid_qmin_coord),stacklevel=2)
self.param['CHK_QMIN_COORD'] = valid_qmin_coord[0]
else:
self.param['CHK_QMIN_COORD'] = qmin_coord.upper()
@@ -1969,7 +2002,7 @@ def add_solar_system_body(self,
if type(ephemeris_id) is int:
ephemeris_id = [ephemeris_id]
if len(ephemeris_id) != len(name):
- warnings.warn(f"The length of ephemeris_id ({len(ephemeris_id)}) does not match the length of name ({len(name)})")
+ warnings.warn(f"The length of ephemeris_id ({len(ephemeris_id)}) does not match the length of name ({len(name)})",stacklevel=2)
return None
else:
ephemeris_id = [None] * len(name)
@@ -1982,11 +2015,11 @@ def add_solar_system_body(self,
try:
datetime.datetime.fromisoformat(date)
except:
- warnings.warn(f"{date} is not a valid date format. Must be 'YYYY-MM-DD'. Setting to {self.ephemeris_date}")
+ warnings.warn(f"{date} is not a valid date format. Must be 'YYYY-MM-DD'. Setting to {self.ephemeris_date}",stacklevel=2)
date = self.ephemeris_date
if source.upper() != "HORIZONS":
- warnings.warn("Currently only the JPL Horizons ephemeris service is supported")
+ warnings.warn("Currently only the JPL Horizons ephemeris service is supported",stacklevel=2)
body_list = []
for i,n in enumerate(name):
@@ -2091,8 +2124,9 @@ def set_ephemeris_date(self,
datetime.datetime.fromisoformat(ephemeris_date)
except:
valid_date_args = ['"MBCL"', '"TODAY"', '"YYYY-MM-DD"']
- warnings.warn(f"{ephemeris_date} is not a valid format. Valid options include:", ', '.join(valid_date_args))
- warnings.warn("Using MBCL for date.")
+ msg = f"{ephemeris_date} is not a valid format. Valid options include:", ', '.join(valid_date_args)
+ msg += "\nUsing MBCL for date."
+ warnings.warn(msg,stacklevel=2)
ephemeris_date = minton_bcl
self.ephemeris_date = ephemeris_date
@@ -2127,7 +2161,7 @@ def get_ephemeris_date(self, arg_list: str | List[str] | None = None, verbose: b
try:
self.ephemeris_date
except:
- warnings.warn(f"ephemeris_date is not set")
+ warnings.warn(f"ephemeris_date is not set",stacklevel=2)
return
valid_arg = {"ephemeris_date": self.ephemeris_date}
@@ -2322,8 +2356,9 @@ def _combine_and_fix_dsnew(self,dsnew):
dsnew = dsnew.swap_dims({"id" : "name"})
dsnew = dsnew.reset_coords("id")
else:
- warnings.warn("Non-unique names detected for bodies. The Dataset will be dimensioned by integer id instead of name.")
- warnings.warn("Consider using unique names instead.")
+ msg = "Non-unique names detected for bodies. The Dataset will be dimensioned by integer id instead of name."
+ msg +="\nConsider using unique names instead."
+ print(msg)
self.data = xr.combine_by_coords([self.data, dsnew])
@@ -2393,7 +2428,7 @@ def read_param(self,
elif codename == "Swift":
self.param = io.read_swift_param(param_file, verbose=verbose)
else:
- warnings.warn(f'{codename} is not a recognized code name. Valid options are "Swiftest", "Swifter", or "Swift".')
+ warnings.warn(f'{codename} is not a recognized code name. Valid options are "Swiftest", "Swifter", or "Swift".',stacklevel=2)
return False
return True
@@ -2444,7 +2479,7 @@ def write_param(self,
elif codename == "Swift":
io.write_swift_param(param, param_file)
else:
- warnings.warn('Cannot process unknown code type. Call the read_param method with a valid code name. Valid options are "Swiftest", "Swifter", or "Swift".')
+ warnings.warn('Cannot process unknown code type. Call the read_param method with a valid code name. Valid options are "Swiftest", "Swifter", or "Swift".',stacklevel=2)
return
def convert(self, param_file, newcodename="Swiftest", plname="pl.swiftest.in", tpname="tp.swiftest.in",
@@ -2474,10 +2509,10 @@ def convert(self, param_file, newcodename="Swiftest", plname="pl.swiftest.in", t
"""
oldparam = self.param
if self.codename == newcodename:
- warnings.warn(f"This parameter configuration is already in {newcodename} format")
+ warnings.warn(f"This parameter configuration is already in {newcodename} format",stacklevel=2)
return oldparam
if newcodename != "Swift" and newcodename != "Swifter" and newcodename != "Swiftest":
- warnings.warn(f'{newcodename} is an invalid code type. Valid options are "Swiftest", "Swifter", or "Swift".')
+ warnings.warn(f'{newcodename} is an invalid code type. Valid options are "Swiftest", "Swifter", or "Swift".',stacklevel=2)
return oldparam
goodconversion = True
if self.codename == "Swifter":
@@ -2498,7 +2533,7 @@ def convert(self, param_file, newcodename="Swiftest", plname="pl.swiftest.in", t
if goodconversion:
self.write_param(param_file)
else:
- warnings.warn(f"Conversion from {self.codename} to {newcodename} is not supported.")
+ warnings.warn(f"Conversion from {self.codename} to {newcodename} is not supported.",stacklevel=2)
return oldparam
def bin2xr(self):
@@ -2525,9 +2560,9 @@ def bin2xr(self):
self.data = io.swifter2xr(param_tmp, verbose=self.verbose)
if self.verbose: print('Swifter simulation data stored as xarray DataSet .data')
elif self.codename == "Swift":
- warnings.warn("Reading Swift simulation data is not implemented yet")
+ warnings.warn("Reading Swift simulation data is not implemented yet",stacklevel=2)
else:
- warnings.warn('Cannot process unknown code type. Call the read_param method with a valid code name. Valid options are "Swiftest", "Swifter", or "Swift".')
+ warnings.warn('Cannot process unknown code type. Call the read_param method with a valid code name. Valid options are "Swiftest", "Swifter", or "Swift".',stacklevel=2)
return
def follow(self, codestyle="Swifter"):
@@ -2558,7 +2593,7 @@ def follow(self, codestyle="Swifter"):
i_list = [i for i in line.split(" ") if i.strip()]
nskp = int(i_list[0])
except IOError:
- warnings.warn('No follow.in file found')
+ warnings.warn('No follow.in file found',stacklevel=2)
ifol = None
nskp = None
fol = tool.follow_swift(self.data, ifol=ifol, nskp=nskp)
@@ -2603,7 +2638,8 @@ def save(self,
param = self.param
if codename == "Swiftest":
- io.swiftest_xr2infile(ds=self.data, param=param, in_type=self.param['IN_TYPE'], framenum=framenum)
+ infile_name = Path(self.sim_dir) / param['NC_IN']
+ io.swiftest_xr2infile(ds=self.data, param=param, in_type=self.param['IN_TYPE'], infile_name=infile_name, framenum=framenum)
self.write_param(param_file=param_file)
elif codename == "Swifter":
if codename == "Swiftest":
@@ -2613,7 +2649,7 @@ def save(self,
io.swifter_xr2infile(self.data, swifter_param, framenum)
self.write_param(param_file, param=swifter_param)
else:
- warnings.warn(f'Saving to {codename} not supported')
+ warnings.warn(f'Saving to {codename} not supported',stacklevel=2)
return
@@ -2658,7 +2694,7 @@ def initial_conditions_from_bin(self, framenum=-1, new_param=None, new_param_fil
elif self.param['OUT_TYPE'] == 'NETCDF_FLOAT':
new_param['IN_TYPE'] = 'NETCDF_FLOAT'
else:
- warnings.warn(f"{self.param['OUT_TYPE']} is an invalid OUT_TYPE file")
+ warnings.warn(f"{self.param['OUT_TYPE']} is an invalid OUT_TYPE file",stacklevel=2)
return
if self.param['BIN_OUT'] != new_param['BIN_OUT'] and restart:
diff --git a/src/io/io.f90 b/src/io/io.f90
index 86cf55728..08ab7f868 100644
--- a/src/io/io.f90
+++ b/src/io/io.f90
@@ -9,8 +9,80 @@
submodule (swiftest_classes) s_io
use swiftest
+
contains
+ module subroutine io_pbar_reset(self, nloops)
+ !! author: David A. Minton
+ !!
+ !! Resets the progress bar to the beginning
+ implicit none
+ ! Arguments
+ class(progress_bar),intent(inout) :: self
+ integer(I8B), intent(in) :: nloops
+ ! Internals
+ character(len=2) :: numchar
+ character(len=5) :: barfmt
+
+ if (.not.allocated(self%barstr)) then
+ allocate(character(self%PBARSIZE+4) :: self%barstr)
+ end if
+ write(numchar,'(I2)') self%PBARSIZE
+ self%fmt = '(A1,"[",A' // numchar // ',"]",$)'
+ barfmt = '(A' // numchar // ')'
+ write(self%barstr,barfmt) " "
+ self%nloops = nloops
+ self%spinner = 0
+
+ call self%update(0)
+
+ return
+ end subroutine io_pbar_reset
+
+ module subroutine io_pbar_update(self,i)
+ !! author: David A. Minton
+ !!
+ !! Updates the progress bar with new values and causes the "spinner" to flip.
+ implicit none
+ ! Arguments
+ class(progress_bar), intent(inout) :: self
+ integer(I8B), intent(in) :: i
+ ! Internals
+ integer(I4B) :: k
+ real(DP) :: frac
+ integer(I4B) :: pos !! The current integer position of the progress bar
+
+ ! Compute the current position
+ frac = real(i,kind=DP) / real(self%nloops,kind=DP)
+ pos = 1 + min(int(ceiling(frac * self%PBARSIZE),kind=I4B),self%PBARSIZE)
+
+ ! Fill in the bar character up to the current position
+ do k = 1, pos
+ self%barstr(k:k) = self%barchar
+ end do
+
+ self%spinner = self%spinner + 1
+ if (self%spinner > 4) self%spinner = 1
+ select case(self%spinner)
+ case(1)
+ self%barstr(pos:pos) = "/"
+ case(2)
+ self%barstr(pos:pos) = "-"
+ case(3)
+ self%barstr(pos:pos) = "\"
+ case(4)
+ self%barstr(pos:pos) = "|"
+ end select
+
+ write(*,fmt=self%fmt) char(13),self%barstr
+
+
+ return
+ end subroutine io_pbar_update
+
+
+
+
module subroutine io_conservation_report(self, param, lterminal)
!! author: The Purdue Swiftest Team - David A. Minton, Carlisle A. Wishard, Jennifer L.L. Pouplin, and Jacob R. Elliott
!!
diff --git a/src/main/swiftest_driver.f90 b/src/main/swiftest_driver.f90
index 467403269..0eca02509 100644
--- a/src/main/swiftest_driver.f90
+++ b/src/main/swiftest_driver.f90
@@ -31,6 +31,7 @@ program swiftest_driver
real(DP) :: old_t_final = 0.0_DP !! Output time at which writing should start, in order to prevent duplicate lines being written for restarts
type(walltimer) :: integration_timer !! Object used for computing elapsed wall time
real(DP) :: tfrac
+ type(progress_bar) :: pbar !! Object used to print out a progress bar
character(*), parameter :: statusfmt = '("Time = ", ES12.5, "; fraction done = ", F6.3, ' // &
'"; Number of active pl, tp = ", I5, ", ", I5)'
character(*), parameter :: symbastatfmt = '("Time = ", ES12.5, "; fraction done = ", F6.3, ' // &
@@ -88,6 +89,7 @@ program swiftest_driver
!$ write(*,'(a,i3,/)') ' Number of threads = ', nthreads
write(*, *) " *************** Main Loop *************** "
if (param%lrestart .and. param%lenergy) call nbody_system%conservation_report(param, lterminal=.true.)
+ call pbar%reset(nloops)
do iloop = 1, nloops
!> Step the system forward in time
call integration_timer%start()
@@ -100,6 +102,7 @@ program swiftest_driver
call nbody_system%discard(param)
!> If the loop counter is at the output cadence value, append the data file with a single frame
+ call pbar%update(iloop)
if (istep_out > 0) then
iout = iout - 1
if (iout == 0) then
@@ -108,15 +111,15 @@ program swiftest_driver
tfrac = (param%t - param%t0) / (param%tstop - param%t0)
- select type(pl => nbody_system%pl)
- class is (symba_pl)
- write(*, symbastatfmt) param%t, tfrac, pl%nplm, pl%nbody, nbody_system%tp%nbody
- class default
- write(*, statusfmt) param%t, tfrac, pl%nbody, nbody_system%tp%nbody
- end select
+ ! select type(pl => nbody_system%pl)
+ ! class is (symba_pl)
+ ! write(*, symbastatfmt) param%t, tfrac, pl%nplm, pl%nbody, nbody_system%tp%nbody
+ ! class default
+ ! write(*, statusfmt) param%t, tfrac, pl%nbody, nbody_system%tp%nbody
+ ! end select
if (param%lenergy) call nbody_system%conservation_report(param, lterminal=.true.)
- call integration_timer%report(message="Integration steps:", nsubsteps=istep_out)
- call integration_timer%reset()
+ !call integration_timer%report(message="Integration steps:", nsubsteps=istep_out)
+ !call integration_timer%reset()
iout = istep_out
end if
diff --git a/src/modules/swiftest_classes.f90 b/src/modules/swiftest_classes.f90
index 6c0ac2be3..49735a424 100644
--- a/src/modules/swiftest_classes.f90
+++ b/src/modules/swiftest_classes.f90
@@ -414,6 +414,21 @@ module swiftest_classes
generic :: read_particle_info => read_particle_info_bin, read_particle_info_netcdf !! Genereric method call for reading in the particle information metadata
end type swiftest_nbody_system
+ type :: progress_bar
+ !! author: David A. Minton
+ !!
+ !! Implements a class for a simple progress bar that can print on the screen.
+ integer(I4B) :: PBARSIZE = 80 !! Number of characters acros for a whole progress bar
+ integer(I8B) :: nloops !! The total number of loops that the progrees bar is executing
+ character(len=:), allocatable :: barstr !! The string that prints out as the progress bar
+ integer(I4B) :: spinner !! Position of the "spinner" that indicates that progress is being made
+ character(len=1) :: barchar = "=" !! The progress bar character
+ character(len=32) :: fmt !! The format string that is used to define the progress bar itself
+ contains
+ procedure :: reset => io_pbar_reset !! Resets the progress bar to the beginning
+ procedure :: update => io_pbar_update !! Updates the progress bar with new values and causes the "spinner" to flip.
+ end type progress_bar
+
abstract interface
subroutine abstract_accel(self, system, param, t, lbeg)
@@ -582,6 +597,18 @@ pure module subroutine gr_vh2pv_body(self, param)
class(swiftest_parameters), intent(in) :: param !! Current run configuration parameters
end subroutine gr_vh2pv_body
+ module subroutine io_pbar_reset(self, nloops)
+ implicit none
+ class(progress_bar),intent(inout) :: self
+ integer(I8B), intent(in) :: nloops
+ end subroutine io_pbar_reset
+
+ module subroutine io_pbar_update(self,i)
+ implicit none
+ class(progress_bar), intent(inout) :: self
+ integer(I8B), intent(in) :: i
+ end subroutine io_pbar_update
+
module subroutine io_conservation_report(self, param, lterminal)
implicit none
class(swiftest_nbody_system), intent(inout) :: self !! Swiftest nbody system object