From 6198f5109e9835ced8a77eb2598af809779f910c Mon Sep 17 00:00:00 2001 From: RJbalikian <46536937+RJbalikian@users.noreply.github.com> Date: Tue, 31 Oct 2023 15:10:38 -0500 Subject: [PATCH] fix error with settings read, update vernum --- conda/meta.yaml | 4 +- docs/generate_docs.py | 2 +- docs/main.html | 243 +++++++++-- docs/sprit_hvsr.html | 404 +++++++++++++++---- pyproject.toml | 2 +- setup.py | 2 +- sprit/__pycache__/sprit_hvsr.cpython-310.pyc | Bin 178224 -> 184846 bytes sprit/sprit_hvsr.py | 37 +- 8 files changed, 568 insertions(+), 126 deletions(-) diff --git a/conda/meta.yaml b/conda/meta.yaml index 3cb9414..af2ce6b 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -1,10 +1,10 @@ package: name: sprit - version: 0.1.50 + version: 0.1.51 source: git_url: https://github.com/RJbalikian/SPRIT-HVSR - git_tag: v0.1.50 + git_tag: v0.1.51 build: number: 0 diff --git a/docs/generate_docs.py b/docs/generate_docs.py index 7383dae..5a34648 100644 --- a/docs/generate_docs.py +++ b/docs/generate_docs.py @@ -8,7 +8,7 @@ #Whether to convert_md using markdown library (True), or let github do it (False) convert_md=True rtd_theme=False #Not currently working -release_version= '0.1.50' +release_version= '0.1.51' run_tests=True lint_it=True diff --git a/docs/main.html b/docs/main.html index 322d7d7..1f85d24 100644 --- a/docs/main.html +++ b/docs/main.html @@ -636,7 +636,7 @@

Returns

defaultVDict = dict(zip(inspect.getfullargspec(check_peaks).args[1:], inspect.getfullargspec(check_peaks).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v hvsr_band = orig_args['hvsr_band'] @@ -965,7 +965,7 @@

Parameters

-def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=False) +def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True)

Save settings to json file

@@ -990,7 +990,7 @@

Parameters

Expand source code -
def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=False):
+
def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True):
     """Save settings to json file
 
     Parameters
@@ -1070,31 +1070,70 @@ 

Parameters

else: processing_settings_dict[funcName][arg] = hvsr_data['processing_parameters'][funcName][arg] + if verbose: + print("Exporting Settings") #Save settings files if export_settings_type.lower()=='instrument' or export_settings_type.lower()=='all': - with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: - jsonString = json.dumps(instrument_settings_dict, indent=2) - #Format output for readability - jsonString = jsonString.replace('\n ', ' ') - jsonString = jsonString.replace('[ ', '[') - jsonString = jsonString.replace('\n ]', ']') - #Export - instSetF.write(jsonString) + try: + with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: + jsonString = json.dumps(instrument_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + #Export + instSetF.write(jsonString) + except: + instSetFPath = pathlib.Path.home().joinpath(instSetFPath.name) + with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: + jsonString = json.dumps(instrument_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + #Export + instSetF.write(jsonString) + + if verbose: + print(f"Instrument settings exported to {instSetFPath}") + print(f"{jsonString}") + print() if export_settings_type.lower()=='processing' or export_settings_type.lower()=='all': - with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: - jsonString = json.dumps(processing_settings_dict, indent=2) - #Format output for readability - jsonString = jsonString.replace('\n ', ' ') - jsonString = jsonString.replace('[ ', '[') - jsonString = jsonString.replace('\n ]', ']') - jsonString = jsonString.replace('\n },','\n\t\t},\n') - jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') - jsonString = jsonString.replace(', "', ',\n\t\t"') - jsonString = jsonString.replace('\n }', '\n\t\t}') - jsonString = jsonString.replace(': {', ':\n\t\t\t{') - - #Export - procSetF.write(jsonString)
+ try: + with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: + jsonString = json.dumps(processing_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + jsonString = jsonString.replace('\n },','\n\t\t},\n') + jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') + jsonString = jsonString.replace(', "', ',\n\t\t"') + jsonString = jsonString.replace('\n }', '\n\t\t}') + jsonString = jsonString.replace(': {', ':\n\t\t\t{') + + #Export + procSetF.write(jsonString) + except: + procSetFPath = pathlib.Path.home().joinpath(procSetFPath.name) + with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: + jsonString = json.dumps(processing_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + jsonString = jsonString.replace('\n },','\n\t\t},\n') + jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') + jsonString = jsonString.replace(', "', ',\n\t\t"') + jsonString = jsonString.replace('\n }', '\n\t\t}') + jsonString = jsonString.replace(': {', ':\n\t\t\t{') + + #Export + procSetF.write(jsonString) + if verbose: + print(f"Processing settings exported to {procSetFPath}") + print(f"{jsonString}") + print()
@@ -1109,6 +1148,7 @@

Parameters

def fetch_data(params, source='file', trim_dir=None, export_format='mseed', detrend='spline', detrend_order=2, update_metadata=True, plot_input_stream=False, verbose=False, **kwargs):
     #Get intput paramaters
     orig_args = locals().copy()
+    start_time = datetime.datetime.now()
     
     # Update with processing parameters specified previously in input_params, if applicable
     if 'processing_parameters' in params.keys():
@@ -1118,7 +1158,7 @@ 

Parameters

defaultVDict['kwargs'] = kwargs for k, v in params['processing_parameters']['fetch_data'].items(): # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if k!='params' and k in orig_args.keys() and orig_args[k]==defaultVDict[k]: orig_args[k] = v #Update local variables, in case of previously-specified parameters @@ -1530,7 +1570,7 @@

Parameters

for line in dataINStr: print('\t',line) - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params
@@ -1786,7 +1826,9 @@

Returns

Dictionary containing entries with ppsds for each channel """ #First, divide up for batch or not + print(locals()) orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() ppsd_kwargs_sprit_defaults = ppsd_kwargs.copy() #Set defaults here that are different than obspy defaults @@ -1820,9 +1862,8 @@

Returns

inspect.getfullargspec(generate_ppsds).defaults)) defaultVDict['ppsd_kwargs'] = ppsd_kwargs for k, v in hvsr_data['processing_parameters']['generate_ppsds'].items(): - # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if not isinstance(v, (HVSRData, HVSRBatch)) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v remove_outliers = orig_args['remove_outliers'] @@ -2030,7 +2071,7 @@

Returns

hvsr_data['processing_parameters']['generate_ppsds'][key] = value hvsr_data['ProcessingStatus']['PPSDStatus'] = True - hvsr_data = _check_processing_status(hvsr_data) + hvsr_data = _check_processing_status(hvsr_data, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return hvsr_data
@@ -2260,7 +2301,7 @@

Returns

defaultVDict = dict(zip(inspect.getfullargspec(get_report).args[1:], inspect.getfullargspec(get_report).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v report_format = orig_args['report_format'] @@ -2741,7 +2782,7 @@

Returns

-def input_params(datapath, site='HVSR Site', network='AM', station='RAC84', loc='00', channels=['EHZ', 'EHN', 'EHE'], acq_date='2023-10-30', starttime='00:00:00.00', endtime='23:59:59.999999', tzone='UTC', xcoord=-88.2290526, ycoord=40.1012122, elevation=755, input_crs='EPSG:4326', output_crs='EPSG:4326', elev_unit='feet', depth=0, instrument='Raspberry Shake', metapath=None, hvsr_band=[0.4, 40], peak_freq_range=[0.4, 40], processing_parameters={}, verbose=False) +def input_params(datapath, site='HVSR Site', network='AM', station='RAC84', loc='00', channels=['EHZ', 'EHN', 'EHE'], acq_date='2023-10-31', starttime='00:00:00.00', endtime='23:59:59.999999', tzone='UTC', xcoord=-88.2290526, ycoord=40.1012122, elevation=755, input_crs='EPSG:4326', output_crs='EPSG:4326', elev_unit='feet', depth=0, instrument='Raspberry Shake', metapath=None, hvsr_band=[0.4, 40], peak_freq_range=[0.4, 40], processing_parameters={}, verbose=False)

Function for designating input parameters for reading in and processing data

@@ -2899,6 +2940,7 @@

Returns

""" orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() #Reformat times if type(acq_date) is datetime.datetime: @@ -3041,7 +3083,7 @@

Returns

#Format everything nicely params = sprit_utils.make_it_classy(inputParamDict) params['ProcessingStatus']['InputParams'] = True - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params
@@ -3406,6 +3448,7 @@

Returns

""" orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -3414,7 +3457,7 @@

Returns

defaultVDict = dict(zip(inspect.getfullargspec(process_hvsr).args[1:], inspect.getfullargspec(process_hvsr).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v method = orig_args['method'] @@ -3685,7 +3728,7 @@

Returns

hvsr_out['input_stream'] = hvsr_dataUpdate['input_params']['input_stream'] #input_stream hvsr_out = sprit_utils.make_it_classy(hvsr_out) hvsr_out['ProcessingStatus']['HVStatus'] = True - hvsr_out = _check_processing_status(hvsr_out) + hvsr_out = _check_processing_status(hvsr_out, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) hvsr_data['processing_parameters']['process_hvsr'] = {} for key, value in orig_args.items(): @@ -3874,6 +3917,7 @@

Returns

""" #Get intput paramaters orig_args = locals().copy() + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -4025,7 +4069,7 @@

Returns

output['processing_parameters']['remove_noise'][key] = value output['ProcessingStatus']['RemoveNoiseStatus'] = True - output = _check_processing_status(output) + output = _check_processing_status(output, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) if 'hvsr_df' in output.keys() or ('params' in output.keys() and 'hvsr_df' in output['params'].keys())or ('input_params' in output.keys() and 'hvsr_df' in output['input_params'].keys()): hvsrDF = output['hvsr_df'] @@ -4160,6 +4204,7 @@

Raises

RuntimeError If the data being processed is a single file, an error will be raised if generate_ppsds() does not work correctly. No errors are raised for remove_noise() errors (since that is an optional step) and the process_hvsr() step (since that is the last processing step) . """ + if 'hvsr_band' not in kwargs.keys(): kwargs['hvsr_band'] = inspect.signature(input_params).parameters['hvsr_band'].default if 'peak_freq_range' not in kwargs.keys(): @@ -4479,6 +4524,29 @@

Classes

"""Wrapper of get_report()""" return self.get_report(**kwargs) + def export_settings(self, site_name=None, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True): + """Method to export settings from HVSRData object in HVSRBatch object. Simply calls sprit.export_settings() from specified HVSRData object in the HVSRBatch object. See sprit.export_settings() for more details. + + Parameters + ---------- + site_name : str, default=None + The name of the site whose settings should be exported. If None, will default to the first site, by default None. + export_settings_path : str, optional + Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default' + export_settings_type : str, {'all', 'instrument', 'processing'}, optional + They type of settings to save, by default 'all' + include_location : bool, optional + Whether to include the location information in the instrument settings, if that settings type is selected, by default False + verbose : bool, optional + Whether to print output (filepath and settings) to terminal, by default True + """ + #If no site name selected, use first site + if site_name is None: + site_name = self.sites[0] + + export_settings(hvsr_data=self[site_name], + export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose) + def __iter__(self): return iter(self._batch_dict.keys()) @@ -4548,6 +4616,52 @@

Parameters

export_data(hvsr_data=self, export_path=export_path, ext=ext) +
+def export_settings(self, site_name=None, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True) +
+
+

Method to export settings from HVSRData object in HVSRBatch object. Simply calls sprit.export_settings() from specified HVSRData object in the HVSRBatch object. See sprit.export_settings() for more details.

+

Parameters

+
+
site_name : str, default=None
+
The name of the site whose settings should be exported. If None, will default to the first site, by default None.
+
export_settings_path : str, optional
+
Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
+
export_settings_type : str, {'all', 'instrument', 'processing'}, optional
+
They type of settings to save, by default 'all'
+
include_location : bool, optional
+
Whether to include the location information in the instrument settings, if that settings type is selected, by default False
+
verbose : bool, optional
+
Whether to print output (filepath and settings) to terminal, by default True
+
+
+ +Expand source code + +
def export_settings(self, site_name=None, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True):
+    """Method to export settings from HVSRData object in HVSRBatch object. Simply calls sprit.export_settings() from specified HVSRData object in the HVSRBatch object. See sprit.export_settings() for more details.
+
+    Parameters
+    ----------
+    site_name : str, default=None
+        The name of the site whose settings should be exported. If None, will default to the first site, by default None.
+    export_settings_path : str, optional
+        Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
+    export_settings_type : str, {'all', 'instrument', 'processing'}, optional
+        They type of settings to save, by default 'all'
+    include_location : bool, optional
+        Whether to include the location information in the instrument settings, if that settings type is selected, by default False
+    verbose : bool, optional
+        Whether to print output (filepath and settings) to terminal, by default True
+    """
+    #If no site name selected, use first site
+    if site_name is None:
+        site_name = self.sites[0]
+        
+    export_settings(hvsr_data=self[site_name], 
+                    export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose)
+
+
def get_report(self, **kwargs)
@@ -4829,6 +4943,23 @@

Returns

"""Wrapper of get_report()""" report_return = get_report(self, **kwargs) return report_return + + def export_settings(self, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True): + """Method to export settings from HVSRData object. Simply calls sprit.export_settings() from the HVSRData object. See sprit.export_settings() for more details. + + Parameters + ---------- + export_settings_path : str, optional + Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default' + export_settings_type : str, {'all', 'instrument', 'processing'}, optional + They type of settings to save, by default 'all' + include_location : bool, optional + Whether to include the location information in the instrument settings, if that settings type is selected, by default False + verbose : bool, optional + Whether to print output (filepath and settings) to terminal, by default True + """ + export_settings(hvsr_data=self, + export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose) #ATTRIBUTES #params @@ -5103,6 +5234,44 @@

Parameters

export_data(hvsr_data=self, export_path=export_path, ext=ext) +
+def export_settings(self, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True) +
+
+

Method to export settings from HVSRData object. Simply calls sprit.export_settings() from the HVSRData object. See sprit.export_settings() for more details.

+

Parameters

+
+
export_settings_path : str, optional
+
Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
+
export_settings_type : str, {'all', 'instrument', 'processing'}, optional
+
They type of settings to save, by default 'all'
+
include_location : bool, optional
+
Whether to include the location information in the instrument settings, if that settings type is selected, by default False
+
verbose : bool, optional
+
Whether to print output (filepath and settings) to terminal, by default True
+
+
+ +Expand source code + +
def export_settings(self, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True):
+    """Method to export settings from HVSRData object. Simply calls sprit.export_settings() from the HVSRData object. See sprit.export_settings() for more details.
+
+    Parameters
+    ----------
+    export_settings_path : str, optional
+        Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
+    export_settings_type : str, {'all', 'instrument', 'processing'}, optional
+        They type of settings to save, by default 'all'
+    include_location : bool, optional
+        Whether to include the location information in the instrument settings, if that settings type is selected, by default False
+    verbose : bool, optional
+        Whether to print output (filepath and settings) to terminal, by default True
+    """
+    export_settings(hvsr_data=self, 
+                    export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose)
+
+
def get_report(self, **kwargs)
@@ -5284,6 +5453,7 @@

HVSRBatch
  • copy
  • export
  • +
  • export_settings
  • get_report
  • items
  • keys
  • @@ -5298,6 +5468,7 @@

    HVSRDatacopy
  • datastream
  • export
  • +
  • export_settings
  • get_report
  • items
  • keys
  • diff --git a/docs/sprit_hvsr.html b/docs/sprit_hvsr.html index 85f70b8..9553ede 100644 --- a/docs/sprit_hvsr.html +++ b/docs/sprit_hvsr.html @@ -254,6 +254,29 @@

    Module sprit.sprit_hvsr

    """Wrapper of get_report()""" return self.get_report(**kwargs) + def export_settings(self, site_name=None, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True): + """Method to export settings from HVSRData object in HVSRBatch object. Simply calls sprit.export_settings() from specified HVSRData object in the HVSRBatch object. See sprit.export_settings() for more details. + + Parameters + ---------- + site_name : str, default=None + The name of the site whose settings should be exported. If None, will default to the first site, by default None. + export_settings_path : str, optional + Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default' + export_settings_type : str, {'all', 'instrument', 'processing'}, optional + They type of settings to save, by default 'all' + include_location : bool, optional + Whether to include the location information in the instrument settings, if that settings type is selected, by default False + verbose : bool, optional + Whether to print output (filepath and settings) to terminal, by default True + """ + #If no site name selected, use first site + if site_name is None: + site_name = self.sites[0] + + export_settings(hvsr_data=self[site_name], + export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose) + def __iter__(self): return iter(self._batch_dict.keys()) @@ -393,6 +416,23 @@

    Module sprit.sprit_hvsr

    """Wrapper of get_report()""" report_return = get_report(self, **kwargs) return report_return + + def export_settings(self, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True): + """Method to export settings from HVSRData object. Simply calls sprit.export_settings() from the HVSRData object. See sprit.export_settings() for more details. + + Parameters + ---------- + export_settings_path : str, optional + Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default' + export_settings_type : str, {'all', 'instrument', 'processing'}, optional + They type of settings to save, by default 'all' + include_location : bool, optional + Whether to include the location information in the instrument settings, if that settings type is selected, by default False + verbose : bool, optional + Whether to print output (filepath and settings) to terminal, by default True + """ + export_settings(hvsr_data=self, + export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose) #ATTRIBUTES #params @@ -583,6 +623,7 @@

    Module sprit.sprit_hvsr

    RuntimeError If the data being processed is a single file, an error will be raised if generate_ppsds() does not work correctly. No errors are raised for remove_noise() errors (since that is an optional step) and the process_hvsr() step (since that is the last processing step) . """ + if 'hvsr_band' not in kwargs.keys(): kwargs['hvsr_band'] = inspect.signature(input_params).parameters['hvsr_band'].default if 'peak_freq_range' not in kwargs.keys(): @@ -766,7 +807,7 @@

    Module sprit.sprit_hvsr

    defaultVDict = dict(zip(inspect.getfullargspec(check_peaks).args[1:], inspect.getfullargspec(check_peaks).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v hvsr_band = orig_args['hvsr_band'] @@ -976,7 +1017,7 @@

    Module sprit.sprit_hvsr

    ###WORKING ON THIS #Save default instrument and processing settings to json file(s) -def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=False): +def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True): """Save settings to json file Parameters @@ -1056,36 +1097,76 @@

    Module sprit.sprit_hvsr

    else: processing_settings_dict[funcName][arg] = hvsr_data['processing_parameters'][funcName][arg] + if verbose: + print("Exporting Settings") #Save settings files if export_settings_type.lower()=='instrument' or export_settings_type.lower()=='all': - with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: - jsonString = json.dumps(instrument_settings_dict, indent=2) - #Format output for readability - jsonString = jsonString.replace('\n ', ' ') - jsonString = jsonString.replace('[ ', '[') - jsonString = jsonString.replace('\n ]', ']') - #Export - instSetF.write(jsonString) + try: + with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: + jsonString = json.dumps(instrument_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + #Export + instSetF.write(jsonString) + except: + instSetFPath = pathlib.Path.home().joinpath(instSetFPath.name) + with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: + jsonString = json.dumps(instrument_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + #Export + instSetF.write(jsonString) + + if verbose: + print(f"Instrument settings exported to {instSetFPath}") + print(f"{jsonString}") + print() if export_settings_type.lower()=='processing' or export_settings_type.lower()=='all': - with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: - jsonString = json.dumps(processing_settings_dict, indent=2) - #Format output for readability - jsonString = jsonString.replace('\n ', ' ') - jsonString = jsonString.replace('[ ', '[') - jsonString = jsonString.replace('\n ]', ']') - jsonString = jsonString.replace('\n },','\n\t\t},\n') - jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') - jsonString = jsonString.replace(', "', ',\n\t\t"') - jsonString = jsonString.replace('\n }', '\n\t\t}') - jsonString = jsonString.replace(': {', ':\n\t\t\t{') - - #Export - procSetF.write(jsonString) + try: + with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: + jsonString = json.dumps(processing_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + jsonString = jsonString.replace('\n },','\n\t\t},\n') + jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') + jsonString = jsonString.replace(', "', ',\n\t\t"') + jsonString = jsonString.replace('\n }', '\n\t\t}') + jsonString = jsonString.replace(': {', ':\n\t\t\t{') + + #Export + procSetF.write(jsonString) + except: + procSetFPath = pathlib.Path.home().joinpath(procSetFPath.name) + with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: + jsonString = json.dumps(processing_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + jsonString = jsonString.replace('\n },','\n\t\t},\n') + jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') + jsonString = jsonString.replace(', "', ',\n\t\t"') + jsonString = jsonString.replace('\n }', '\n\t\t}') + jsonString = jsonString.replace(': {', ':\n\t\t\t{') + + #Export + procSetF.write(jsonString) + if verbose: + print(f"Processing settings exported to {procSetFPath}") + print(f"{jsonString}") + print() #Reads in traces to obspy stream def fetch_data(params, source='file', trim_dir=None, export_format='mseed', detrend='spline', detrend_order=2, update_metadata=True, plot_input_stream=False, verbose=False, **kwargs): #Get intput paramaters orig_args = locals().copy() + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in params.keys(): @@ -1095,7 +1176,7 @@

    Module sprit.sprit_hvsr

    defaultVDict['kwargs'] = kwargs for k, v in params['processing_parameters']['fetch_data'].items(): # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if k!='params' and k in orig_args.keys() and orig_args[k]==defaultVDict[k]: orig_args[k] = v #Update local variables, in case of previously-specified parameters @@ -1507,7 +1588,7 @@

    Module sprit.sprit_hvsr

    for line in dataINStr: print('\t',line) - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params @@ -1542,7 +1623,9 @@

    Module sprit.sprit_hvsr

    Dictionary containing entries with ppsds for each channel """ #First, divide up for batch or not + print(locals()) orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() ppsd_kwargs_sprit_defaults = ppsd_kwargs.copy() #Set defaults here that are different than obspy defaults @@ -1576,9 +1659,8 @@

    Module sprit.sprit_hvsr

    inspect.getfullargspec(generate_ppsds).defaults)) defaultVDict['ppsd_kwargs'] = ppsd_kwargs for k, v in hvsr_data['processing_parameters']['generate_ppsds'].items(): - # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if not isinstance(v, (HVSRData, HVSRBatch)) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v remove_outliers = orig_args['remove_outliers'] @@ -1786,7 +1868,7 @@

    Module sprit.sprit_hvsr

    hvsr_data['processing_parameters']['generate_ppsds'][key] = value hvsr_data['ProcessingStatus']['PPSDStatus'] = True - hvsr_data = _check_processing_status(hvsr_data) + hvsr_data = _check_processing_status(hvsr_data, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return hvsr_data #Gets the metadata for Raspberry Shake, specifically for 3D v.7 @@ -1927,7 +2009,7 @@

    Module sprit.sprit_hvsr

    defaultVDict = dict(zip(inspect.getfullargspec(get_report).args[1:], inspect.getfullargspec(get_report).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v report_format = orig_args['report_format'] @@ -2391,6 +2473,7 @@

    Module sprit.sprit_hvsr

    """ orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() #Reformat times if type(acq_date) is datetime.datetime: @@ -2533,7 +2616,7 @@

    Module sprit.sprit_hvsr

    #Format everything nicely params = sprit_utils.make_it_classy(inputParamDict) params['ProcessingStatus']['InputParams'] = True - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params #Main function for plotting results @@ -2887,6 +2970,7 @@

    Module sprit.sprit_hvsr

    """ orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -2895,7 +2979,7 @@

    Module sprit.sprit_hvsr

    defaultVDict = dict(zip(inspect.getfullargspec(process_hvsr).args[1:], inspect.getfullargspec(process_hvsr).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v method = orig_args['method'] @@ -3166,7 +3250,7 @@

    Module sprit.sprit_hvsr

    hvsr_out['input_stream'] = hvsr_dataUpdate['input_params']['input_stream'] #input_stream hvsr_out = sprit_utils.make_it_classy(hvsr_out) hvsr_out['ProcessingStatus']['HVStatus'] = True - hvsr_out = _check_processing_status(hvsr_out) + hvsr_out = _check_processing_status(hvsr_out, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) hvsr_data['processing_parameters']['process_hvsr'] = {} for key, value in orig_args.items(): @@ -3222,6 +3306,7 @@

    Module sprit.sprit_hvsr

    """ #Get intput paramaters orig_args = locals().copy() + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -3373,7 +3458,7 @@

    Module sprit.sprit_hvsr

    output['processing_parameters']['remove_noise'][key] = value output['ProcessingStatus']['RemoveNoiseStatus'] = True - output = _check_processing_status(output) + output = _check_processing_status(output, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) if 'hvsr_df' in output.keys() or ('params' in output.keys() and 'hvsr_df' in output['params'].keys())or ('input_params' in output.keys() and 'hvsr_df' in output['input_params'].keys()): hvsrDF = output['hvsr_df'] @@ -3809,7 +3894,7 @@

    Module sprit.sprit_hvsr

    return hvsr_data #Special helper function that checks the processing status at each stage of processing to help determine if any processing steps were skipped -def _check_processing_status(hvsr_data): +def _check_processing_status(hvsr_data, start_time=datetime.datetime.now(), func_name='', verbose=False): """Internal function to check processing status, used primarily in the sprit.run() function to allow processing to continue if one site is bad. Parameters @@ -3842,7 +3927,11 @@

    Module sprit.sprit_hvsr

    if isinstance(hvsr_data, HVSRData): hvsr_data = hvsr_interim[siteName] - return hvsr_data + + if verbose: + elapsed = (datetime.datetime.now()-start_time) + print(f"\t\t{func_name} completed in {str(elapsed)[:-3]}") + return hvsr_data #HELPER functions for fetch_data() and get_metadata() #Read in metadata .inv file, specifically for RaspShake @@ -7061,7 +7150,7 @@

    Returns

    defaultVDict = dict(zip(inspect.getfullargspec(check_peaks).args[1:], inspect.getfullargspec(check_peaks).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v hvsr_band = orig_args['hvsr_band'] @@ -7290,7 +7379,7 @@

    Parameters

    -def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=False) +def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True)

    Save settings to json file

    @@ -7315,7 +7404,7 @@

    Parameters

    Expand source code -
    def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=False):
    +
    def export_settings(hvsr_data, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True):
         """Save settings to json file
     
         Parameters
    @@ -7395,31 +7484,70 @@ 

    Parameters

    else: processing_settings_dict[funcName][arg] = hvsr_data['processing_parameters'][funcName][arg] + if verbose: + print("Exporting Settings") #Save settings files if export_settings_type.lower()=='instrument' or export_settings_type.lower()=='all': - with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: - jsonString = json.dumps(instrument_settings_dict, indent=2) - #Format output for readability - jsonString = jsonString.replace('\n ', ' ') - jsonString = jsonString.replace('[ ', '[') - jsonString = jsonString.replace('\n ]', ']') - #Export - instSetF.write(jsonString) + try: + with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: + jsonString = json.dumps(instrument_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + #Export + instSetF.write(jsonString) + except: + instSetFPath = pathlib.Path.home().joinpath(instSetFPath.name) + with open(instSetFPath.with_suffix('.inst').as_posix(), 'w') as instSetF: + jsonString = json.dumps(instrument_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + #Export + instSetF.write(jsonString) + + if verbose: + print(f"Instrument settings exported to {instSetFPath}") + print(f"{jsonString}") + print() if export_settings_type.lower()=='processing' or export_settings_type.lower()=='all': - with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: - jsonString = json.dumps(processing_settings_dict, indent=2) - #Format output for readability - jsonString = jsonString.replace('\n ', ' ') - jsonString = jsonString.replace('[ ', '[') - jsonString = jsonString.replace('\n ]', ']') - jsonString = jsonString.replace('\n },','\n\t\t},\n') - jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') - jsonString = jsonString.replace(', "', ',\n\t\t"') - jsonString = jsonString.replace('\n }', '\n\t\t}') - jsonString = jsonString.replace(': {', ':\n\t\t\t{') - - #Export - procSetF.write(jsonString)
    + try: + with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: + jsonString = json.dumps(processing_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + jsonString = jsonString.replace('\n },','\n\t\t},\n') + jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') + jsonString = jsonString.replace(', "', ',\n\t\t"') + jsonString = jsonString.replace('\n }', '\n\t\t}') + jsonString = jsonString.replace(': {', ':\n\t\t\t{') + + #Export + procSetF.write(jsonString) + except: + procSetFPath = pathlib.Path.home().joinpath(procSetFPath.name) + with open(procSetFPath.with_suffix('.proc').as_posix(), 'w') as procSetF: + jsonString = json.dumps(processing_settings_dict, indent=2) + #Format output for readability + jsonString = jsonString.replace('\n ', ' ') + jsonString = jsonString.replace('[ ', '[') + jsonString = jsonString.replace('\n ]', ']') + jsonString = jsonString.replace('\n },','\n\t\t},\n') + jsonString = jsonString.replace('{ "', '\n\t\t{\n\t\t"') + jsonString = jsonString.replace(', "', ',\n\t\t"') + jsonString = jsonString.replace('\n }', '\n\t\t}') + jsonString = jsonString.replace(': {', ':\n\t\t\t{') + + #Export + procSetF.write(jsonString) + if verbose: + print(f"Processing settings exported to {procSetFPath}") + print(f"{jsonString}") + print()
    @@ -7434,6 +7562,7 @@

    Parameters

    def fetch_data(params, source='file', trim_dir=None, export_format='mseed', detrend='spline', detrend_order=2, update_metadata=True, plot_input_stream=False, verbose=False, **kwargs):
         #Get intput paramaters
         orig_args = locals().copy()
    +    start_time = datetime.datetime.now()
         
         # Update with processing parameters specified previously in input_params, if applicable
         if 'processing_parameters' in params.keys():
    @@ -7443,7 +7572,7 @@ 

    Parameters

    defaultVDict['kwargs'] = kwargs for k, v in params['processing_parameters']['fetch_data'].items(): # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if k!='params' and k in orig_args.keys() and orig_args[k]==defaultVDict[k]: orig_args[k] = v #Update local variables, in case of previously-specified parameters @@ -7855,7 +7984,7 @@

    Parameters

    for line in dataINStr: print('\t',line) - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params
    @@ -7923,7 +8052,9 @@

    Returns

    Dictionary containing entries with ppsds for each channel """ #First, divide up for batch or not + print(locals()) orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() ppsd_kwargs_sprit_defaults = ppsd_kwargs.copy() #Set defaults here that are different than obspy defaults @@ -7957,9 +8088,8 @@

    Returns

    inspect.getfullargspec(generate_ppsds).defaults)) defaultVDict['ppsd_kwargs'] = ppsd_kwargs for k, v in hvsr_data['processing_parameters']['generate_ppsds'].items(): - # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if not isinstance(v, (HVSRData, HVSRBatch)) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v remove_outliers = orig_args['remove_outliers'] @@ -8167,7 +8297,7 @@

    Returns

    hvsr_data['processing_parameters']['generate_ppsds'][key] = value hvsr_data['ProcessingStatus']['PPSDStatus'] = True - hvsr_data = _check_processing_status(hvsr_data) + hvsr_data = _check_processing_status(hvsr_data, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return hvsr_data
    @@ -8379,7 +8509,7 @@

    Returns

    defaultVDict = dict(zip(inspect.getfullargspec(get_report).args[1:], inspect.getfullargspec(get_report).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v report_format = orig_args['report_format'] @@ -8840,7 +8970,7 @@

    Returns

    -def input_params(datapath, site='HVSR Site', network='AM', station='RAC84', loc='00', channels=['EHZ', 'EHN', 'EHE'], acq_date='2023-10-30', starttime='00:00:00.00', endtime='23:59:59.999999', tzone='UTC', xcoord=-88.2290526, ycoord=40.1012122, elevation=755, input_crs='EPSG:4326', output_crs='EPSG:4326', elev_unit='feet', depth=0, instrument='Raspberry Shake', metapath=None, hvsr_band=[0.4, 40], peak_freq_range=[0.4, 40], processing_parameters={}, verbose=False) +def input_params(datapath, site='HVSR Site', network='AM', station='RAC84', loc='00', channels=['EHZ', 'EHN', 'EHE'], acq_date='2023-10-31', starttime='00:00:00.00', endtime='23:59:59.999999', tzone='UTC', xcoord=-88.2290526, ycoord=40.1012122, elevation=755, input_crs='EPSG:4326', output_crs='EPSG:4326', elev_unit='feet', depth=0, instrument='Raspberry Shake', metapath=None, hvsr_band=[0.4, 40], peak_freq_range=[0.4, 40], processing_parameters={}, verbose=False)

    Function for designating input parameters for reading in and processing data

    @@ -8998,6 +9128,7 @@

    Returns

    """ orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() #Reformat times if type(acq_date) is datetime.datetime: @@ -9140,7 +9271,7 @@

    Returns

    #Format everything nicely params = sprit_utils.make_it_classy(inputParamDict) params['ProcessingStatus']['InputParams'] = True - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params
    @@ -9642,6 +9773,7 @@

    Returns

    """ orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -9650,7 +9782,7 @@

    Returns

    defaultVDict = dict(zip(inspect.getfullargspec(process_hvsr).args[1:], inspect.getfullargspec(process_hvsr).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v method = orig_args['method'] @@ -9921,7 +10053,7 @@

    Returns

    hvsr_out['input_stream'] = hvsr_dataUpdate['input_params']['input_stream'] #input_stream hvsr_out = sprit_utils.make_it_classy(hvsr_out) hvsr_out['ProcessingStatus']['HVStatus'] = True - hvsr_out = _check_processing_status(hvsr_out) + hvsr_out = _check_processing_status(hvsr_out, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) hvsr_data['processing_parameters']['process_hvsr'] = {} for key, value in orig_args.items(): @@ -10027,6 +10159,7 @@

    Returns

    """ #Get intput paramaters orig_args = locals().copy() + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -10178,7 +10311,7 @@

    Returns

    output['processing_parameters']['remove_noise'][key] = value output['ProcessingStatus']['RemoveNoiseStatus'] = True - output = _check_processing_status(output) + output = _check_processing_status(output, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) if 'hvsr_df' in output.keys() or ('params' in output.keys() and 'hvsr_df' in output['params'].keys())or ('input_params' in output.keys() and 'hvsr_df' in output['input_params'].keys()): hvsrDF = output['hvsr_df'] @@ -10424,6 +10557,7 @@

    Raises

    RuntimeError If the data being processed is a single file, an error will be raised if generate_ppsds() does not work correctly. No errors are raised for remove_noise() errors (since that is an optional step) and the process_hvsr() step (since that is the last processing step) . """ + if 'hvsr_band' not in kwargs.keys(): kwargs['hvsr_band'] = inspect.signature(input_params).parameters['hvsr_band'].default if 'peak_freq_range' not in kwargs.keys(): @@ -10735,6 +10869,29 @@

    Classes

    """Wrapper of get_report()""" return self.get_report(**kwargs) + def export_settings(self, site_name=None, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True): + """Method to export settings from HVSRData object in HVSRBatch object. Simply calls sprit.export_settings() from specified HVSRData object in the HVSRBatch object. See sprit.export_settings() for more details. + + Parameters + ---------- + site_name : str, default=None + The name of the site whose settings should be exported. If None, will default to the first site, by default None. + export_settings_path : str, optional + Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default' + export_settings_type : str, {'all', 'instrument', 'processing'}, optional + They type of settings to save, by default 'all' + include_location : bool, optional + Whether to include the location information in the instrument settings, if that settings type is selected, by default False + verbose : bool, optional + Whether to print output (filepath and settings) to terminal, by default True + """ + #If no site name selected, use first site + if site_name is None: + site_name = self.sites[0] + + export_settings(hvsr_data=self[site_name], + export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose) + def __iter__(self): return iter(self._batch_dict.keys()) @@ -10804,6 +10961,52 @@

    Parameters

    export_data(hvsr_data=self, export_path=export_path, ext=ext) +
    +def export_settings(self, site_name=None, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True) +
    +
    +

    Method to export settings from HVSRData object in HVSRBatch object. Simply calls sprit.export_settings() from specified HVSRData object in the HVSRBatch object. See sprit.export_settings() for more details.

    +

    Parameters

    +
    +
    site_name : str, default=None
    +
    The name of the site whose settings should be exported. If None, will default to the first site, by default None.
    +
    export_settings_path : str, optional
    +
    Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
    +
    export_settings_type : str, {'all', 'instrument', 'processing'}, optional
    +
    They type of settings to save, by default 'all'
    +
    include_location : bool, optional
    +
    Whether to include the location information in the instrument settings, if that settings type is selected, by default False
    +
    verbose : bool, optional
    +
    Whether to print output (filepath and settings) to terminal, by default True
    +
    +
    + +Expand source code + +
    def export_settings(self, site_name=None, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True):
    +    """Method to export settings from HVSRData object in HVSRBatch object. Simply calls sprit.export_settings() from specified HVSRData object in the HVSRBatch object. See sprit.export_settings() for more details.
    +
    +    Parameters
    +    ----------
    +    site_name : str, default=None
    +        The name of the site whose settings should be exported. If None, will default to the first site, by default None.
    +    export_settings_path : str, optional
    +        Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
    +    export_settings_type : str, {'all', 'instrument', 'processing'}, optional
    +        They type of settings to save, by default 'all'
    +    include_location : bool, optional
    +        Whether to include the location information in the instrument settings, if that settings type is selected, by default False
    +    verbose : bool, optional
    +        Whether to print output (filepath and settings) to terminal, by default True
    +    """
    +    #If no site name selected, use first site
    +    if site_name is None:
    +        site_name = self.sites[0]
    +        
    +    export_settings(hvsr_data=self[site_name], 
    +                    export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose)
    +
    +
    def get_report(self, **kwargs)
    @@ -11085,6 +11288,23 @@

    Returns

    """Wrapper of get_report()""" report_return = get_report(self, **kwargs) return report_return + + def export_settings(self, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True): + """Method to export settings from HVSRData object. Simply calls sprit.export_settings() from the HVSRData object. See sprit.export_settings() for more details. + + Parameters + ---------- + export_settings_path : str, optional + Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default' + export_settings_type : str, {'all', 'instrument', 'processing'}, optional + They type of settings to save, by default 'all' + include_location : bool, optional + Whether to include the location information in the instrument settings, if that settings type is selected, by default False + verbose : bool, optional + Whether to print output (filepath and settings) to terminal, by default True + """ + export_settings(hvsr_data=self, + export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose) #ATTRIBUTES #params @@ -11359,6 +11579,44 @@

    Parameters

    export_data(hvsr_data=self, export_path=export_path, ext=ext) +
    +def export_settings(self, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True) +
    +
    +

    Method to export settings from HVSRData object. Simply calls sprit.export_settings() from the HVSRData object. See sprit.export_settings() for more details.

    +

    Parameters

    +
    +
    export_settings_path : str, optional
    +
    Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
    +
    export_settings_type : str, {'all', 'instrument', 'processing'}, optional
    +
    They type of settings to save, by default 'all'
    +
    include_location : bool, optional
    +
    Whether to include the location information in the instrument settings, if that settings type is selected, by default False
    +
    verbose : bool, optional
    +
    Whether to print output (filepath and settings) to terminal, by default True
    +
    +
    + +Expand source code + +
    def export_settings(self, export_settings_path='default', export_settings_type='all', include_location=False, verbose=True):
    +    """Method to export settings from HVSRData object. Simply calls sprit.export_settings() from the HVSRData object. See sprit.export_settings() for more details.
    +
    +    Parameters
    +    ----------
    +    export_settings_path : str, optional
    +        Filepath to output file. If left as 'default', will save as the default value in the resources directory. If that is not possible, will save to home directory, by default 'default'
    +    export_settings_type : str, {'all', 'instrument', 'processing'}, optional
    +        They type of settings to save, by default 'all'
    +    include_location : bool, optional
    +        Whether to include the location information in the instrument settings, if that settings type is selected, by default False
    +    verbose : bool, optional
    +        Whether to print output (filepath and settings) to terminal, by default True
    +    """
    +    export_settings(hvsr_data=self, 
    +                    export_settings_path=export_settings_path, export_settings_type=export_settings_type, include_location=include_location, verbose=verbose)
    +
    +
    def get_report(self, **kwargs)
    @@ -11529,6 +11787,7 @@

  • copy
  • export
  • +
  • export_settings
  • get_report
  • items
  • keys
  • @@ -11543,6 +11802,7 @@

    copy
  • datastream
  • export
  • +
  • export_settings
  • get_report
  • items
  • keys
  • diff --git a/pyproject.toml b/pyproject.toml index 7d62bff..8fb79f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "sprit" authors = [{name="Riley Balikian"}, {name="Hongyu Xaio"}] dynamic = ["readme"] license = {file = "LICENSE"} -version="0.1.50" +version="0.1.51" description = "A package for processing and analyzing HVSR (Horizontal to Vertical Spectral Ratio) data" keywords = ["HVSR", "seismic", "horizontal to vertical spectral ratio", "obspy", 'geology', 'geophysics', 'geotechnical'] requires-python = ">=3.9" diff --git a/setup.py b/setup.py index 3b9f9de..a2fe0ed 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ name="sprit", author= "Riley Balikian", author_email = "balikian@illinois.edu", - version="0.1.50", + version="0.1.51", package_data={'sprit': ['resources/*', 'resources/icon/*', 'resources/themes/*', 'resources/themes/forest-dark/*', 'resources/themes/forest-light/*', 'resources/sample_data/*','resources/settings/*']}, long_description_content_type="text/markdown", diff --git a/sprit/__pycache__/sprit_hvsr.cpython-310.pyc b/sprit/__pycache__/sprit_hvsr.cpython-310.pyc index 662c49ae3919cef70d510fa52ed2d533778ed388..9d02e71cfc463dd32f42960e61130595d7a46557 100644 GIT binary patch delta 49070 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 diff --git a/sprit/sprit_hvsr.py b/sprit/sprit_hvsr.py index 709af57..4c88339 100644 --- a/sprit/sprit_hvsr.py +++ b/sprit/sprit_hvsr.py @@ -592,6 +592,7 @@ def run(datapath, source='file', verbose=False, **kwargs): RuntimeError If the data being processed is a single file, an error will be raised if generate_ppsds() does not work correctly. No errors are raised for remove_noise() errors (since that is an optional step) and the process_hvsr() step (since that is the last processing step) . """ + if 'hvsr_band' not in kwargs.keys(): kwargs['hvsr_band'] = inspect.signature(input_params).parameters['hvsr_band'].default if 'peak_freq_range' not in kwargs.keys(): @@ -775,7 +776,7 @@ def check_peaks(hvsr_data, hvsr_band=[0.4, 40], peak_selection='max', peak_freq_ defaultVDict = dict(zip(inspect.getfullargspec(check_peaks).args[1:], inspect.getfullargspec(check_peaks).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v hvsr_band = orig_args['hvsr_band'] @@ -1129,10 +1130,12 @@ def export_settings(hvsr_data, export_settings_path='default', export_settings_t print(f"Processing settings exported to {procSetFPath}") print(f"{jsonString}") print() + #Reads in traces to obspy stream def fetch_data(params, source='file', trim_dir=None, export_format='mseed', detrend='spline', detrend_order=2, update_metadata=True, plot_input_stream=False, verbose=False, **kwargs): #Get intput paramaters orig_args = locals().copy() + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in params.keys(): @@ -1142,7 +1145,7 @@ def fetch_data(params, source='file', trim_dir=None, export_format='mseed', detr defaultVDict['kwargs'] = kwargs for k, v in params['processing_parameters']['fetch_data'].items(): # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if k!='params' and k in orig_args.keys() and orig_args[k]==defaultVDict[k]: orig_args[k] = v #Update local variables, in case of previously-specified parameters @@ -1554,7 +1557,7 @@ def fetch_data(params, source='file', trim_dir=None, export_format='mseed', detr for line in dataINStr: print('\t',line) - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params @@ -1589,7 +1592,9 @@ def generate_ppsds(hvsr_data, remove_outliers=True, outlier_std=3, verbose=False Dictionary containing entries with ppsds for each channel """ #First, divide up for batch or not + print(locals()) orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() ppsd_kwargs_sprit_defaults = ppsd_kwargs.copy() #Set defaults here that are different than obspy defaults @@ -1623,9 +1628,8 @@ def get_default_args(func): inspect.getfullargspec(generate_ppsds).defaults)) defaultVDict['ppsd_kwargs'] = ppsd_kwargs for k, v in hvsr_data['processing_parameters']['generate_ppsds'].items(): - # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if not isinstance(v, (HVSRData, HVSRBatch)) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v remove_outliers = orig_args['remove_outliers'] @@ -1833,7 +1837,7 @@ def convert_to_mpl_dates(obspyUTCDateTime): hvsr_data['processing_parameters']['generate_ppsds'][key] = value hvsr_data['ProcessingStatus']['PPSDStatus'] = True - hvsr_data = _check_processing_status(hvsr_data) + hvsr_data = _check_processing_status(hvsr_data, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return hvsr_data #Gets the metadata for Raspberry Shake, specifically for 3D v.7 @@ -1974,7 +1978,7 @@ def get_report(hvsr_results, report_format='print', plot_type='HVSR p ann C+ p a defaultVDict = dict(zip(inspect.getfullargspec(get_report).args[1:], inspect.getfullargspec(get_report).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v report_format = orig_args['report_format'] @@ -2438,6 +2442,7 @@ def input_params(datapath, """ orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() #Reformat times if type(acq_date) is datetime.datetime: @@ -2580,7 +2585,7 @@ def input_params(datapath, #Format everything nicely params = sprit_utils.make_it_classy(inputParamDict) params['ProcessingStatus']['InputParams'] = True - params = _check_processing_status(params) + params = _check_processing_status(params, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) return params #Main function for plotting results @@ -2934,6 +2939,7 @@ def process_hvsr(hvsr_data, method=3, smooth=True, freq_smooth='konno ohmachi', """ orig_args = locals().copy() #Get the initial arguments + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -2942,7 +2948,7 @@ def process_hvsr(hvsr_data, method=3, smooth=True, freq_smooth='konno ohmachi', defaultVDict = dict(zip(inspect.getfullargspec(process_hvsr).args[1:], inspect.getfullargspec(process_hvsr).defaults)) # Manual input to function overrides the imported parameter values - if k in orig_args.keys() and orig_args[k]==defaultVDict[k]: + if (not isinstance(v, (HVSRData, HVSRBatch))) and (k in orig_args.keys()) and (orig_args[k]==defaultVDict[k]): orig_args[k] = v method = orig_args['method'] @@ -3213,7 +3219,7 @@ def process_hvsr(hvsr_data, method=3, smooth=True, freq_smooth='konno ohmachi', hvsr_out['input_stream'] = hvsr_dataUpdate['input_params']['input_stream'] #input_stream hvsr_out = sprit_utils.make_it_classy(hvsr_out) hvsr_out['ProcessingStatus']['HVStatus'] = True - hvsr_out = _check_processing_status(hvsr_out) + hvsr_out = _check_processing_status(hvsr_out, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) hvsr_data['processing_parameters']['process_hvsr'] = {} for key, value in orig_args.items(): @@ -3269,6 +3275,7 @@ def remove_noise(hvsr_data, remove_method='auto', sat_percent=0.995, noise_perce """ #Get intput paramaters orig_args = locals().copy() + start_time = datetime.datetime.now() # Update with processing parameters specified previously in input_params, if applicable if 'processing_parameters' in hvsr_data.keys(): @@ -3420,7 +3427,7 @@ def remove_noise(hvsr_data, remove_method='auto', sat_percent=0.995, noise_perce output['processing_parameters']['remove_noise'][key] = value output['ProcessingStatus']['RemoveNoiseStatus'] = True - output = _check_processing_status(output) + output = _check_processing_status(output, start_time=start_time, func_name=inspect.stack()[0][3], verbose=verbose) if 'hvsr_df' in output.keys() or ('params' in output.keys() and 'hvsr_df' in output['params'].keys())or ('input_params' in output.keys() and 'hvsr_df' in output['input_params'].keys()): hvsrDF = output['hvsr_df'] @@ -3856,7 +3863,7 @@ def _process_hvsr_batch(**process_hvsr_kwargs): return hvsr_data #Special helper function that checks the processing status at each stage of processing to help determine if any processing steps were skipped -def _check_processing_status(hvsr_data): +def _check_processing_status(hvsr_data, start_time=datetime.datetime.now(), func_name='', verbose=False): """Internal function to check processing status, used primarily in the sprit.run() function to allow processing to continue if one site is bad. Parameters @@ -3889,7 +3896,11 @@ def _check_processing_status(hvsr_data): if isinstance(hvsr_data, HVSRData): hvsr_data = hvsr_interim[siteName] - return hvsr_data + + if verbose: + elapsed = (datetime.datetime.now()-start_time) + print(f"\t\t{func_name} completed in {str(elapsed)[:-3]}") + return hvsr_data #HELPER functions for fetch_data() and get_metadata() #Read in metadata .inv file, specifically for RaspShake