From c69219e684c36834569064bb26699ccfdf552eb7 Mon Sep 17 00:00:00 2001 From: WEgeophysics Date: Sat, 10 Dec 2022 22:32:10 +0800 Subject: [PATCH 1/4] ,new config --- pycsamt/__init__.py | 45 +++++------------------------ pycsamt/{utils => }/_csamtpylog.py | 0 pycsamt/_path.py | 38 ++++++++++++++++++++++++ pycsamt/core/avg.py | 4 +-- pycsamt/{utils => }/p.configlog.yml | 0 5 files changed, 47 insertions(+), 40 deletions(-) rename pycsamt/{utils => }/_csamtpylog.py (100%) create mode 100644 pycsamt/_path.py rename pycsamt/{utils => }/p.configlog.yml (100%) diff --git a/pycsamt/__init__.py b/pycsamt/__init__.py index 8373168..da4f6fe 100644 --- a/pycsamt/__init__.py +++ b/pycsamt/__init__.py @@ -39,7 +39,7 @@ import subprocess import logging import os -import tempfile + # setup the package if __package__ is None or __name__ == '__main__': @@ -49,15 +49,14 @@ # configure the logger from pycsamt.utils._csamtpylog import csamtpylog -try: - csamtpylog.load_configure(os.path.join( - os.path.abspath('.'),'pycsamt', 'utils', "p.configlog.yml")) -except: - csamtpylog.load_configure(os.path.join( - os.path.abspath('.'),'utils', "p.configlog.yml")) + + +conffile = os.path.join( + os.path.dirname(__file__), "p.configlog.yml") +csamtpylog.load_configure(conffile) # set loging Level -logging.getLogger('matplotlib').setLevel(logging.WARNING) +logging.getLogger('matplotlib.font_manager').setLevel(logging.WARNING) epsg_dict = { 28350: ['+proj=utm +zone=50 +south +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs', 50], @@ -199,37 +198,9 @@ def is_installing ( else : imtpy =True -# set path for demo and testing module -# Get the repository dir like https://github.com/WEgeophysics/pycsamt / -#C:github.com/WEgeophysics/pyCSAMT -PYCSAMT_ROOT = os.path.normpath( - os.path.abspath( - os.path.dirname( - os.path.dirname(__file__) - ) - ) -) - -EDI_DATA_DIR = os.path.normpath( - os.path.join(PYCSAMT_ROOT , 'data/edi')) -AVG_DATA_DIR = os.path.normpath( - os.path.join(PYCSAMT_ROOT , 'data/avg')) -J_DATA_DIR = os.path.normpath( - os.path.join(PYCSAMT_ROOT , 'data/j')) -DRILL_DATA_DIR = os.path.normpath( - os.path.join(PYCSAMT_ROOT , 'data/drill_examples_files')) -OCCAM2D_DATA_DIR = os.path.normpath( - os.path.join(PYCSAMT_ROOT , 'data/occam2d')) -STN_DATA_DIR = os.path.normpath( - os.path.join(PYCSAMT_ROOT , 'data/stn_profiles')) -CONFIG_DATA_DIR = os.path.normpath( - os.path.join(PYCSAMT_ROOT , 'data/_conffiles')) - -SYSTEM_TEMP_DIR = tempfile.gettempdir() -NEW_TEMP_DIR=tempfile.mkdtemp(prefix="pycsamt_tmpdir_") - +__all__=['tqdm', 'mtpy', 'numba', 'is_installing'] diff --git a/pycsamt/utils/_csamtpylog.py b/pycsamt/_csamtpylog.py similarity index 100% rename from pycsamt/utils/_csamtpylog.py rename to pycsamt/_csamtpylog.py diff --git a/pycsamt/_path.py b/pycsamt/_path.py new file mode 100644 index 0000000..32a19bc --- /dev/null +++ b/pycsamt/_path.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sat Dec 10 18:49:02 2022 + +@author: daniel +""" +import os +import tempfile +# set path for demo and testing module +# Get the repository dir like https://github.com/WEgeophysics/pycsamt / +#C:github.com/WEgeophysics/pyCSAMT +PYCSAMT_ROOT = os.path.normpath( + os.path.abspath( + os.path.dirname( + os.path.dirname(__file__) + ) + ) +) + +EDI_DATA_DIR = os.path.normpath( + os.path.join(PYCSAMT_ROOT , 'data/edi')) +AVG_DATA_DIR = os.path.normpath( + os.path.join(PYCSAMT_ROOT , 'data/avg')) +J_DATA_DIR = os.path.normpath( + os.path.join(PYCSAMT_ROOT , 'data/j')) +DRILL_DATA_DIR = os.path.normpath( + os.path.join(PYCSAMT_ROOT , 'data/drill_examples_files')) +OCCAM2D_DATA_DIR = os.path.normpath( + os.path.join(PYCSAMT_ROOT , 'data/occam2d')) +STN_DATA_DIR = os.path.normpath( + os.path.join(PYCSAMT_ROOT , 'data/stn_profiles')) +CONFIG_DATA_DIR = os.path.normpath( + os.path.join(PYCSAMT_ROOT , 'data/_conffiles')) + +SYSTEM_TEMP_DIR = tempfile.gettempdir() +NEW_TEMP_DIR=tempfile.mkdtemp(prefix="pycsamt_tmpdir_") + diff --git a/pycsamt/core/avg.py b/pycsamt/core/avg.py index d4f6ae7..d3f8daa 100644 --- a/pycsamt/core/avg.py +++ b/pycsamt/core/avg.py @@ -290,9 +290,7 @@ def _read_avg_file (self, data_fn=None ): raise CSex.pyCSAMTError_avg_file( 'The number provided doesnt not match Avgfile.') - - - + def avg_write_2_to_1 (self, data_fn=None , savepath =None ): """ Method to rewrite avg Astatic file (F2) to main file F1 . diff --git a/pycsamt/utils/p.configlog.yml b/pycsamt/p.configlog.yml similarity index 100% rename from pycsamt/utils/p.configlog.yml rename to pycsamt/p.configlog.yml From 5609f88d4aec11866e4a21a9f40c7372a9d25014 Mon Sep 17 00:00:00 2001 From: WEgeophysics Date: Sun, 11 Dec 2022 17:41:42 +0800 Subject: [PATCH 2/4] new config --- pycsamt/__init__.py | 2 +- pycsamt/bases.py | 2 +- pycsamt/core/avg.py | 2 +- pycsamt/core/cs.py | 2 +- pycsamt/core/edi.py | 2 +- pycsamt/core/j.py | 2 +- pycsamt/core/z.py | 2 +- pycsamt/geodrill/geocore.py | 2 +- pycsamt/geodrill/geodatabase.py | 2 +- pycsamt/geodrill/ml.py | 2 +- pycsamt/geodrill/requestmanager.py | 2 +- pycsamt/geodrill/strata.py | 2 +- pycsamt/geodrill/structural.py | 2 +- pycsamt/modeling/modem.py | 2 +- pycsamt/modeling/occam2d.py | 2 +- pycsamt/processing/corr.py | 2 +- pycsamt/processing/ctools.py | 10 ++-- pycsamt/site.py | 2 +- pycsamt/utils/__init__.py | 65 ++------------------------ pycsamt/utils/_p.py | 2 +- pycsamt/utils/_set_gdal.py | 73 ++++++++++++++++++++++++++++++ pycsamt/utils/avg_utils.py | 3 +- pycsamt/utils/decorator.py | 2 +- pycsamt/utils/func_utils.py | 2 +- pycsamt/utils/gis_tools.py | 2 +- pycsamt/utils/plotdecorator.py | 2 +- pycsamt/utils/zcalculator.py | 2 +- pycsamt/view/plot.py | 22 ++++----- 28 files changed, 117 insertions(+), 102 deletions(-) create mode 100644 pycsamt/utils/_set_gdal.py diff --git a/pycsamt/__init__.py b/pycsamt/__init__.py index da4f6fe..f0addce 100644 --- a/pycsamt/__init__.py +++ b/pycsamt/__init__.py @@ -48,7 +48,7 @@ __package__= 'pycsamt' # configure the logger -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog conffile = os.path.join( diff --git a/pycsamt/bases.py b/pycsamt/bases.py index 973f2a7..c9223d2 100644 --- a/pycsamt/bases.py +++ b/pycsamt/bases.py @@ -27,7 +27,7 @@ ) import pycsamt.utils.func_utils as FU -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog T=TypeVar('T') diff --git a/pycsamt/core/avg.py b/pycsamt/core/avg.py index d3f8daa..0a14be6 100644 --- a/pycsamt/core/avg.py +++ b/pycsamt/core/avg.py @@ -20,11 +20,11 @@ import pandas as pd from datetime import (datetime, timezone) +from pycsamt._csamtpylog import csamtpylog from pycsamt.utils import _p as infOS from pycsamt.utils.decorator import deprecated from pycsamt.site import Site from pycsamt.utils import zcalculator as Zcc -from pycsamt.utils._csamtpylog import csamtpylog from pycsamt.utils import avg_utils as cfunc from pycsamt.utils import func_utils as func from pycsamt.utils import exceptions as CSex diff --git a/pycsamt/core/cs.py b/pycsamt/core/cs.py index 36a3cda..195e2c0 100644 --- a/pycsamt/core/cs.py +++ b/pycsamt/core/cs.py @@ -23,7 +23,7 @@ from pycsamt.site import (Site, Location, Profile) from pycsamt.utils import exceptions as CSex from pycsamt.utils import func_utils as func -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog try : from pycsamt.__init__ import itqdm diff --git a/pycsamt/core/edi.py b/pycsamt/core/edi.py index 1dc306d..c835f84 100644 --- a/pycsamt/core/edi.py +++ b/pycsamt/core/edi.py @@ -36,7 +36,7 @@ from pycsamt.utils. _p import _sensitive as SB from pycsamt.utils import gis_tools as gis from pycsamt.utils import exceptions as CSex -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog if imtpy: import mtpy diff --git a/pycsamt/core/j.py b/pycsamt/core/j.py index 046c997..cd62038 100644 --- a/pycsamt/core/j.py +++ b/pycsamt/core/j.py @@ -26,7 +26,7 @@ from pycsamt.utils.decorator import deprecated import pycsamt.utils.func_utils as func from pycsamt.utils import exceptions as CSex -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger = csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/core/z.py b/pycsamt/core/z.py index b76f647..bebbfe8 100644 --- a/pycsamt/core/z.py +++ b/pycsamt/core/z.py @@ -26,7 +26,7 @@ import pycsamt.utils.mtcalculator as MTcc import pycsamt.utils.exceptions as MTex -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger = csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/geodrill/geocore.py b/pycsamt/geodrill/geocore.py index 5ab11ab..a820407 100644 --- a/pycsamt/geodrill/geocore.py +++ b/pycsamt/geodrill/geocore.py @@ -36,7 +36,7 @@ from pycsamt.geodrill.geodatabase import GeoDataBase try : - from pycsamt.utils._csamtpylog import csamtpylog + from pycsamt._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) except : pass diff --git a/pycsamt/geodrill/geodatabase.py b/pycsamt/geodrill/geodatabase.py index 74b547a..da816c3 100644 --- a/pycsamt/geodrill/geodatabase.py +++ b/pycsamt/geodrill/geodatabase.py @@ -25,7 +25,7 @@ from pycsamt.geodrill._dictapp import Glob from pycsamt.geodrill.requestmanager import ManageDB from pycsamt.utils.decorator import deprecated_to -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog #set LogInfos try : _logger=csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/geodrill/ml.py b/pycsamt/geodrill/ml.py index d03eafa..520c996 100644 --- a/pycsamt/geodrill/ml.py +++ b/pycsamt/geodrill/ml.py @@ -8,7 +8,7 @@ from pycsamt.utils.ml_utils import neuron from pycsamt.geodrill.requestmanager import ManageDB -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger =csamtpylog().get_csamtpy_logger(__name__) diff --git a/pycsamt/geodrill/requestmanager.py b/pycsamt/geodrill/requestmanager.py index 4c93d1e..23659b8 100644 --- a/pycsamt/geodrill/requestmanager.py +++ b/pycsamt/geodrill/requestmanager.py @@ -15,7 +15,7 @@ import sqlite3 as sq3 try : - from pycsamt.utils._csamtpylog import csamtpylog + from pycsamt._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) except : pass diff --git a/pycsamt/geodrill/strata.py b/pycsamt/geodrill/strata.py index 0630f16..6725bbd 100644 --- a/pycsamt/geodrill/strata.py +++ b/pycsamt/geodrill/strata.py @@ -11,7 +11,7 @@ """ from pycsamt.utils.geo_utils import mapping_stratum as setdico try : - from pycsamt.utils._csamtpylog import csamtpylog + from pycsamt._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) except : pass diff --git a/pycsamt/geodrill/structural.py b/pycsamt/geodrill/structural.py index e19933f..47e7633 100644 --- a/pycsamt/geodrill/structural.py +++ b/pycsamt/geodrill/structural.py @@ -15,7 +15,7 @@ from pycsamt.utils.geo_utils import mapping_stratum as strato from pycsamt.utils import exceptions as CSex -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/modeling/modem.py b/pycsamt/modeling/modem.py index 02d6230..3eba63b 100644 --- a/pycsamt/modeling/modem.py +++ b/pycsamt/modeling/modem.py @@ -37,7 +37,7 @@ imtpy, is_installing, ) -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog from pycsamt.core.edi import Edi_collection _logger =csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/modeling/occam2d.py b/pycsamt/modeling/occam2d.py index 888292d..77c6ce7 100644 --- a/pycsamt/modeling/occam2d.py +++ b/pycsamt/modeling/occam2d.py @@ -28,7 +28,7 @@ from pycsamt.utils import plot_utils as punc from pycsamt.utils import plotdecorator as mdeco from pycsamt.utils._p import _sensitive as SB -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger =csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/processing/corr.py b/pycsamt/processing/corr.py index 848099e..90ab9a6 100644 --- a/pycsamt/processing/corr.py +++ b/pycsamt/processing/corr.py @@ -21,10 +21,10 @@ from pycsamt.core.cs import CSAMT from pycsamt.core import z as CSAMTz from pycsamt.utils import zcalculator as Zcc -from pycsamt.utils._csamtpylog import csamtpylog from pycsamt.utils.decorator import deprecated from pycsamt.utils import exceptions as CSex from pycsamt.utils import func_utils as func +from pycsamt._csamtpylog import csamtpylog try : from pycsamt.__init__ import itqdm if itqdm : diff --git a/pycsamt/processing/ctools.py b/pycsamt/processing/ctools.py index 95498af..335333c 100644 --- a/pycsamt/processing/ctools.py +++ b/pycsamt/processing/ctools.py @@ -188,7 +188,7 @@ def get_reference_frequency (ediObjs= None, arr2d=None, freqs=None, the reference frequency at the clean data in Hz Examples --------- - >>> from pycsamt.processimg import make2d, get_reference_frequency + >>> from pycsamt.processing import make2d, get_reference_frequency >>> edipath ='data/3edis' >>> ediObjs = get_ediObjs (edipath) >>> cfs= get_full_frequency(ediObjs) @@ -388,7 +388,7 @@ def make2d (ediObjs, out = 'resxy', *, kind = 'complex' , **kws ): [ nan, nan, 44.04498791]]) >>> phyx.shape ... (55, 3) - >>> # get the real number of the yy componet of tensor z + >>> # get the real number of the yy component of tensor z >>> zyy_r = make2d (ediObjs, 'zyx', kind ='real') ... array([[ 4165.6 , 8665.64 , 5285.47 ], [ 7072.81 , 11663.1 , 6900.33 ], @@ -1062,9 +1062,9 @@ def moving_average (y, *, window_size = 3 , method ='sma', References ---------- - .. * [1] https://en.wikipedia.org/wiki/Moving_average - .. * [2] https://www.sciencedirect.com/topics/engineering/hanning-window - .. * [3] https://stackoverflow.com/questions/12816011/weighted-moving-average-with-numpy-convolve + .. [1] https://en.wikipedia.org/wiki/Moving_average + .. [2] https://www.sciencedirect.com/topics/engineering/hanning-window + .. [3] https://stackoverflow.com/questions/12816011/weighted-moving-average-with-numpy-convolve """ y = np.array(y) diff --git a/pycsamt/site.py b/pycsamt/site.py index 7ced73a..a5924ee 100644 --- a/pycsamt/site.py +++ b/pycsamt/site.py @@ -9,11 +9,11 @@ import warnings import numpy as np +from pycsamt._csamtpylog import csamtpylog from pycsamt.utils import _p as inFO from pycsamt.utils import exceptions as CSex from pycsamt.utils import gis_tools as gis from pycsamt.utils import avg_utils as cfunc -from pycsamt.utils._csamtpylog import csamtpylog from pycsamt.utils import func_utils as func _logger = csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/utils/__init__.py b/pycsamt/utils/__init__.py index 047581f..68cb501 100644 --- a/pycsamt/utils/__init__.py +++ b/pycsamt/utils/__init__.py @@ -1,65 +1,6 @@ -import os -import re -import sys -import numpy as np -from .decorator import gdal_data_check - -# Check for gdal availability at module level so we don't have to -# do this every time a function in gis_tools is being called. - -HAS_GDAL = gdal_data_check(None)._gdal_data_found - -if (not HAS_GDAL): - try: - import pyproj - except ImportError: - raise RuntimeError("Either GDAL or PyProj must be installed") - # end try -# end if - -EPSG_DICT = {} -try: - import pyproj - - epsgfn = os.path.join(pyproj.pyproj_datadir, 'epsg') - - f = open(epsgfn, 'r') - lines = f.readlines() - - for line in lines: - if ('#' in line): continue - - epsg_code_val = re.compile('<(\d+)>').findall(line) - - # print( "epsg_code_val", epsg_code_val) - - if epsg_code_val is not None and len(epsg_code_val) > 0 and epsg_code_val[0].isdigit(): - epsg_code = int(epsg_code_val[0]) - epsg_string = re.compile('>(.*)<').findall(line)[0].strip() - - EPSG_DICT[epsg_code] = epsg_string - else: - pass #print("epsg_code_val NOT found for this line ", line, epsg_code_val) - #end for -except Exception as e: - # Failed to load EPSG codes and corresponding proj4 projections strings from pyproj. - # Since version 1.9.5 the epsg file stored in pyproj_datadir has been removed and - # replaced by 'proj.db', which is stored in a different folder. - # Since the underlying proj4 projection strings haven't changed, we simply load a - # local copy of these mappings to ensure backward compatibility. - - path = os.path.dirname(os.path.abspath(__file__)) - try: - epsg_dict_fn = os.path.join(path, 'epsg.npy') - EPSG_DICT = np.load(epsg_dict_fn, allow_pickle=True).item() - except: - sys.stdout.write(f'Do not find `epsg.npy` in {epsg_dict_fn}') - -# import necessary utils - -# from .func_utils import ( -# scale_position, -2 +from pycsamt.utils._set_gdal import set_GDAL +HAS_GDAL , EPSG_DICT = set_GDAL() +__all__=['HAS_GDAL', 'EPSG_DICT'] \ No newline at end of file diff --git a/pycsamt/utils/_p.py b/pycsamt/utils/_p.py index a03f089..e4321a6 100644 --- a/pycsamt/utils/_p.py +++ b/pycsamt/utils/_p.py @@ -14,7 +14,7 @@ import warnings import numpy as np -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog from pycsamt.utils.exceptions import pyCSAMTError_file_handling class notion : diff --git a/pycsamt/utils/_set_gdal.py b/pycsamt/utils/_set_gdal.py new file mode 100644 index 0000000..f225d32 --- /dev/null +++ b/pycsamt/utils/_set_gdal.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Dec 11 15:02:44 2022 + +@author: daniel +""" +import os +import re +import sys +import numpy as np + +from pycsamt.utils.decorator import gdal_data_check + +def set_GDAL () : + # Check for gdal availability at module level so we don't have to + # do this every time a function in gis_tools is being called. + HAS_GDAL = gdal_data_check(None)._gdal_data_found + + is_pyproj = False + if (not HAS_GDAL): + try: + import pyproj + except ImportError: + raise RuntimeError("Either GDAL or PyProj must be installed") + else: + is_pyproj =True + + EPSG_DICT = {} + + try: + if is_pyproj: + epsgfn = os.path.join(pyproj.pyproj_datadir, 'epsg') + + f = open(epsgfn, 'r') + lines = f.readlines() + + for line in lines: + if ('#' in line): continue + epsg_code_val = re.compile('<(\d+)>').findall(line) + + # print( "epsg_code_val", epsg_code_val) + + if epsg_code_val is not None and len(epsg_code_val) > 0 and epsg_code_val[0].isdigit(): + epsg_code = int(epsg_code_val[0]) + epsg_string = re.compile('>(.*)<').findall(line)[0].strip() + + EPSG_DICT[epsg_code] = epsg_string + else: + pass + #print("epsg_code_val NOT found for this line ", line, epsg_code_val) + #end for + else : + # force set espsg.locally + raise + + except: + # Failed to load EPSG codes and corresponding proj4 projections strings from pyproj. + # Since version 1.9.5 the epsg file stored in pyproj_datadir has been removed and + # replaced by 'proj.db', which is stored in a different folder. + # Since the underlying proj4 projection strings haven't changed, we simply load a + # local copy of these mappings to ensure backward compatibility. + + path = os.path.dirname(os.path.abspath(__file__)) + try: + epsg_dict_fn = os.path.join(path, 'epsg.npy') + EPSG_DICT = np.load(epsg_dict_fn, allow_pickle=True).item() + except: + sys.stdout.write(f'Do not find `epsg.npy` in {epsg_dict_fn}') + + + return HAS_GDAL , EPSG_DICT + diff --git a/pycsamt/utils/avg_utils.py b/pycsamt/utils/avg_utils.py index db20aaf..47483d9 100644 --- a/pycsamt/utils/avg_utils.py +++ b/pycsamt/utils/avg_utils.py @@ -35,9 +35,10 @@ import numpy as np import pandas as pd +from pycsamt._csamtpylog import csamtpylog + from pycsamt.utils import gis_tools as gis from pycsamt.utils import exceptions as CSex -from pycsamt.utils._csamtpylog import csamtpylog from pycsamt.utils import func_utils as func from scipy.interpolate import interp1d _logger=csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/utils/decorator.py b/pycsamt/utils/decorator.py index 258bb4b..d156f91 100644 --- a/pycsamt/utils/decorator.py +++ b/pycsamt/utils/decorator.py @@ -2,7 +2,7 @@ import inspect import os import warnings -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog class donothing : """ Decorator to do nothing. Just return the func as it was. diff --git a/pycsamt/utils/func_utils.py b/pycsamt/utils/func_utils.py index bbe7fca..7753a22 100644 --- a/pycsamt/utils/func_utils.py +++ b/pycsamt/utils/func_utils.py @@ -74,7 +74,7 @@ import pycsamt.utils.gis_tools as gis import pycsamt.utils.exceptions as CSex from pycsamt.utils.decorator import deprecated -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger = csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/utils/gis_tools.py b/pycsamt/utils/gis_tools.py index 9d28ffa..509e3e2 100644 --- a/pycsamt/utils/gis_tools.py +++ b/pycsamt/utils/gis_tools.py @@ -28,7 +28,7 @@ import numpy as np from pycsamt.utils.decorator import deprecated -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog from pycsamt.utils.__init__ import ( HAS_GDAL, diff --git a/pycsamt/utils/plotdecorator.py b/pycsamt/utils/plotdecorator.py index 24d87f7..d62eb0e 100644 --- a/pycsamt/utils/plotdecorator.py +++ b/pycsamt/utils/plotdecorator.py @@ -31,7 +31,7 @@ # from matplotlib.ticker import MultipleLocator, NullLocator from pycsamt.utils import plot_utils as mplotus -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/utils/zcalculator.py b/pycsamt/utils/zcalculator.py index d2e42bc..5fc5945 100644 --- a/pycsamt/utils/zcalculator.py +++ b/pycsamt/utils/zcalculator.py @@ -15,7 +15,7 @@ from pycsamt.utils import exceptions as CSex from pycsamt.utils import func_utils as func from pycsamt.utils.decorator import (deprecated, deprecated_to) -from pycsamt.utils._csamtpylog import csamtpylog +from pycsamt._csamtpylog import csamtpylog _logger =csamtpylog.get_csamtpy_logger(__name__) try: diff --git a/pycsamt/view/plot.py b/pycsamt/view/plot.py index def4a3a..15aad29 100644 --- a/pycsamt/view/plot.py +++ b/pycsamt/view/plot.py @@ -6,11 +6,11 @@ Module View ============ -Some templates for plotting purposes. It gives a quick alternative for uses to -save their time for writting their own plot scripts. However to have full +Some templates for plotting purposes. It gives a quick alternative to +save time writting their own plot scripts. However to have full control of the plot, it is recommended to write your own plot scripts. -Note that the package can not handle all the plots possibilty that offers the +Note that the package can not handle all the possible plots that offers the software. In the future, many plots should be removed and replaced by some most efficient ones. @@ -42,8 +42,8 @@ from pycsamt.geodrill import geocore as geoD from pycsamt.utils.plotdecorator import geoplot1d from pycsamt.utils._p import suit -from pycsamt.utils._csamtpylog import csamtpylog from pycsamt.processing import Processing +from pycsamt._csamtpylog import csamtpylog try : from pycsamt.__init__ import imtpy @@ -656,7 +656,7 @@ def plottensors (self, resargs , phsargs , res_err=None, phs_err =None, **errorbar_kw ) - jj +=3 # to jup to next value if + jj +=3 # to jump to next value if if phs_err is not None: phs_err = func._assert_all_types(phs_err, list, tuple, np.ndarray) @@ -893,13 +893,13 @@ def plot_topo_sep_azim(self,fn = None , profile_fn=None , xtick_label_size= kwargs.pop('xtick_labelsize', 12), ytick_label_size= kwargs.pop('ytick_labelsize', 12) ) - lw ,ls, color, alpha =kws['lw'],kws ['ls'], kws['color'], kws['alpha'] + # lw ,ls, color, alpha =kws['lw'],kws ['ls'], kws['color'], kws['alpha'] + lw ,ls, color=kws['lw'],kws ['ls'], kws['color'] - x_ticklabel_rotation, x_ticks_labelsize ,\ - y_ticks_labelsize = kws['xtick_label_rotation'], \ - kws['xtick_label_size'],\ - kws['ytick_label_size'] - + x_ticklabel_rotation=kws['xtick_label_rotation'] + x_ticks_labelsize= kws['xtick_label_size'] + #y_ticks_labelsize=kws['ytick_label_size'] + marker_style, markerfacecolor, markeredgecolor =\ kws['marker'], kws['markerfacecolor'], kws['markeredgecolor'] From ee3ca9907ba29f494d735e54316fd723ddecbbc1 Mon Sep 17 00:00:00 2001 From: Daniel03 <59920007+WEgeophysics@users.noreply.github.com> Date: Mon, 12 Dec 2022 00:32:24 +0800 Subject: [PATCH 3/4] Delete examplefitcurves.png --- examples/examplefitcurves.png | Bin 167285 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/examplefitcurves.png diff --git a/examples/examplefitcurves.png b/examples/examplefitcurves.png deleted file mode 100644 index 62a0ebd694fffa4db812f9f3f9d97174b429a8c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167285 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

hi4G-TIZg>+>-*3KLvfr3ss%bPPQZTZE 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 Date: Mon, 12 Dec 2022 22:52:16 +0800 Subject: [PATCH 4/4] change exceptions --- pycsamt/core/avg.py | 291 ++++++++++++++++++-------------- pycsamt/core/cs.py | 24 ++- pycsamt/core/edi.py | 50 +++--- pycsamt/core/j.py | 52 +++--- pycsamt/core/z.py | 23 +-- pycsamt/geodrill/geocore.py | 61 +++---- pycsamt/geodrill/geodatabase.py | 42 ++--- pycsamt/geodrill/structural.py | 10 +- pycsamt/modeling/modem.py | 32 ++-- pycsamt/modeling/occam2d.py | 46 ++--- pycsamt/processing/corr.py | 41 +++-- pycsamt/site.py | 82 ++++----- pycsamt/utils/decorator.py | 34 ++-- pycsamt/utils/exceptions.py | 100 +++++------ pycsamt/utils/geo_utils.py | 8 +- pycsamt/utils/gis_tools.py | 16 +- pycsamt/utils/mtcalculator.py | 38 ++--- pycsamt/utils/plot_utils.py | 77 +++++---- pycsamt/utils/zcalculator.py | 101 +++++------ pycsamt/view/__init__.py | 10 +- pycsamt/view/plot.py | 41 +++-- 21 files changed, 618 insertions(+), 561 deletions(-) diff --git a/pycsamt/core/avg.py b/pycsamt/core/avg.py index 0a14be6..3e1a4b4 100644 --- a/pycsamt/core/avg.py +++ b/pycsamt/core/avg.py @@ -25,10 +25,21 @@ from pycsamt.utils.decorator import deprecated from pycsamt.site import Site from pycsamt.utils import zcalculator as Zcc -from pycsamt.utils import avg_utils as cfunc +from pycsamt.utils import avg_utils as cfunc from pycsamt.utils import func_utils as func -from pycsamt.utils import exceptions as CSex - +#from pycsamt.utils import exceptions as CSex +from pycsamt.exceptions import ( + AVGError, + ConfigFileError, + FrequencyError, + StationError, + HeaderError, + ZError, + ProcessingError, + ResistivityError, + PhaseError, + + ) _logger=csamtpylog.get_csamtpy_logger(__name__) @@ -75,31 +86,55 @@ def __init__(self, data_fn=None , **kwargs): self.profile_fn =kwargs.pop('profile_fn', None) - self._f1_labels , self._f2_labels=['skp','Station', - ' Freq','Comp', - ' Amps','Emag', - 'Ephz','Hmag', - 'Hphz','Resistivity', - 'Phase','%Emag', - 'sEphz','%Hmag', - 'sHphz','%Rho', - 'sPhz'],['skp', - 'Freq','Tx.Amp', - 'E.mag','E.phz', - 'B.mag','B.phz', - 'Z.mag','Z.phz', - 'ARes.mag','SRes', - 'E.wgt', 'B.wgt', - 'E.%err','E.perr', - 'B.%err','B.perr', - 'Z.%err','Z.perr', - 'ARes.%err'] + self._f1_labels=[ + 'skp', + 'Station', + ' Freq', + 'Comp', + ' Amps', + 'Emag', + 'Ephz', + 'Hmag', + 'Hphz', + 'Resistivity', + 'Phase', + '%Emag', + 'sEphz', + '%Hmag', + 'sHphz', + '%Rho', + 'sPhz' + ] + self._f2_labels=[ + 'skp', + 'Freq', + 'Tx.Amp', + 'E.mag', + 'E.phz', + 'B.mag', + 'B.phz', + 'Z.mag', + 'Z.phz', + 'ARes.mag', + 'SRes', + 'E.wgt', + 'B.wgt', + 'E.%err', + 'E.perr', + 'B.%err', + 'B.perr', + 'Z.%err', + 'Z.perr', + 'ARes.%err' + ] self._zmark='$' - self.EM_zonge2j = {'ExHx':'RXX', - 'ExHy':'RXY', - 'EyHx':'RYX', - 'EyHy':'RYY'} + self.EM_zonge2j = { + 'ExHx':'RXX', + 'ExHy':'RXY', + 'EyHx':'RYX', + 'EyHy':'RYY' + } for key in list(kwargs.keys()): self.__setattr__(key, kwargs[key]) @@ -122,13 +157,13 @@ def _read_avg_file (self, data_fn=None ): if data_fn is not None : self.data_fn =data_fn if self.data_fn is None : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( 'No AVG file found.Please check ' 'your pathfile :<{0}>'.format(os.getcwd())) # print(self.data_fn) if self.data_fn is not None : if os.path.isfile(self.data_fn) is False : - raise CSex.pyCSAMTError_AVG('could not find %s', self.data_fn) + raise AVGError('could not find %s', self.data_fn) self._logging.info('reading and checking the AVG file %s', self.data_fn) #------------------------------------------------------------- @@ -138,7 +173,7 @@ def _read_avg_file (self, data_fn=None ): self.data_fn.endswith('avg'.upper())) == False : warnings.warn( ' :{0}'.format(infOS.notion.AVG)) - raise CSex.pyCSAMTError_AVG( + raise AVGError( 'file provided is not an AVGfile.Please check your file!') @@ -264,7 +299,7 @@ def _read_avg_file (self, data_fn=None ): 'It seems something wrong while reading' ' Astatic file.', category=DeprecationWarning, filename= func.__name__, lineno=1) - raise CSex.pyCSAMTError_avg_file( + raise AVGError( 'File might be corrupted. number of frequency must' ' be the same along along all stations. ') @@ -287,7 +322,7 @@ def _read_avg_file (self, data_fn=None ): # we set skip flag to '2' as good quality data self.Skip_flag.setandget_skip_flag(skip_flag='2' ) else : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( 'The number provided doesnt not match Avgfile.') @@ -304,12 +339,12 @@ def avg_write_2_to_1 (self, data_fn=None , savepath =None ): """ if data_fn is not None : self.data_fn =data_fn if self.data_fn is None : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( 'No file detected. check your right path .') if self.data_fn is not None : if os.path.isfile (self.data_fn) is not True : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( 'No file detected. Please check your right path. ') #-- check whether the file is astatic file : @@ -320,13 +355,13 @@ def avg_write_2_to_1 (self, data_fn=None , savepath =None ): self.checker = cfunc._validate_avg_file(avg_data_file) if self.checker == 1 : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( 'Input Avgfile < {0}> is not Zonge ASTATIC file. ' 'Please put the right file !'.format( os.path.basename(self.data_fn))) else : - raise CSex.pyCSAMTError_AVG( + raise AVGError( 'No avg file detected ! Please check your file !') write_lines =['\\'] @@ -530,14 +565,14 @@ def avg_to_jfile (self, avg_data_fn=None , profile_fn =None, if avg_data_fn is not None : self.data_fn = avg_data_fn if self.data_fn is None : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( "Could not find any path to read ." "Please provide your right AVG file.") if profile_fn is not None : self.profile_fn = profile_fn if self.profile_fn is None : - raise CSex.pyCSAMTError_station( + raise StationError( 'Need absolutely station file.' ' please provide your ".STN" file.') @@ -804,7 +839,7 @@ def avg_to_edifile (self, data_fn =None , profile_fn =None , if data_fn is not None : self.data_fn = data_fn if self.data_fn is None : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( "Could not find any path to read ." "Please provide your right AVG file.") @@ -963,7 +998,7 @@ def avg_to_edifile (self, data_fn =None , profile_fn =None , ' Please provided the right filters' ' for computing.'% apply_filter) - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'Filters provided is not acceptable.' ' Recognized filters are "TMA","AMA" AND "FLMA"') @@ -1386,7 +1421,7 @@ def set_survey_annotations_infos (self, survey_annotations_data =None): self.zon_serv_annotations = survey_annotations_data if self.zon_serv_annotations is None : - CSex.pyCSAMTError_inputarguments( + raise ValueError( 'No survey_annotations informations found!') @@ -1396,7 +1431,7 @@ def set_survey_annotations_infos (self, survey_annotations_data =None): with open (self.zon_serv_annotations , 'r', encoding ='utf8') as fid : self.zon_serv_annotations =fid.readlines() - except : raise CSex.pyCSAMTError_Header( + except : raise HeaderError( "Please check your annotation data ! " "Must be be a list or file ") @@ -1533,14 +1568,14 @@ def set_survey_configuration_infos (self, survey_config_data =None): if survey_config_data is not None : self.zon_surv_config = survey_config_data if self.zon_surv_config is None : - raise CSex.pyCSAMTError_Header("No Survey configuration data found." + raise HeaderError("No Survey configuration data found." " Please check your configuration file.") if type(self.zon_surv_config) is not list: try : if os.path.isfile(self.zon_surv_config) is True : with open (self.zon_surv_config, 'r', encoding ='utf8') as fid : self.zon_surv_config=fid.readlines() - except : raise CSex.pyCSAMTError_Header( + except : raise HeaderError( "your configuration data must be either a list or a file.") # print(self.zon_surv_config) @@ -1679,7 +1714,7 @@ def set_transmitter_properties(self, Tx_data =None): """ if Tx_data is None : - CSex.pyCSAMTError_inputarguments('No Tx-proprerties found !') + TypeError('No Tx-proprerties found !') if Tx_data is not None : for tx_infos in Tx_data : @@ -1786,14 +1821,14 @@ def set_receiver_properties(self, Rx_data =None ): self.rx_data = Rx_data if self.rx_data is None : - CSex.pyCSAMTError_inputarguments('No Zonge Rx-proprerties found !') + raise ValueError('No Zonge Rx-proprerties found !') if type(self.rx_data) is not list : try : if os.path.isfile(self.rx_data) ==True : with open(self.rx_data, 'r', encoding='utf-8') as frx: self.rx_data =frx.readlines() - except : raise CSex.pyCSAMTError_Header( + except : raise HeaderError( 'Argument provided for rx_data are wrong !' ' Must be a list of file.') @@ -1886,7 +1921,7 @@ def setandget_skip_flag (self, skip_flag=None): if skip_flag is not None : if skip_flag not in list(self.skip_flag_dict.keys()): - raise CSex.pyCSAMTError_config_file( + raise ConfigFileError( 'Wrong Input ! skip flag must be' ' among : {0}'.format(list(self.skip_flag_dict.keys()))) self.skip_flag = str(skip_flag) @@ -1969,7 +2004,7 @@ def set_harware_infos (self, zonge_hardw_infos =None ): if zonge_hardw_infos is not None : self.zonge_hardw_infos=zonge_hardw_infos if self.zonge_hardw_infos is None : - raise CSex.pyCSAMTError_Header( + raise HeaderError( 'No informations from hardware found !') if type (self.zonge_hardw_infos) is not list : @@ -1977,7 +2012,7 @@ def set_harware_infos (self, zonge_hardw_infos =None ): with open(self.zonge_hardw_infos, 'r', encoding='utf8') as fzh : self.zonge_hardw_infos= fzh.readlines() except : - raise CSex.pyCSAMTError_Header( + raise HeaderError( "Harware - infos must be either a list or file !") for infos in self.zonge_hardw_infos : @@ -2131,12 +2166,12 @@ def _avg_set_components_from_data (self, data_array=None ,data_type=None, self._data_array =data_array if self._data_array is None : - raise CSex.pyCSAMTError_AvgData('No Data from avgfile to read.') + raise AVGError('No Data from avgfile to read.') if data_type is not None : self._f =data_type if self._f not in [1,2]: - raise CSex.pyCSAMTError_AvgData(' May check your avg Datafile.') + raise AVGError(' May check your avg Datafile.') number_of_freq_array , rep = \ @@ -2381,7 +2416,7 @@ def _set_station_(self,station_data_array =None, rename_station =None , [np.float(kk) for kk in station_data_array ]) if self.station_data_array is None : - raise CSex.pyCSAMTError_station('No stations data to read .') + raise StationError('No stations data to read .') num_station_counts , repsta = np.unique (self.station_data_array, @@ -2389,7 +2424,7 @@ def _set_station_(self,station_data_array =None, rename_station =None , #---> check if stations_data provided are each the same length. if np.all(repsta, axis=0) != True : - raise CSex.pyCSAMTError_station( + raise StationError( 'Stations provided must be the same length of reccurency.') if repsta[0] ==1 : self.value = self.station_data_array @@ -2408,7 +2443,7 @@ def _set_station_(self,station_data_array =None, rename_station =None , self._logging.warn( 'Station units provided is incorect.Try "km" or "ft." Default is "m."') - raise CSex.pyCSAMTError_station( + raise StationError( 'Unit provided <{0}> doesnt not match correct units.' 'acceptable units are : "m", "km" or "ft"'.format(self.unit)) @@ -2434,7 +2469,7 @@ def _set_station_(self,station_data_array =None, rename_station =None , if type (self.rename_station) is list : self.rename_station=np.array(self.rename_station) if self.value.size != self.rename_station.size : - raise CSex.pyCSAMTError_station( + raise StationError( 'Stations rename array must have the same length as' ' the aray_data provided. lenght or stations_data is :<{0}>' ' '.format(self.value.size)) @@ -2548,7 +2583,7 @@ def _set_frequency (self, freq_array =None, normalize_freq_betw = None ): self._freq_array =freq_array if self._freq_array is None : - raise CSex.pyCSAMTError_frequency('No Frequency Data found.') + raise FrequencyError('No Frequency Data found.') self._freq_array =np.array([np.float(kk) for kk in self._freq_array]) @@ -2560,7 +2595,7 @@ def _set_frequency (self, freq_array =None, normalize_freq_betw = None ): self.numfreq = vacount_freq.size if np.all(freq_repeat, axis =0) != True : - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( 'Problem occured when reading frequency data.' 'All frequency on Avgfile Must be the same length. ') # self.value =vacount_freq @@ -2620,7 +2655,7 @@ def normalize_frequency (self , normalize_freq_betw=None ): self._logging.warn ( "Input interpolated arguments are wrong! arguments " "type must a list of integers. ") - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Input value of frequency is wrong. Must be" " a list of integers.") lengh_interp =np.linspace(self.normalize_freq_betw[0], @@ -2685,14 +2720,14 @@ def __init__(self, comp_name =None , **kwargs): self.name =self.new_comp else : self._logging.warn ('Component provided is wrong !') - CSex.pyCSAMTError_inputarguments( + TypeError( "Component provide as new component is wrong" "list of components:{0}".format(self.component_type)) else : warnings.warn( "Components must be a string : a list of arguments below:" "{0}".format(self.component_type)) - CSex.pyCSAMTError_inputarguments( + TypeError( "Component type is wrong. must a string." "list of components:{0}".format(self.component_type)) @@ -2775,16 +2810,16 @@ def _read_amps_data (self, amps_array= None, number_of_frequencies=None , if amps_array is not None : self._amps_array =amps_array if self._amps_array is None : - raise CSex.pyCSAMTError_inputarguments('No Ampers data !') + raise TypeError('No Ampers data !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations - else : raise CSex.pyCSAMTError_inputarguments( + else : raise TypeError( 'Please specify the number of stations') try : @@ -2793,7 +2828,7 @@ def _read_amps_data (self, amps_array= None, number_of_frequencies=None , self._amps_array=np.array([np.float(cc) for cc in self._amps_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the amp are wrong." " must be float or integer .") @@ -2883,17 +2918,17 @@ def _read_emag_data (self,e_mag_array=None, number_of_frequencies=None , if e_mag_array is not None : self.e_mag_array =e_mag_array if self.e_mag_array is None : - raise CSex.pyCSAMTError_inputarguments('No E-Field data found !') + raise TypeError('No E-Field data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') try : @@ -2902,7 +2937,7 @@ def _read_emag_data (self,e_mag_array=None, number_of_frequencies=None , self.e_mag_array=np.array([np.float(cc) for cc in self.e_mag_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the amp are wrong." " must be float or integer .") @@ -3001,17 +3036,17 @@ def _read_ephz_data (self,e_phz_array=None, number_of_frequencies=None , if e_phz_array is not None : self.e_phz_array =e_phz_array if self.e_phz_array is None : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'No E-phase data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies - else :raise CSex.pyCSAMTError_inputarguments( + else :raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') try : @@ -3020,7 +3055,7 @@ def _read_ephz_data (self,e_phz_array=None, number_of_frequencies=None , self.e_phz_array=np.array([np.float(cc) for cc in self.e_phz_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the E-phase are wrong." " must be float or integer .") if self.to_degree : #---> set angle to degree . @@ -3118,17 +3153,17 @@ def _read_hmag_data (self, h_mag_array=None, number_of_frequencies=None , self.h_mag_array =h_mag_array if self.h_mag_array is None : - raise CSex.pyCSAMTError_inputarguments('No B-Field data found !') + raise TypeError('No B-Field data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') try : @@ -3137,7 +3172,7 @@ def _read_hmag_data (self, h_mag_array=None, number_of_frequencies=None , self.h_mag_array=np.array([np.float(cc) for cc in self.h_mag_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the B-Field are wrong." " must be float or integer .") @@ -3234,17 +3269,17 @@ def _read_hphz_data (self,h_phz_array=None, number_of_frequencies=None , if h_phz_array is not None : self.h_phz_array=h_phz_array if self.h_phz_array is None : - raise CSex.pyCSAMTError_inputarguments('No E-phase data found !') + raise TypeError('No E-phase data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') try : @@ -3253,7 +3288,7 @@ def _read_hphz_data (self,h_phz_array=None, number_of_frequencies=None , self.h_phz_array=np.array([np.float(cc) for cc in self.h_phz_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the E-phase are wrong." " must be float or integer .") if self.to_degree : #---> set angle to degree . @@ -3265,7 +3300,7 @@ def _read_hphz_data (self,h_phz_array=None, number_of_frequencies=None , self.h_phz_array, return_counts=True) if not np.all(repeat_hphz): - raise CSex.pyCSAMTError_value( + raise ValueError( 'Values of B-phases provided must have the same length' ' for each stations. ') self.value =vcounts_h_phz @@ -3368,24 +3403,24 @@ def _read_res_data (self,res_array =None, number_of_frequencies=None , if res_array is not None : self.res_array= res_array if self.res_array is None : - raise CSex.pyCSAMTError_inputarguments('No Resistivity data found !') + raise TypeError('No Resistivity data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') if Sres is not None : self.Sres = np.array([np.float(res) for res in Sres]) if self.res_array.shape[0] != self.Sres.size : - raise CSex.pyCSAMTError_rho( + raise ResistivityError( 'Resistivity calculated & ' 'Astatic_array must get the same length!.') try : @@ -3394,14 +3429,14 @@ def _read_res_data (self,res_array =None, number_of_frequencies=None , self.res_array =np.array([np.float(cc) for cc in self.res_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the Resistivities are wrong." " must be float or integer .") vcounts_res, repeat_hphz=np.unique (self.res_array, return_counts=True) if not np.all(repeat_hphz): - raise CSex.pyCSAMTError_value( + raise ValueError( 'Values of Rho provided must have the same length'\ ' for each stations.') self.value =vcounts_res @@ -3514,17 +3549,17 @@ def _read_phase_data (self,phase_array =None,number_of_frequencies=None , if phase_array is not None : self._phase_array =phase_array if self._phase_array is None : - raise CSex.pyCSAMTError_inputarguments('No Phase data found !') + raise TypeError('No Phase data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') @@ -3535,7 +3570,7 @@ def _read_phase_data (self,phase_array =None,number_of_frequencies=None , for cc in self._phase_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the Phase are wrong."\ " must be float or integer .") if self.to_degree : #---> set angle to degree . @@ -3641,7 +3676,7 @@ def freq (self, freq_array): try : self._freq =np.array([np.float(ff) for ff in freq_array]) except : - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( 'wrong Input frequencies values.must be a float number. ') @@ -3653,7 +3688,7 @@ def phase(self, phz_array): if phz_array.dtype not in ['float', 'int']: try :self._phase =np.array([float(phz) for phz in phz_array]) except : - raise CSex.pyCSAMTError_Phase( + raise PhaseError( 'Arguments phase values must be int, or float .') @property @@ -3665,7 +3700,7 @@ def z_error(self, z_error_): try : self._z_err =np.array([float(zz) for zz in z_error_]) except : - raise CSex.pyCSAMTError_Z( + raise ZError( "Error z input values are incorrects.") @property @@ -3678,7 +3713,7 @@ def z (self, zz_array): try : zz_array= np.array([np.float(zz) for zz in zz_array]) except : - raise CSex.pyCSAMTError_Z( + raise ZError( 'z_impedance value must be a complex_number.') #---> provide freq value and phase . if zz_array.dtype in ['float', 'int']: @@ -3716,7 +3751,7 @@ def z_and_zerr_2rhophi(self, z_array=None , freq=None): if freq is not None : self.freq=freq if self.z is None or self.freq is None : - raise CSex.pyCSAMTError_Z( + raise ZError( 'None values can not be computed. Check values !') self.rho =np.apply_along_axis( @@ -3738,7 +3773,7 @@ def rho(self, res_array): if res_array.dtype not in ['float', 'int']: try : self._rho = np.array([float(res) for res in res_array]) except : - raise CSex.pyCSAMTError_rho( + raise ResistivityError( 'Resistivities values must be float ' 'or integer , not a None type !') @@ -3776,13 +3811,13 @@ def rhophi2rhoph_errors (self, res_array=None , phase_array = None, if self.rho is None or self.phase is None or self.freq is None : self._logging.warn( 'NoneType can not be computed. please check your data arrays.') - raise CSex.pyCSAMTError_Z( + raise ZError( 'could note compute a Nonetype number. please check numbers.') # compute imag part and real part of Z if self.rho.size != self.freq.size : - raise CSex.pyCSAMTError_Z( + raise ZError( 'Resistivity , freq and phase_array must be the same size. ') # zz_= np.array([np.sqrt(0.2 * self._freq[ii] * @@ -3830,18 +3865,18 @@ def _read_zAS_data (self, z_abs_array =None , number_of_frequencies=None , if z_abs_array is not None : self.zAS = z_abs_array if self.zAS is None : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'No zonge Astatic data found !') if number_of_frequencies is not None : self.nfreq = number_of_frequencies - else :raise CSex.pyCSAMTError_inputarguments( + else :raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations= number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') @@ -3861,7 +3896,7 @@ def _read_zAS_data (self, z_abs_array =None , number_of_frequencies=None , np.int(self.number_of_stations) zabs_array=np.array([np.float(cc) for cc in zabs_array]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for Z-astatic are wrong." " check your Zonge AVG file .") @@ -3953,17 +3988,17 @@ def _read_pcEmag_data (self,pc_e_mag_array=None, number_of_frequencies=None , if pc_e_mag_array is not None : self._pcEmag =pc_e_mag_array if self._pcEmag is None : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'No E-mag statistical variation data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations - else : raise CSex.pyCSAMTError_inputarguments( + else : raise TypeError( 'Please specify the number of stations') try : @@ -3971,7 +4006,7 @@ def _read_pcEmag_data (self,pc_e_mag_array=None, number_of_frequencies=None , np.int(self.number_of_stations) self._pcEmag=np.array([np.float(cc) for cc in self._pcEmag]) - except : raise CSex.pyCSAMTError_value( + except : raise ValueError( "Values provided for the E-mag stat.variation are wrong." " must be float or integer .") @@ -4070,18 +4105,18 @@ def _read_sEphz_data (self, sEphz_array=None, number_of_frequencies=None , if sEphz_array is not None : self._sEphz =sEphz_array if self._sEphz is None : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'No stat.variation B-field-phase data found !') if number_of_frequencies is not None : self.nfreq = number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') self.nfreq , self.number_of_stations =np.int(self.nfreq),\ np.int(self.number_of_stations) @@ -4095,7 +4130,7 @@ def _inspect_input (input_obj , to_degree=False): try : input_obj =np.array([float(ss) for ss in input_obj]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for computing are wrong." " must be float or integer .") @@ -4200,18 +4235,18 @@ def _read_pcHmag_data (self,pc_h_mag_array=None, number_of_frequencies=None , if pc_h_mag_array is not None : self._pcHmag =pc_h_mag_array if self._pcHmag is None : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'No B-mag statistical variation data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') try : @@ -4220,7 +4255,7 @@ def _read_pcHmag_data (self,pc_h_mag_array=None, number_of_frequencies=None , self._pcHmag=np.array([np.float(cc) for cc in self._pcHmag]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the B-mag stat.variation are wrong." " must be float or integer .") @@ -4321,18 +4356,18 @@ def _read_sHphz_data (self, sHphz_array=None, number_of_frequencies=None , if sHphz_array is not None : self._sHphz =sHphz_array if self._sHphz is None : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'No stat.variation B-field-phase data found !') if number_of_frequencies is not None : self.nfreq = number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') self.nfreq , self.number_of_stations =np.int(self.nfreq),\ np.int(self.number_of_stations) @@ -4346,7 +4381,7 @@ def _inspect_input (input_obj , to_degree=False): try : input_obj =np.array([float(ss) for ss in input_obj]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for computing are wrong."\ " must be float or integer .") @@ -4458,17 +4493,17 @@ def _read_pcRes_data (self, pcRes_array =None, number_of_frequencies=None , if pcRes_array is not None : self._pcRes= pcRes_array if self._pcRes is None : - raise CSex.pyCSAMTError_inputarguments('No Resistivity data found !') + raise TypeError('No Resistivity data found !') if number_of_frequencies is not None : self.nfreq =number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Please specify the number of stations') try : @@ -4477,13 +4512,13 @@ def _read_pcRes_data (self, pcRes_array =None, number_of_frequencies=None , self._pcRes =np.array([np.float(cc) for cc in self._pcRes]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for the Resistivities are wrong." " must be float or integer .") vcounts_pcres, repeat_hphz=np.unique (self._pcRes, return_counts=True) if not np.all(repeat_hphz): - raise CSex.pyCSAMTError_value( + raise ValueError( 'Values of Rho provided must have the same length' ' for each stations. ') self.value =vcounts_pcres @@ -4581,18 +4616,18 @@ def _read_sPhz_data (self, sPhase_array=None, number_of_frequencies=None , if sPhase_array is not None : self._sPhs =sPhase_array if self._sPhs is None : - raise CSex.pyCSAMTError_inputarguments( + raise ZError( 'No stat.variation Impedance Z -phase data found !') if number_of_frequencies is not None : self.nfreq = number_of_frequencies else : - raise CSex.pyCSAMTError_inputarguments( + raise ValueError( "Please specify the number of frequency !") if number_of_stations is not None : self.number_of_stations =number_of_stations else : - raise CSex.pyCSAMTError_inputarguments( + raise StationError( 'Please specify the number of stations') self.nfreq , self.number_of_stations =np.int(self.nfreq),\ np.int(self.number_of_stations) @@ -4606,7 +4641,7 @@ def _inspect_input (input_obj , to_degree=False): try : input_obj =np.array([float(ss) for ss in input_obj]) except : - raise CSex.pyCSAMTError_value( + raise ValueError( "Values provided for computing are wrong."\ " must be float or integer .") diff --git a/pycsamt/core/cs.py b/pycsamt/core/cs.py index 195e2c0..44a4b98 100644 --- a/pycsamt/core/cs.py +++ b/pycsamt/core/cs.py @@ -19,12 +19,20 @@ import pycsamt.core.z as CSAMTz import pycsamt.utils.zcalculator as Zcc from pycsamt.utils import _p as inFO -from pycsamt.core import j as CSAMTj -from pycsamt.site import (Site, Location, Profile) -from pycsamt.utils import exceptions as CSex +from pycsamt.core import j as CSAMTj +from pycsamt.site import ( + Site, + Location, + Profile + ) from pycsamt.utils import func_utils as func +from pycsamt.utils.exceptions import ( + ProcessingError, + JError, + AVGError, + FileHanglingError +) from pycsamt._csamtpylog import csamtpylog - try : from pycsamt.__init__ import itqdm if itqdm : @@ -248,7 +256,7 @@ def _read_csamt_objs (self, fn=None ): 'Please provide one amomg them.') self._logging.warn('It seems file provided <%s> does not match ' '"edi" nor "avg" nor "j" file.' % self._fn) - raise CSex.pyCSAMTError_file_handling( + raise FileHanglingError( f"Unable to read {self._fn!r}. Only 'edi', 'j' and 'avg' " " formats can be parsed. Please check your path|file." ) @@ -762,7 +770,7 @@ def j2edi(self, data_fn=None, savepath =None, **kwargs): # self.jfiles_list = jfn if self.jfiles_list is None : - raise CSex.pyCSAMTError_J( + raise JError( 'No files found ! Please provide A.G. J-files ') # export to savepath if savepath is not None: @@ -1023,7 +1031,7 @@ def avg2edi (self, if data_fn is not None : self._fn = data_fn if self._fn is None : - raise CSex.pyCSAMTError_avg_file( + raise AVGError( "Could not find any path to read ." "Please provide your right AVG file.") @@ -1182,7 +1190,7 @@ def avg2edi (self, ' Please provided the right filters' ' for computing.'% apply_filter) - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'Filters provided is not acceptable.' ' Recognized filters are "TMA","AMA" AND "FLMA"') diff --git a/pycsamt/core/edi.py b/pycsamt/core/edi.py index c835f84..c2fc655 100644 --- a/pycsamt/core/edi.py +++ b/pycsamt/core/edi.py @@ -35,7 +35,10 @@ from pycsamt.site import Location from pycsamt.utils. _p import _sensitive as SB from pycsamt.utils import gis_tools as gis -from pycsamt.utils import exceptions as CSex +from pycsamt.utils.exceptions import ( + EDIError, + HeaderError + ) from pycsamt._csamtpylog import csamtpylog if imtpy: @@ -232,7 +235,7 @@ def _fetch_headinfos (cobj, attr): elif os.path.dirname (self.edifiles): # path is given and read edipath = self.edifiles - else : raise CSex.pyCSAMTError_EDI( + else : raise EDIError( f"Path to {self.edifiles!r} is wrong!") rfiles = os.listdir (edipath) @@ -263,13 +266,13 @@ def _fetch_headinfos (cobj, attr): try: self.ediObjs = list(map( lambda o: _assert_edi_obj(o), self.ediObjs)) - except CSex.pyCSAMTError_EDI: + except EDIError: raise ValueError ("Expect a list of EDI objects not " f"{type(self.ediObjs[0]).__name__!r}") if self.ediObjs is None: - raise CSex.pyCSAMTError_EDI("None EDI object detected!") + raise EDIError("None EDI object detected!") # sorted ediObjs from latlong self.ediObjs , self.edinames = func.fit_by_ll(self.ediObjs) @@ -808,11 +811,11 @@ def read_edi (self, edifile =None): if edifile is not None : self.edifile = edifile if self.edifile is None : - raise CSex.pyCSAMTError_EDI('NoneType can not read. ' + raise EDIError('NoneType can not read. ' 'Please provide at least an edifile.') if self.edifile is not None : if not os.path.isfile(self.edifile): - raise CSex.pyCSAMTError_EDI('Can not find edifile to read.' + raise EDIError('Can not find edifile to read.' ' Please check your path.') #---> read each section and populate attribute @@ -855,12 +858,12 @@ def _get_specific_comp (self, edifile=None, data_sect_line =None): ' section :edifile :{0}.'.format( edifile)) if edifile is not None : self.edifile = edifile - else :raise CSex.pyCSAMTError_EDI( + else :raise EDIError( 'Can not find edifile to read.' ' NoneType can not be read. Check, your rigth path.') if self.edifile is not None : if os.path.isfile(self.edifile) is False : - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'No edifile file .Please check your edipath.') # we assume that the file will check above , @@ -923,7 +926,7 @@ def _fill_data_array (self, data_dict =None ): if data_dict is not None : self.comp_dict = data_dict elif data_dict is None : - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'None value found. Can not read data') # get frequency array and initialise z_array and Z_error @@ -1138,7 +1141,7 @@ def write_edifile (self, edi_fn=None, new_edifilename=None, datatype =None , 'Currently can write ">=MTSECT" or ' ' ">=EMAPSECT". The only acceptables datatype keys ' ' are either "mt" or "emap".') - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'Datatype provided is not acceptable .' 'Please try "mt" or "emap".') @@ -1421,7 +1424,7 @@ def _write_components_blocks (self, edi_datacomp , else : if comp_key.lower()!='freq': - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'Could not write the component key <%s>'% comp_key) elif datatype in ['emap', '>=emapsect','emapsect']: @@ -1429,7 +1432,7 @@ def _write_components_blocks (self, edi_datacomp , comp_block_line = ['>{0} {1} //{2}\n'.format( comp_key.upper(), block_rot[2],edi_datacomp.size)] - # else : raise CSex.pyCSAMTError_EDI( + # else : raise EDIError( #'Could not write the component key <%s>'% comp_key) else :#datatype.lower() not in ['mt' , '>=mtsect'] @@ -1440,7 +1443,7 @@ def _write_components_blocks (self, edi_datacomp , ' We suggest to use "mt" or "emap".as datatype key, if not ' 'please refer to SEG-Edile ''write principles.'.format(datatype)) - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'DataType <{0}> provided is wrong!' ' please use "MT"or "EMAP".'.format(datatype)) @@ -1810,12 +1813,12 @@ def get_header_list_from_edi (cls, edi_fn=None): """ _logger.info ('Geting <%s> Head info'% edi_fn) - if edi_fn is None : raise CSex.pyCSAMTError_EDI( + if edi_fn is None : raise EDIError( 'Edile file not found ! Please check your right path.') markhead , ediheadlist=0,[] if SB.which_file(filename= edi_fn, deep=False ) =='edi': if SB.which_file(filename= edi_fn, deep=True) !='edi': - raise CSex.pyCSAMTError_Header( + raise HeaderError( 'Edifile <{0}> - Header is not correct.' 'Please check your edifile'.format( os.apth.basename(edi_fn))) @@ -1868,7 +1871,7 @@ def read_head (self, edi_header_list =None): if edi_header_list is not None : self.edi_header = edi_header_list if self.edi_header is None : - raise CSex.pyCSAMTError_Header('None items found to read.') + raise HeaderError('None items found to read.') new_header =[] if self.edi_header is not None : for ii, item in enumerate(self.edi_header) : @@ -2045,7 +2048,7 @@ def get_info_list_from_edi (cls, edi_fn =None): info_mark=0 if edi_fn is None : - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'None infos to read! Please provide the right path .') # we do this simultaneous assement to get a ms of computation . if SB.which_file(filename =edi_fn, deep=False)=='edi': @@ -2080,7 +2083,7 @@ def read_info (self, edi_info_list =None ): if edi_info_list is not None : self.ediinfo =edi_info_list if self.ediinfo is None : - raise CSex.pyCSAMTError_EDI('None list found. can not read.') + raise EDIError('None list found. can not read.') for ii , iteminfo in enumerate(self.ediinfo ): iteminfo=iteminfo.replace('"','') @@ -2359,7 +2362,7 @@ def get_DefineMeasurement_info (cls, edi_fn=None): _flagmeas =0 dfmeasurementlist = [] - if edi_fn is None : raise CSex.pyCSAMTError_EDI( + if edi_fn is None : raise EDIError( 'Can not read edifile .None value is found.') if SB.which_file(filename = edi_fn) =='edi': with open(edi_fn , 'r', encoding= 'utf8') as fdefm : @@ -2421,7 +2424,7 @@ def read_define_measurement (self, define_measurement_list =None ): if define_measurement_list is not None : self.define_measurement= define_measurement_list if self.define_measurement is None : - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'Can not find DefineMeasurment list to read.' 'Please provide the list of definemeasurment.' ' e:g:[key_01=value_01 , ..., key_xx = value_xx] ') @@ -2578,7 +2581,7 @@ def write_define_measurement(self, define_measurement_list = None): self.logging.warn( 'Please check your definemeasurement ' 'list provided. Can not be written.') - raise CSex.pyCSAMTError_EDI( + raise EDIError( "Can not write the items on the definemeasurement " "list provided. It seems no right parser is found.") @@ -2880,7 +2883,8 @@ def get_mtemap_section_list (cls, edi_fn =None ): _logger.info ( "Read MT Section on edifile <%s>: %s" % ( os.path.basename(edi_fn),cls.__name__)) - if edi_fn is None :CSex.pyCSAMTError_EDI( + if edi_fn is None : + raise EDIError( 'NoneType can not be read. Please provide your right edipath.') mtflag, mtsection = 0, [] gmtsectmeasurement=[] @@ -2946,7 +2950,7 @@ def read_mtemap_section (self, mt_or_emap_section_list =None): warnings.warn( 'Nonelist can not be read. Please ' 'provide the right MT section list info.') - raise CSex.pyCSAMTError_EDI( + raise EDIError( 'Nonelist found. cannot read MTsection info.' ' Please provide the riht path.') diff --git a/pycsamt/core/j.py b/pycsamt/core/j.py index cd62038..ffc0d18 100644 --- a/pycsamt/core/j.py +++ b/pycsamt/core/j.py @@ -25,7 +25,7 @@ from pycsamt.utils._p import _sensitive as SB from pycsamt.utils.decorator import deprecated import pycsamt.utils.func_utils as func -from pycsamt.utils import exceptions as CSex +from pycsamt.utils.exceptions import JError from pycsamt._csamtpylog import csamtpylog _logger = csamtpylog.get_csamtpy_logger(__name__) @@ -173,7 +173,7 @@ def stnames(self, jstnames): self._station_names = J.jname(number_of_sites= int(jstnames), survey_name=self.survey_name) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Stations names must be on list or the number' ' of stations not <{0}>.'.format(type(jstnames))) @@ -213,7 +213,7 @@ class objectand get from this class edi infos. self.jfiles_list = jfn if self.jfiles_list is None : - raise CSex.pyCSAMTError_J('No files found !' + raise JError('No files found !' ' Please provide A.G. J-files ') # export to savepath if savepath is None : # create a folder in your current work directory @@ -399,7 +399,7 @@ def collect_jfiles (self, list_of_jfiles=None, jpath = None): self.jfiles_list = sorted(list_of_jfiles) elif self.jfiles_list is None : - raise CSex.pyCSAMTError_J ('Can not find a list of j files.' + raise JError ('Can not find a list of j files.' ' Please check your path !') # we assume that only file is read than put on list for looping. @@ -523,7 +523,7 @@ def plot_Topo_Stn_Azim (self, list_of_jfiles =None, plot ='*', """ if list_of_jfiles is not None : self.jfiles_list= list_of_jfiles - if self.jfiles_list is None : raise CSex.pyCSAMTError_J( + if self.jfiles_list is None : raise JError( 'Can not compute NoneType.Check your path') elif self.jfiles_list is not None : self.collect_jfiles(list_of_jfiles =self.jfiles_list ) @@ -607,7 +607,7 @@ def rewrite_jfiles (self, list_of_jfiles=None , savepath =None, self.savepath = savepath if self.jfiles_list is None : - raise CSex.pyCSAMTError_J( + raise JError( 'No files found to read . ' 'Please check your path <%s>'% os.getcwd()) elif self.jfiles_list is not None : @@ -879,7 +879,7 @@ def jperiod(self, jperds): try : self._jperiod=np.array([float(ii) for ii in jperds]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Can not convert "str"jperiod value to float.') @property @@ -893,7 +893,7 @@ def japp_rho (self, japp_rho): try : self._app_rho=np.array([float(ii) for ii in japp_rho]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Can not convert "str" apparent resistivity value to float.') @property @@ -907,7 +907,7 @@ def jpha (self, jphase): try : self._jpha=np.array([float(ii) for ii in jphase]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" phase value to float.') @property @@ -921,7 +921,7 @@ def jrhomax (self, jrhomax): try : self._jrhomax=np.array([float(ii) for ii in jrhomax]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str"jrhomax value to float.') @property @@ -935,7 +935,7 @@ def jphamax (self, jphamax): try : self._jphamax=np.array([float(ii) for ii in jphamax]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str"jphamax value to float.') @property @@ -949,7 +949,7 @@ def jrhomin (self, jrhomin): try : self._jrhomin=np.array([float(ii) for ii in jrhomin]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str"jrhomin value to float.') @property @@ -963,7 +963,7 @@ def jphamin (self, jphamin): try : self._jphamin=np.array([float(ii) for ii in jphamin]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" jphamin value to float.') @property @@ -978,7 +978,7 @@ def jwrho (self, jwrho): self._jwrho=np.array( [float(ii) for ii in jwrho]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" jwrho value to float.') @property @@ -992,7 +992,7 @@ def jwpha (self, jwpha): try : self._jwpha=np.array([float(ii) for ii in jwpha]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" jwphase value to float.') @property @@ -1006,7 +1006,7 @@ def jreal (self, jreal): try : self._jreal=np.array([float(ii) for ii in jreal]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" jreal value to float.') @@ -1021,7 +1021,7 @@ def jimag (self, jimag): try : self._jimag=np.array([float(ii) for ii in jimag]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" jimag value to float.') @property @@ -1035,7 +1035,7 @@ def jerror (self, jerror): try : self._jerror=np.array([float(ii) for ii in jerror]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" jerror value to float.') @property @@ -1051,7 +1051,7 @@ def jweight (self, jweight): try : self._jweight=np.array([float(ii) for ii in jweight]) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Could not convert "str" jweight value to float.') @@ -1062,7 +1062,7 @@ def jmode (self): @jmode.setter def jmode(self, jpolar): if not isinstance(jpolar, str): - raise CSex.pyCSAMTError_J( + raise JError( 'jMode polarization must be on str not <{}>'.format(type(jpolar))) self._jmode =self.jMode(polarization_type=jpolar) @@ -1087,7 +1087,7 @@ def jname( number_of_sites, survey_name= None,): if not isinstance(number_of_sites, int): try :number_of_sites = int(number_of_sites) except : - raise CSex.pyCSAMTError_J( + raise JError( 'Number of sites must be int not <{0}>.'. format(type(number_of_sites))) for ss in range(number_of_sites): @@ -1132,7 +1132,7 @@ def jMode(self, polarization_type='RXY'): print(notion.j) - raise CSex.pyCSAMTError_J( + raise JError( 'Value provided is not in polarization mode ' '.Please consult the dict above.') @@ -1149,7 +1149,7 @@ def read_j (self, j_fn=None ): jdata=[] if j_fn is not None : self.jfn =j_fn if self.jfn is None : - raise CSex.pyCSAMTError_J( + raise JError( 'Error file. Please Provide the right path!') self._logging.info('Reading A.G.Jones J-format "%s"'%os.path.basename( @@ -1163,7 +1163,7 @@ def read_j (self, j_fn=None ): else : self._logging.warn("File <%s>is not J-format file." %self.jfn) warnings.warn('File <%s> is not J-Format File.'% self.jfn) - raise CSex.pyCSAMTError_J( + raise JError( 'File provided doesn no match the J-format.' ' Please consult :"http://mtnet.info/docs/jformat.txt" {0}'.\ format(webbrowser.open('http://mtnet.info/docs/jformat.txt'))) @@ -1246,7 +1246,7 @@ def read_j (self, j_fn=None ): warnings.warn ( 'J-FORMAT expects to get "9" records' ' values like <{0}>'.format(*list(notion.j_recordR.keys()))) - raise CSex.pyCSAMTError_J( + raise JError( 'For data type=R?? Only 9 Range values are not ' 'acceptable. You provided <{0}>'.format(JDAT.shape[1])) elif (re.match(r'^Z', self.jmode) is True) or \ @@ -1260,7 +1260,7 @@ def read_j (self, j_fn=None ): warnings.warn('JFORMAT for GSC responses expects' ' to get "5" records values like<{0}>'.\ format(*list(notion.j_recordZ.keys()))) - raise CSex.pyCSAMTError_J( + raise JError( 'For data type=R?? Only 9 Range values' ' are not acceptable. You provided <{0}>'.format(JDAT.shape[1])) diff --git a/pycsamt/core/z.py b/pycsamt/core/z.py index bebbfe8..ce371b0 100644 --- a/pycsamt/core/z.py +++ b/pycsamt/core/z.py @@ -25,7 +25,7 @@ # ================================================================= import pycsamt.utils.mtcalculator as MTcc -import pycsamt.utils.exceptions as MTex +from pycsamt.utils.exceptions import ZError from pycsamt._csamtpylog import csamtpylog _logger = csamtpylog.get_csamtpy_logger(__name__) @@ -160,11 +160,11 @@ def set_res_phase(self, res_array, phase_array, freq, res_err_array=None, # assert real array: if np.linalg.norm(np.imag(res_array)) != 0: - raise MTex.MTpyError_inputarguments('Error - array "res" is not' + \ + raise TypeError('Error - array "res" is not' + \ 'real valued !') if np.linalg.norm(np.imag(phase_array)) != 0: - raise MTex.MTpyError_inputarguments('Error - array "phase" is' + \ + raise TypeError('Error - array "phase" is' + \ 'not real valued !') abs_z = np.sqrt(5.0 * self.freq * (self.resistivity.T)).T @@ -533,7 +533,7 @@ def inverse(self): try: inverse[idx_f, :, :] = np.array((np.matrix(self.z[idx_f, :, :])).I) except: - raise MTex.MTpyError_Z('The {0}ith impedance'.format(idx_f + 1) + \ + raise ZError('The {0}ith impedance'.format(idx_f + 1) + \ 'tensor cannot be inverted') return inverse @@ -796,13 +796,13 @@ def remove_distortion(self, distortion_tensor, distortion_err_tensor=None): distortion_tensor = np.matrix(np.real(distortion_tensor)) except ValueError: - raise MTex.MTpyError_Z('The array provided is not a proper' + \ + raise ZError('The array provided is not a proper' + \ 'distortion tensor') try: DI = distortion_tensor.I except np.linalg.LinAlgError: - raise MTex.MTpyError_Z('The provided distortion tensor is' + \ + raise ZError('The provided distortion tensor is' + \ 'singular - I cannot invert that!') # propagation of errors (using 1-norm) - step 1 - inversion of D: @@ -1240,9 +1240,10 @@ def tipper_err(self, tipper_err_array): """ if self.tipper_err is not None and \ (self._tipper_err.shape != tipper_err_array.shape): - raise MT_Z_Error('Shape of new "tipper_err" array does not match old' + \ - 'new shape {0} != old shape {1}'.format(tipper_err_array.shape), + raise ZError('Shape of new "tipper_err" array does not match old' + 'new shape {0} != old shape {1}'.format(tipper_err_array.shape, self._tipper_err.shape) + ) # make sure the input array is of required shape if tipper_err_array is not None: @@ -1616,7 +1617,7 @@ def correct4sensor_orientation(Z_prime, Bx=0, By=90, Ex=0, Ey=90, Z_prime = np.matrix(Z_prime) except: - raise MTex.MTpyError_inputarguments('ERROR - Z array not valid!' + \ + raise TypeError('ERROR - Z array not valid!' + \ 'Must be 2x2 complex array') if Z_prime_error is not None: @@ -1630,7 +1631,7 @@ def correct4sensor_orientation(Z_prime, Bx=0, By=90, Ex=0, Ey=90, raise except: - raise MTex.MTpyError_inputarguments('ERROR - Z-error array not' + \ + raise TypeError('ERROR - Z-error array not' + \ 'valid! Must be 2x2 real array') T = np.matrix(np.zeros((2, 2))) @@ -1654,7 +1655,7 @@ def correct4sensor_orientation(Z_prime, Bx=0, By=90, Ex=0, Ey=90, try: z_arr = np.array(np.dot(T, np.dot(Z_prime, U.I))) except: - raise MTex.MTpyError_inputarguments("ERROR - Given angles do not" + \ + raise TypeError("ERROR - Given angles do not" + \ "define basis for 2 dimensions - cannot convert Z'") z_err_arr = copy.copy(Z_prime_error) diff --git a/pycsamt/geodrill/geocore.py b/pycsamt/geodrill/geocore.py index a820407..b3ad71e 100644 --- a/pycsamt/geodrill/geocore.py +++ b/pycsamt/geodrill/geocore.py @@ -25,16 +25,19 @@ import pandas as pd import pycsamt.bases as BS -import pycsamt.utils.exceptions as CSex import pycsamt.utils.geo_utils as GU import pycsamt.geodrill.structural as STRL +from pycsamt.geodrill.geodatabase import GeoDataBase from pycsamt.modeling import occam2d from pycsamt.site import Profile from pycsamt.utils import func_utils as func from pycsamt.utils import plot_utils as punc -from pycsamt.utils.plotdecorator import geoplot2d -from pycsamt.geodrill.geodatabase import GeoDataBase - +from pycsamt.utils.plotdecorator import geoplot2d +from pycsamt.utils.exceptions import ( + GeoError, + FileHanglingError, + GeoMemoryError +) try : from pycsamt._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) @@ -394,7 +397,7 @@ def frame_each_site_into_3_offsets(site_name, model_res, 'like model, mesh, data, and iteration files.']) warnings.warn('! Error reading *.bln flile !' + mess) self._logging.error(mess) - raise CSex.pyCSAMTError_geodrill_inputarguments( + raise GeoError( '! Error reading *.bln flile ! No (*bln) station file found.' ' Need sites names and sites locations.') i2d_obj = occam2d.Iter2Dat(iter2dat_fn=self.iter2dat_fn, @@ -418,7 +421,7 @@ def frame_each_site_into_3_offsets(site_name, model_res, format(str(attr).replace('_fn', '')) self._logging.error(msg) warnings.warn(msg) - raise CSex.pyCSAMTError_plot_geoinputargument(msg) + raise GeoError(msg) # Recreate object and get ncessaries attributes model_obj= occam2d.Model(model_fn =self.model_fn, @@ -575,7 +578,7 @@ def find_and_replace_rho(rowlines, value_range): try : value_range= np.array([float(ss) for ss in value_range]) except : - raise CSex.pyCSAMTError_geodrill_inputarguments( + raise GeoError( 'Value provided as resistivity ' 'range must be an array of float number.') @@ -683,7 +686,7 @@ def ascertain_input_layers(input_layers, input_rho): try : self.input_resistivities = float(self.input_resistivities ) except : - raise CSex.pyCSAMTError_plot_geoinputargument( + raise GeoError( 'Can not converted value <%s> into float number.'\ ' Please provide a float number.'%self.input_resistivities ) @@ -752,7 +755,7 @@ def get_structure (resistivities_range): try : resistivities_range=[float(resistivities_range)] except : - raise CSex.pyCSAMTError_geodrill_inputarguments( + raise GeoError( 'Can not convert <%s> to float number !Input resistivity ' 'must be a float number not <%s>!'% (resistivities_range, type(resistivities_range))) @@ -761,7 +764,7 @@ def get_structure (resistivities_range): try : resistivities_range =np.array( [float(ss) for ss in resistivities_range]) except : - raise CSex.pyCSAMTError_geodrill_inputarguments( + raise GeoError( 'Input argument provided is wrong. ' 'Please check your resistivities ' ' range, values must be a float number.') @@ -805,7 +808,7 @@ def get_average_rho(data_array, transpose =False ): except : warnings.warn("It seems somethings wrong" " happened during data conversion to float values.") - raise CSex.pyCSAMTError_inputarguments( + raise GeoError( 'Could not convert value' ' to float numbers , Please check your data!') @@ -935,17 +938,17 @@ def get_conductive_zone (dep_array, rho_array, step_in_deep): try : step_in_deep =float(step_in_deep) except : - raise CSex.pyCSAMTError_plot_geoinputargument( + raise GeoError( 'Could not convert depth value ={} to float.' ' Please check your value.'.format(step_in_deep)) if step_in_deep< dep_array.min(): - raise CSex.pyCSAMTError_plot_geoinputargument( + raise GeoError( 'Value provided ={0} m is less than the minimum' ' depth ={1} m.'.format(step_in_deep, dep_array.min())) if step_in_deep > dep_array.max(): - raise CSex.pyCSAMTError_plot_geoinputargument( + raise GeoError( 'Value provided is = {0} m is greater than ' 'maximum depth ={1}m.'.format(step_in_deep, dep_array.max())) @@ -1467,7 +1470,7 @@ def to_golden_software(self, input_resistivities=None,input_layers =None, warnings.warn(mess) self._logging.error(mess) - raise CSex.pyCSAMTError_plot_geoinputargument( + raise GeoError( "Can not write details sequences log files " "! Please provided at least one" " truth layer resistivity.") @@ -1973,7 +1976,7 @@ def create_model_matrix (station_names, geo_dict_rho, transpose =True): ' Support only *.xlsx and *.csv format' self._logging.error (mess) warnings.warn (mess) - raise CSex.pyCSAMTError_geodrill_inputarguments( + raise GeoError( 'wrong format ={0} !'\ ' Could not write geo file to oasis.'\ ' Support only *.xlsx or *.csv format ') @@ -2151,10 +2154,10 @@ def _validate_oasis_path(self, path =None ) : ' right path to oasis models files.' warnings.warn(mess) self._logging.error(mess) - raise CSex.pyCSAMTError_inputarguments(mess) + raise GeoError(mess) elif self.path is None : - raise CSex.pyCSAMTError_inputarguments( + raise GeoError( 'No path found ! Please provided a right path.') # get extension file @@ -2165,7 +2168,7 @@ def _validate_oasis_path(self, path =None ) : mess = 'Unacceptable format = {0}. Could read format ={1}'.\ format(self.extension_file, tuple(self.geo_surface_format)) self._logging.warning(mess) - raise CSex.pyCSAMTError_file_handling(mess) + raise FileHanglingError(mess) @property @@ -2345,7 +2348,7 @@ def get_depth_surfaces(self, path =None , depth_values =None): if depth_values is not None : self.depth_values= depth_values if self.depth_values is None : - raise CSex.pyCSAMTError_plot_geoinputargument( + raise GeoError( 'NoneType could not be computed. Please provided' ' right values !') @@ -2353,7 +2356,7 @@ def get_depth_surfaces(self, path =None , depth_values =None): try : self.depth_values = float(self.depth_values) except : - raise CSex.pyCSAMTError_float('Could not convert {0} to float'.\ + raise TypeError('Could not convert {0} to float'.\ format(self.depth_values)) else : self.depth_values =np.array([self.depth_values]) @@ -2361,7 +2364,7 @@ def get_depth_surfaces(self, path =None , depth_values =None): self.depth_values = np.array(self.depth_values) try : self.depth_values = self.depth_values.astype('float') - except : raise CSex.pyCSAMTError_float( + except : raise TypeError( 'Could not convert values to float!') def get_single_surface_from_one_line(site_name , depth_value) : @@ -2465,7 +2468,7 @@ def write_file(self, path =None, depth_values =None, ' Please provide the rigth format !']) self._logging.error(mess.format(self.export_format)) warnings.warn(mess.format(self.export_format)) - raise CSex.pyCSAMTError_file_handling( + raise FileHanglingError( 'Format provided = {0} is wrong ! ' 'Could output only `csv` or `xlsx`.'. format(self.export_format)) @@ -2476,14 +2479,14 @@ def write_file(self, path =None, depth_values =None, 'Need to specify the value of depth for imaging !') self._logging.warn( 'Need to specify the value of depth for imaging !') - raise CSex.pyCSAMTError_inputarguments( + raise GeoError( 'Need to specify the depth value for imaging.') if self.path is None : warnings.warn( 'Need to provide the rigth path `geodrill` model output files.') self._logging.warning( 'Need to provide the rigth path `geodrill` model output files.') - raise CSex.pyCSAMTError_inputarguments( + raise GeoError( 'No path detected! Please provide a right path to `geodrill`\ oasis montaj outputfiles ') @@ -2575,7 +2578,7 @@ def compute_profile_angle (easting=None, northing=None): _logger.info( 'Computing profile angle from Easting and Nothing coordinates.') if easting is None or northing is None : - raise CSex.pyCSAMTError_inputarguments( + raise GeoError( 'NoneType can not be computed !') # use the one with the lower standard deviation @@ -2583,7 +2586,7 @@ def compute_profile_angle (easting=None, northing=None): easting = easting.astype('float') northing = northing.astype('float') except : - raise CSex.pyCSAMTError_float( + raise TypeError( 'Could not convert input argument to float!') if stats_import is True : @@ -2660,7 +2663,7 @@ def compute_geoelectric_strike (profile_angle = None , easting =None, _logger.warning( 'NoneType found.Could not compute geo-electrike strike!') - raise CSex.pyCSAMTError_inputarguments( + raise GeoError( 'NoneType found. Could not compute geo-electrike strike!') if profile_angle is None : @@ -4662,7 +4665,7 @@ def _ps_memory_management(obj=None, option='set'): 'beforehand to create your first model.') warnings.warn("No memory found. You need to build your " " GeoStratigraphy model by running the class first.") - raise CSex.pyCSAMTError_memory( + raise GeoMemoryError( "Memory not found. Use the GeoStratigraphy class to " "create your model first.") psobj_token, data_ = BS.load_serialized_data( diff --git a/pycsamt/geodrill/geodatabase.py b/pycsamt/geodrill/geodatabase.py index da816c3..8a1b216 100644 --- a/pycsamt/geodrill/geodatabase.py +++ b/pycsamt/geodrill/geodatabase.py @@ -20,12 +20,12 @@ import warnings import datetime -import pycsamt.utils.exceptions as CSex from pycsamt.geodrill.structural import Geo_formation from pycsamt.geodrill._dictapp import Glob from pycsamt.geodrill.requestmanager import ManageDB from pycsamt.utils.decorator import deprecated_to from pycsamt._csamtpylog import csamtpylog +from pycsamt.utils.exceptions import DataBaseError, SQLError #set LogInfos try : _logger=csamtpylog.get_csamtpy_logger(__name__) @@ -208,7 +208,7 @@ def _get_geo_structure(self, structure_name=None): if self.geo_structure_name is None : warnings.warn('No name is inputted as geological formation. Sorry ,' ' your request is aborted !') - raise CSex.pyCSAMTError_SQL_manager( + raise SQLError( 'No name is given as geological formation. Sorry ,'\ ' your request is aborted !') @@ -301,11 +301,11 @@ def _update_geo_structure (self, geo_formation_name =None, **kws): elif 'name' in list(kws.keys()) : geo_formation_name=str(kws['name']) else : - raise CSex.pyCSAMTError_SQL_update_geoinformation( + raise SQLError( ' Unable to find a new geological structure name.') if not isinstance(geo_formation_name, str) : - raise CSex.pyCSAMTError_SQL_update_geoinformation( + raise SQLError( 'Unacceptable rock/layer name ={0}.'\ ' Please provide a right rock/layer name.') geo_formation_name=str(geo_formation_name) # for consistency @@ -329,7 +329,7 @@ def _update_geo_structure (self, geo_formation_name =None, **kws): 'formation with their corresponding values ', ' see { _add_geo_structure } method .']) self._logging.warn(mess) - raise CSex.pyCSAMTError_SQL_update_geoinformation( + raise SQLError( 'Update name= {0} failed ! it doesnt not exist in ' 'geoDataBase'.format(self.geo_structure_name)) @@ -344,7 +344,7 @@ def _update_geo_structure (self, geo_formation_name =None, **kws): " exist in geoDataBase.Please provide a right ", " keys among = {0}". format(tuple(self.codef[2:]))]) self._logging.error(mess) - raise CSex.pyCSAMTError_SQL_manager(mess) + raise SQLError(mess) elif geo_key in self.codef : if geo_key.find('pat') >= 0 : @@ -356,7 +356,7 @@ def _update_geo_structure (self, geo_formation_name =None, **kws): ' value = {0} to float.'.format(kws[geo_key]), 'Please try again later.']) self._logging.error(msg) - raise CSex.pyCSAMTError_SQL_update_geoinformation(msg) + raise SQLError(msg) # let get the formatage of all values (properties values ) elif geo_key.find('colorMPL') >=0 : @@ -554,11 +554,11 @@ def _rev_func_code (code, CODE): elif 'name' in list(kws.keys()) : new_geological_rock_name=str(kws['name']) else : - raise CSex.pyCSAMTError_SQL_update_geoinformation( + raise SQLError( ' ! Unable to find a new geo_logical structure name.') if not isinstance(new_geological_rock_name, str) : - raise CSex.pyCSAMTError_SQL_update_geoinformation( + raise SQLError( 'Unacceptable rocks names ={0}.' ' Please provide a right rock name.') new_geological_rock_name=str(new_geological_rock_name) # @@ -575,7 +575,7 @@ def _rev_func_code (code, CODE): elif geoDataBase_obj.success ==0: mess = "Connection to SQL geoDataBase failed ! Try again later." warnings.warn(mess) - raise CSex.pyCSAMTError_SQL_geoDataBase(mess) + raise DataBaseError(mess) #---------------------------------------------------------------------- @@ -586,7 +586,7 @@ def _rev_func_code (code, CODE): geoDataBase_obj.geo_structure_name) warnings.warn(mess) _logger.error(mess) - raise CSex.pyCSAMTError_SQL_update_geoinformation(mess) + raise SQLError(mess) if geoDataBase_obj.geo_structure_exists is False : @@ -610,7 +610,7 @@ def _rev_func_code (code, CODE): for key in list(kws.keys()) : if key not in new_codef : - raise CSex.pyCSAMTError_SQL_update_geoinformation( + raise SQLError( 'Process aborted ! wrong <{0}> key!' ' could not add new informations. ' 'Please check your key !'.format(key)) @@ -686,7 +686,7 @@ def _rev_func_code (code, CODE): warnings.warn (mess) _logger.error(mess) - raise CSex.pyCSAMTError_SQL_update_geoinformation(mess) + raise SQLError(mess) else : @@ -711,7 +711,7 @@ def pattern (self, pattern_value): mes ='Process aborted ! Could not convert'\ f' {pattern_value} to float number.' self._logging.warning(mes) - raise CSex.pyCSAMTError_SQL_update_geoinformation(mes) + raise SQLError(mes) else : self._pattern = float(pattern_value) @@ -749,7 +749,7 @@ def colorMPL (self, mpl_color): self._mplcolor = mpl.colors.to_rgb(str ( mpl_color)) except : - raise CSex.pyCSAMTError_SQL_manager( + raise SQLError( ' Unsupported {0} color!'.format(mpl_color)) else : # keep only R, G, B and abandon alpha . #Matplotlib give tuple of 4 values @@ -765,7 +765,7 @@ def colorMPL (self, mpl_color): ' again later.']) self._logging.error(msg) - raise CSex.pyCSAMTError_SQL_update_geoinformation(msg) + raise SQLError(msg) # let check whether the value provided can be converted to float if len(mpl_color)==3 : try : @@ -776,7 +776,7 @@ def colorMPL (self, mpl_color): '= {0} to float.'.format(mpl_color), 'Please try again later.']) self._logging.error(msg) - raise CSex.pyCSAMTError_SQL_update_geoinformation(msg) + raise SQLError(msg) else : # try to check if value is under 1. # because color is encoding to 1 to 255 bits @@ -790,7 +790,7 @@ def colorMPL (self, mpl_color): f' `{ival}` is UNacceptable value ! Input ', f' value is {fmt}. It must be encoding from ', '1 to 255 bits as MPL colors.']) - raise CSex.pyCSAMTError_SQL_update_geoinformation(msg) + raise SQLError(msg) self._mplcolor=str( self._mplcolor) # put on str for consistency @@ -842,7 +842,7 @@ def electrical_props(self,range_of_rocks_resvalues): self._electrical_props =[float(res) for res in range_of_rocks_resvalues] except : - raise CSex.pyCSAMTError_SQL_update_geoinformation( + raise SQLError( ' !Could not convert input values to float.') else :# range the values to min to max self._electrical_props =sorted(self._electrical_props) @@ -868,7 +868,7 @@ def electrical_props(self,range_of_rocks_resvalues): self._logging.error(mess) warnings.warn(mess) - raise CSex.pyCSAMTError_SQL_update_geoinformation(mess) + raise SQLError(mess) else : self._electrical_props = .0 #mean value initialised @@ -953,7 +953,7 @@ def _setGeoDatabase(self): except : warnings.warn('Could not create {AGS0} Table !') - raise CSex.pyCSAMTError_SQL_geoDataBase( + raise DataBaseError( 'Table AGS0 already exists !') if self.manage_geoDataBase.success ==1: diff --git a/pycsamt/geodrill/structural.py b/pycsamt/geodrill/structural.py index 47e7633..a531b36 100644 --- a/pycsamt/geodrill/structural.py +++ b/pycsamt/geodrill/structural.py @@ -14,7 +14,9 @@ import numpy as np from pycsamt.utils.geo_utils import mapping_stratum as strato -from pycsamt.utils import exceptions as CSex +from pycsamt.utils.exceptions import ( + StructuralError, ParamNumberError + ) from pycsamt._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) @@ -625,7 +627,7 @@ def agso_fn (self): if _secags0 != self.codef : warnings.warn( ' !Trouble occurs while decoding the geostructures!') - raise CSex.pyCSAMTError_structural( + raise StructuralError( 'Geostructures files provided is wrong !') else : @@ -633,7 +635,7 @@ def agso_fn (self): 'No Geostructure file detected.It seems the file' ' is moved or deleted from its home folder' ' ') - raise CSex.pyCSAMTError_structural( + raise StructuralError( 'pyCSAMT inner geocodes not found ' 'in folder.' ' Please provide the right structures codes.') @@ -726,7 +728,7 @@ def ascertain_cp (cp): if cp >255. : warnings.warn(' !RGB value is range 0 to 255 pixels , ' 'not beyond !. Your input values is = {0}.'.format(cp)) - raise CSex.pyCSAMTError_parameter_number( + raise ParamNumberError( 'Error color RGBA value ! RGB value provided is = {0}.' ' It is larger than 255 pixels.'.format(cp)) return cp diff --git a/pycsamt/modeling/modem.py b/pycsamt/modeling/modem.py index 3eba63b..2046d51 100644 --- a/pycsamt/modeling/modem.py +++ b/pycsamt/modeling/modem.py @@ -31,18 +31,14 @@ @author: Daniel """ -import warnings -from pycsamt.__init__ import ( - imtpy, - is_installing, - ) from pycsamt._csamtpylog import csamtpylog from pycsamt.core.edi import Edi_collection +from pycsamt.utils.exceptions import ModEMError _logger =csamtpylog.get_csamtpy_logger(__name__) -if imtpy: +try: from mtpy.modeling.modem import ( data , model , @@ -53,18 +49,17 @@ control_inv, station ) -else: - _logger.info('Unable to import `MTpy`packages. Loading failed!') - warnings.warn("Auto setting up MTpy failed! you can get MTpy at " - " `.") -try : - import geopandas -except ImportError : - success = is_installing('geopandas', DEVNULL= True ) - if not success: - warnings.warn("Import 'geopandas' failed. Install it mannually and " - " preferably use anaconda!") - +except ModuleNotFoundError as e: + msg=("This error happens because {}. You try to import some ModEM" + " utilities while some dependencies are not installed. Prior" + " intall 'MTpy' for dealing with all dependencies at once or" + " install the related missing module.") + raise ModEMError (msg.format(str(e).lower())) +except : + raise ImportError( + "Loading failed. Unable to import `MTpy` package. You can get" + " 'MTpy' at `.") + __all__= ['DataModelAnalysis', 'Data', 'Model', @@ -79,7 +74,6 @@ class Residual(residual.Residual): class to contain residuals for each data point, and rms values for each station - ====================== ==================================================== Attributes/Key Words Description ====================== ==================================================== diff --git a/pycsamt/modeling/occam2d.py b/pycsamt/modeling/occam2d.py index 77c6ce7..bfd220e 100644 --- a/pycsamt/modeling/occam2d.py +++ b/pycsamt/modeling/occam2d.py @@ -22,13 +22,15 @@ import datetime import numpy as np -import pycsamt.utils.exceptions as CSex from pycsamt.__init__ import imtpy from pycsamt.utils import func_utils as func from pycsamt.utils import plot_utils as punc from pycsamt.utils import plotdecorator as mdeco from pycsamt.utils._p import _sensitive as SB from pycsamt._csamtpylog import csamtpylog +from pycsamt.utils.exceptions import ( + OccamError, I2datError, AVGError + ) _logger =csamtpylog.get_csamtpy_logger(__name__) @@ -100,7 +102,7 @@ def fn (self, logpath): self._fn = logpath else : warnings.warn('No occam 2d logfile detected. Please check your path.') - raise CSex.pyCSAMTError_occam2d( + raise OccamError( 'Doesnt not recognize the Logfile <{0}>'\ ' provided.'.format(os.path.basename(logpath))) @@ -126,7 +128,7 @@ def read_occam2d_logfile(self, fn =None ) : 'Reading and setting OccamLog attributes' ' from logfiles <%s>.'% self.fn ) if self.fn is None : - raise CSex.pyCSAMTError_occam2d( + raise OccamError( 'None occam2dlog file detected. Please check your right path .') with open(self.fn , 'r', encoding ='utf8') as focc: @@ -778,11 +780,11 @@ def read_occam2d_modelfile(self, model_fn=None, mesh_fn=None , iter_fn=None): if ifile is None : self._logging.error(mess) warnings.warn(mess) - raise CSex.pyCSAMTError_occam2d(mess) + raise OccamError(mess) if self.iter_fn is None : - raise CSex.py + raise I2datError("NoneType can not be read ") if SB.which_file(filename =self.model_fn )=='model': with open(self.model_fn , 'r', encoding='utf8')as fmod : @@ -844,7 +846,7 @@ def read_occam2d_modelfile(self, model_fn=None, mesh_fn=None , iter_fn=None): warnings.warn (mess) self._logging.error(mess) - raise CSex.pyCSAMTError_occam2d(mess) + raise OccamError(mess) #---> loop the model rows suppose the number of models layer occm=0 @@ -1614,7 +1616,7 @@ def read_occam2d_mesh(self, mesh_fn =None): mess ='No Mesh file detected. Please provide the'\ ' right occam2d mesh files.' warnings.warn(mess), self._logging.error(mess) - raise CSex.pyCSAMTError_occam2d(mess) + raise OccamError(mess) # characteristic of the mesh (nblocks +1) mesh_char = occam_mesh_lines[1].strip().split() # horizontal nodes , verticales nodes (nlayers+1) @@ -2018,7 +2020,7 @@ def write_iter2dat_file(self, filename=None ,model_fn=None, iter_fn=None , if isinstance(self.elevation , (list, tuple)) : self.elevation = np.array(self.elevation ) assert len(self.elevation) == len(self.station_location) ,\ - CSex.pyCSAMTError_occam2d( + OccamError( 'Elevation provided must be the same with offsets :'\ ' length|size ={0}.'.format(self.station_location.size)) @@ -2156,7 +2158,7 @@ def read_iter2dat(self, iter2dat_fn =None, bln_fn =None, scale ='km', warnings.warn('---> Iter2Dat file is generally converted into km.' ' so provided the specify unit of yourf file.') - raise CSex.pyCSAMTError_occam2d_iter2dat( + raise I2datError( 'Wrong scale ! . Might be "m" or "km".') elev =kws.pop('elevation', None) @@ -2173,7 +2175,7 @@ def validate_iter2dat_file (iter2dat_fn) : self._logging.error(mess) warnings.warn(mess) - raise CSex.pyCSAMTError_occam2d_iter2dat(mess) + raise I2datError(mess) else : with open(iter2dat_fn, 'r') as fi2d: iter2dat_readlines =fi2d.readlines() @@ -2185,7 +2187,7 @@ def validate_iter2dat_file (iter2dat_fn) : ' Please provide the right iterartion file.' warnings.warn(mess) self._logging.error(mess) - raise CSex.pyCSAMTError_occam2d_iter2dat(mess) + raise I2datError(mess) else : newf.append(item) return func.concat_array_from_list(list_of_array=newf) @@ -2222,7 +2224,7 @@ def validate_iter2dat_file (iter2dat_fn) : 'and data file or provided model obj.']) warnings.warn(mess) self._logging.error(mess) - raise CSex.pyCSAMTError_occam2d_iter2dat(mess) + raise I2datError(mess) self.OccamModel = Model(model_fn = self.model_fn , iter_fn =self.iter_fn , @@ -2236,7 +2238,7 @@ def validate_iter2dat_file (iter2dat_fn) : ' data file or occam model obj .']) warnings.warn(mess) self._logging.error(mess) - raise CSex.pyCSAMTError_occam2d_iter2dat(mess) + raise I2datError(mess) elif self.OccamModel is not None : # try to get an spacific attribute from Model try : @@ -2249,7 +2251,7 @@ def validate_iter2dat_file (iter2dat_fn) : warnings.warn(mess) self._logging.error(mess) - raise CSex.pyCSAMTError_occam2d_iter2dat( + raise I2datError( 'Object provided is not a Model class object.'\ ' Please provide a right model obj. ') # thin now everything is fine then : @@ -2410,7 +2412,7 @@ def validate_iter2dat_file (iter2dat_fn) : mess =" It seem all data from station and elevation "\ "can not be convert into float value."\ " Please check your data." - raise CSex.pyCSAMTError_occam2d_iter2dat(mess) + raise I2datError(mess) if self.verbose > 0: print('{0:-^77}'.format('Iter2Dat *Station* info')) @@ -2779,7 +2781,7 @@ def getMisfit(resp_fn =None, data_fn =None, kind='rho', **kwargs) : elif kind.lower().find('ph')>=0 or kind==2: kind = 'phase' else : - raise CSex.pyCSAMTError_occam2d( + raise OccamError( f'Wrong argument kind {kind!r}.' 'Should be `rho` or `phase`') @@ -2807,10 +2809,10 @@ def getMisfit(resp_fn =None, data_fn =None, kind='rho', **kwargs) : 'Please select the right mode.']) warnings.warn(mess) _logger().error (mess) - raise CSex.pyCSAMTError_occam2d(f' Mode `{mode}` is wrong!' + raise OccamError(f' Mode `{mode}` is wrong!' 'Do you mean {0}{1}?'.format(*resp_occam_dtype_obj)) elif mode not in resp_occam_dtype_obj: - raise CSex.pyCSAMTError_occam2d(f' Mode `{mode}` is wrong!' + raise OccamError(f' Mode `{mode}` is wrong!' 'Do you mean {0}{1}?'.format(*resp_occam_dtype_obj)) # check the mode provided , can be str @@ -2972,13 +2974,13 @@ def plotResponse(data_fn =None, resp_fn =None, stations =None, **kws): if file.find('.resp')>=0] if data_fn is None: - raise CSex.pyCSAMTError_AVG( + raise AVGError( 'No `Data` file detected. Please provide an occam data file.') elif isinstance(data_fn, str): data_fn = [data_fn] if resp_fn is None: - raise CSex.pyCSAMTError_occam2d( + raise OccamError( 'No OccamResponse file detected.' ' Please provided an occcam Response file') @@ -2987,7 +2989,7 @@ def plotResponse(data_fn =None, resp_fn =None, stations =None, **kws): if len(data_fn) != len(resp_fn): a_, b_= len(data_fn) , len(resp_fn) - raise CSex.pyCSAMTError_inputarguments('Number of Occam datafiles and' + raise ValueError('Number of Occam datafiles and' 'responses files must be the same length.' f' `{a_}` and `{b_}` are given respectively.') @@ -3020,7 +3022,7 @@ def plotResponse(data_fn =None, resp_fn =None, stations =None, **kws): f" the number of line ={len(data_fn)}.") station_list =station_list [:len(data_fn)] else: - raise CSex.pyCSAMTError_value( + raise ValueError( "Data and stations to visualize must have the same " f"length. But {len(data_fn)} and {len(station_list)} " "were given respectively.") diff --git a/pycsamt/processing/corr.py b/pycsamt/processing/corr.py index 90ab9a6..c9580cf 100644 --- a/pycsamt/processing/corr.py +++ b/pycsamt/processing/corr.py @@ -22,9 +22,16 @@ from pycsamt.core import z as CSAMTz from pycsamt.utils import zcalculator as Zcc from pycsamt.utils.decorator import deprecated -from pycsamt.utils import exceptions as CSex from pycsamt.utils import func_utils as func from pycsamt._csamtpylog import csamtpylog +from pycsamt.utils.exceptions import ( + ProcessingError , + FrequencyError, + ResistivityError, + PhaseError, + FileHandlingError + + ) try : from pycsamt.__init__ import itqdm if itqdm : @@ -142,7 +149,7 @@ def frequency (self): def frequency(self, freq): if freq.dtype not in ['float', 'int']: try : freq = np.array([float(ff) for ff in freq]) - except : raise CSex.pyCSAMTError_frequency( + except : raise FrequencyError( 'Frequency must be float number!') else : self._freq_array=freq @@ -154,7 +161,7 @@ def app_rho (self): def app_rho (self, app_res): if app_res.dtype not in ['float', 'int']: try : app_res =np.array([float(res) for res in app_res]) - except : raise CSex.pyCSAMTError_rho( + except : raise ResistivityError( 'Apparent resistivities values must be float number.!') self._res_array=app_res @@ -167,7 +174,7 @@ def referencefreq(self, reffreq): if isinstance(reffreq, tuple) : # the case where value are preforced reffreq = reffreq[0] try :reffreq =float(reffreq) - except:raise CSex.pyCSAMTError_frequency( + except:raise FrequencyError( 'Reference frequency must be a float or int number.') if reffreq not in self.frequency: @@ -180,7 +187,7 @@ def referencefreq(self, reffreq): 'inside the range <{0} to {1}>'.format( self._freq_array.min(),self._freq_array.max())) - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( 'Frequency out of the range !') self._reference_frequency =Zcc.find_reference_frequency( @@ -201,7 +208,7 @@ def phase(self, phz): phz=np.array([float(pzz) for pzz in phz]) except: - raise CSex.pyCSAMTError_Phase( + raise PhaseError( 'Phase input must be a float number and radians.') self._phase_array=phz @@ -259,7 +266,7 @@ def read_processing_file(self, data_fn=None , self._logging.info(mess) warnings.warn(mess) - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'Could not process data with Nonetype obj!' 'Please provide your right data files or ' ' ndarray|dict of resistivies and phases values in degree.') @@ -290,7 +297,7 @@ def read_processing_file(self, data_fn=None , len(self.phase_obj)))) self._logging.error(mess.format(len(self.res_app_obj, len(self.phase_obj)))) - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'Resistivity and phase must be the same length!') # convert values in rad @@ -328,8 +335,8 @@ def read_processing_file(self, data_fn=None , warnings.warn(mess) self.dipolelength =50. - except CSex.pyCSAMTError_file_handling: - raise CSex.pyCSAMTError_file_handling( + except FileHandlingError: + raise FileHandlingError( f" It seems {self.data_fn!r} does not exist." " Please provide the right filename or path.") @@ -925,7 +932,7 @@ def compute_fixed_and_adaptative_moving_average(self, filterfunc, if freq_array is not None : self.frequency =freq_array if res_array is None or phase_array is None or freq_array is None : - raise CSex.pyCSAMTError_processing('NoneType can be computed.' + raise ProcessingError('NoneType can be computed.' ' Please provide either path to data file ' ' or provided resistivity , phase and frequeny arrays!') @@ -1161,7 +1168,7 @@ def write_corrected_edi (self, try: _filter =FILTER.lower() except : - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'name of filters is `str` not `{0}`'.format(type(_filter))) @@ -1171,7 +1178,7 @@ def write_corrected_edi (self, 'we will set filter to `tma`') print('--> Filter is resetting to TMA') - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'Filter provided is Unrecognizable!') self._logging.info('Rewrite edifiles by applying filters') @@ -1331,7 +1338,7 @@ def write_corrected_edi (self, 'distortion tensor as a 2x2 matrices.') print('---> Remove distorsion Error ! Please provide ' 'the distortion Tensor as 2x2 matrices. ') - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'Could not remove distorsion.Please provided real ' ' distortion 2x2 matrixes') _, z_corrected, z_corrected_err = \ @@ -1493,7 +1500,7 @@ def correct_edi (self, try: _filter =FILTER.lower() except : - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'name of filters is `str` not `{0}`'.format(type(_filter))) if _filter not in TAGS.keys(): @@ -1502,7 +1509,7 @@ def correct_edi (self, f"{func.smart_format(TAGS.keys())}."]) warnings.warn(msg ) - raise CSex.pyCSAMTError_processing(msg) + raise ProcessingError(msg) self._logging.info(f'Apply filters {_filter!r} to edifiles') @@ -2070,7 +2077,7 @@ def interp_to_reference_freq(freq_array, rho_array, rhoObj=np.array([float(kk) for kk in rhoObj]) if reference_freq not in freqObj : - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( 'Reference frequency selected as input ' 'argument is not Found on the frequency array.' ' Please select the right frequency ') diff --git a/pycsamt/site.py b/pycsamt/site.py index a5924ee..60f557a 100644 --- a/pycsamt/site.py +++ b/pycsamt/site.py @@ -11,10 +11,16 @@ from pycsamt._csamtpylog import csamtpylog from pycsamt.utils import _p as inFO -from pycsamt.utils import exceptions as CSex from pycsamt.utils import gis_tools as gis from pycsamt.utils import avg_utils as cfunc from pycsamt.utils import func_utils as func +from pycsamt.utils.exceptions import ( + StationError, + LocationError, + ProfileError, + SiteError, + AzimuthError + ) _logger = csamtpylog.get_csamtpy_logger(__name__) try : @@ -121,19 +127,19 @@ def azimuth(self): def utm_zone (self, utm_zone): if isinstance(utm_zone,(float,int)): warnings.warn('Wrong UTM zone input. Must be a str number.') - raise CSex.pyCSAMTError_location( + raise LocationError( 'UTM Zone must be string, ' 'not <{0}>type.'.format(type(utm_zone))) else : try : float(utm_zone[:-2]) - except : raise CSex.pyCSAMTError_location( + except : raise LocationError( 'Error UTM Zone designator. Both first letters' ' provided are not acceotable !') else : if utm_zone[-1] not in list( inFO.notion.utm_zone_dict_designator.keys()): - raise CSex.pyCSAMTError_location( + raise LocationError( 'Wrong UTM Zone letter designator.' ' Letter must be among <{0}>'. format('|'.join(list( @@ -170,7 +176,7 @@ def easting (self, easting): else : try : self._easting = np.array([ float(east) for east in easting]) - except :raise CSex.pyCSAMTError_float( + except :raise TypeError( 'Easting must be float or an array of float number.') @northing.setter @@ -181,14 +187,14 @@ def northing (self, northing): try : self._northing = np.array([float(north) for north in northing]) - except : raise CSex.pyCSAMTError_float( + except : raise TypeError( 'northing must be float or an array of float number. ') @stn_pos.setter def stn_pos (self, stn_pk): try : self._stn_pos =np.array([float(stn) for stn in stn_pk]) - except:raise CSex.pyCSAMTError_station( + except:raise StationError( 'Station pka must be float number.! ') @@ -196,7 +202,7 @@ def stn_pos (self, stn_pk): def azimuth (self, easting_northing): print(easting_northing) if easting_northing.shape[1] !=2 : - raise CSex.pyCSAMTError_azimuth( + raise AzimuthError( 'Azimuth expected to get two array_like(ndarray,2)') elif easting_northing.shape[1] ==2 : easting , northing =np.hsplit(easting_northing, 2) @@ -223,7 +229,7 @@ def convert_location_2_utm (self, latitude =None , if isinstance(self.latitude, np.ndarray): if self.latitude.size >= 1 or self.longitude >=1: # assert self.latitude.size ==self.longitude.size,\ - CSex.pyCSAMTError_location( + LocationError( 'latitude and longitude must be the same size.') self.easting = np.array([ @@ -254,7 +260,7 @@ def convert_location_2_latlon(self, utm_zone =None ): """ if utm_zone is not None : self._utm_zone =utm_zone if self._utm_zone is None : - raise CSex.pyCSAMTError_location( + raise LocationError( 'Try to input the utm_zone : e.g.: 49N') _data_info_ll = gis.utm_to_ll(reference_ellipsoid=23, @@ -362,7 +368,7 @@ def lat (self, latitude): elif self.stn_name is not None : assert len(self.stn_name)== latitude.size, \ - CSex.pyCSAMTError_site( + SiteError( 'Station names and latitude data must have the same size.' ' But the given latitude size is <{0}>'.format(latitude.size)) @@ -376,7 +382,7 @@ def lon(self, longitude): if self.stn_name is None : self.stn_name = longitude.size if not isinstance(longitude,np.ndarray): longitude =np.array(longitude) if len(self.stn_name) != longitude.size : - raise CSex.pyCSAMTError_site( + raise SiteError( 'Station_names|longitude must' ' have the same size.Longitude size is <{0}>'. format(longitude.size)) @@ -391,7 +397,7 @@ def elev(self, elevation): if not isinstance(elevation,np.ndarray): elevation =np.array(elevation) if len(self.stn_name) != elevation.size : - raise CSex.pyCSAMTError_site('Station_names|Elevation must' + raise SiteError('Station_names|Elevation must' ' have the same size.Elevation size is' ' <{0}>'.format(elevation.size)) self._elev ={stn:elev for stn, elev in zip (self.stn_name, elevation)} @@ -404,7 +410,7 @@ def azim(self, azimuth): if self.stn_name is None : self.stn_name = azimuth.size if not isinstance(azimuth,np.ndarray): azimuth =np.array(azimuth) if len(self.stn_name) != azimuth.size : - raise CSex.pyCSAMTError_site( + raise SiteError( 'Station_names|Azimuth must' ' have the same size.Azimuth size is <{0}>'.\ format(azimuth.size)) @@ -426,7 +432,7 @@ def north(self, northing): if not isinstance(northing,np.ndarray): northing =np.array(northing) if len(self.stn_name) != northing.size : - raise CSex.pyCSAMTError_site( + raise SiteError( 'Station_names|Northing must' ' have the same size.Northing size is <{0}>'. format(northing.size)) @@ -441,7 +447,7 @@ def east(self, easting): self.stn_name = easting.size if not isinstance(easting,np.ndarray): easting =np.array(easting) if len(self.stn_name) != easting.size : - raise CSex.pyCSAMTError_site( + raise SiteError( 'Station_names|Easting must' ' have the same size.Easting size is <{0}>'.\ format(easting.size)) @@ -466,13 +472,13 @@ def set_site_info(self, data_fn = None, easting =None , if data_fn is not None : self.sitedata = data_fn if self.sitedata is None and easting is None and northing is None : - raise CSex.pyCSAMTError_profile('Could not find the file to read. ' + raise ProfileError('Could not find the file to read. ' 'Please provide the right path.') elif self.sitedata is not None : if os.path.isfile (self.sitedata) : profile_obj =Profile(profile_fn=self.sitedata) - else : raise CSex.pyCSAMTError_profile( + else : raise ProfileError( 'Unable to read a file. Please provide the right file.') @@ -697,7 +703,7 @@ def read_stnprofile (self, profile_fn =None , elif self.profile_fn is None and (easting is not None or northing is None) and \ (lat is None or lon is None) : - raise CSex.pyCSAMTError_profile( + raise ProfileError( 'Provided at least easting or lon or Northing' ' or lat value or profile stn file.') @@ -735,7 +741,7 @@ def read_stnprofile (self, profile_fn =None , decision , stn_headlines_id = cfunc._validate_stnprofile( profile_lines= data_lines , spliting=split_type) if decision <2: - raise CSex.pyCSAMTError_profile( + raise ProfileError( 'Please provide at least the Easting' ' and northing coordinates or set the ' 'lat/lon values to parse the data. ') @@ -840,7 +846,7 @@ def read_stnprofile (self, profile_fn =None , elif _pflag ==2 or _pflag ==3 : if _pflag == 2 : assert easting.size == northing.size ,\ - CSex.pyCSAMTError_profile( + ProfileError( 'Easting and Northing must have the same size.' ' easting|northing size are currentlysize is <{0}|{1}>.'. format(easting.size, northing.size)) @@ -856,7 +862,7 @@ def read_stnprofile (self, profile_fn =None , elif _pflag ==3 : assert lat.size == lon.size ,\ - CSex.pyCSAMTError_profile( + ProfileError( 'Easting and Northing must have the same size.' ' lat|lon size are currentlysize is <{0}|{1}>.'. format(lat.size, lon.size)) @@ -898,11 +904,11 @@ def get_profile_angle(self, easting =None, northing =None ): if easting is not None : self.east = easting if self.east is None : - raise CSex.pyCSAMTError_location( + raise LocationError( 'Easting coordinates must be provided . ') if northing is not None : self.north=northing if self.north is None : - raise CSex.pyCSAMTError_location( + raise LocationError( 'Northing coordinate must be provided.') if self.east is not None and self.north is not None : @@ -911,7 +917,7 @@ def get_profile_angle(self, easting =None, northing =None ): self.east =self.east.astype('float') self.north = self.north.astype('float') except : - raise CSex.pyCSAMTError_float( + raise TypeError( 'Could not convert easting/northing to float.') # use the one with the lower standard deviation @@ -1030,7 +1036,7 @@ def reajust_coordinates_values ( x=None, y=None, if isinstance(x, str) or isinstance(y,str) : try : float(x), float(y) except : - raise CSex.pyCSAMTError_profile( + raise ProfileError( 'Readjustment not possible with str number.' ' Please provide the right values of x and y .') if x is None : @@ -1048,7 +1054,7 @@ def reajust_coordinates_values ( x=None, y=None, 'Not possible to reajust coordinates.' ' Provide at least easting and northing values.') - raise CSex.pyCSAMTError_station( + raise StationError( 'Could not find station file ' 'to read. Can not readjust profile coordinates.') @@ -1059,7 +1065,7 @@ def reajust_coordinates_values ( x=None, y=None, if inFO._sensitive().which_file(filename=stn_fn , deep=False )=='stn': if inFO._sensitive.which_file(filename=stn_fn ) !='stn': - raise CSex.pyCSAMTError_profile( + raise ProfileError( 'File provided <{0}>is unacceptable. ' 'Please provide your right stn file.'. format(stn_fn)) @@ -1098,7 +1104,7 @@ def reajust_coordinates_values ( x=None, y=None, ' ["dot|sta, "e|easting, "n|northing, "h|elev].' ' Only this head can be parsed.') - raise CSex.pyCSAMTError_profile( + raise ProfileError( 'Your station file profided is wrong. ' 'Only <"dot|sta, "e|easting, ' '"n|northing, "h|elevation> can be parsed. ') @@ -1225,11 +1231,11 @@ def straighten_profileline (self, X=None, Y=None , if X is not None : self.east = X if Y is not None : self.north = Y if self.east is None or self.north is None : - raise CSex.pyCSAMTError_profile('No possible way to straighten out ' + raise ProfileError('No possible way to straighten out ' 'the profile line . Please provide the right coordinates. ') if self.east.size != self.north.size : - raise CSex.pyCSAMTError_profile('X and Y must be the same size. ' + raise ProfileError('X and Y must be the same size. ' 'X has a size <{0}> while Y has the size <{1}>. ' 'Line cannot be straightened. ' 'Please provide the same size of both arrays'.\ @@ -1238,7 +1244,7 @@ def straighten_profileline (self, X=None, Y=None , # rescalled the coordinates if reajust is not None : if len(reajust) !=2 : - raise CSex.pyCSAMTError_profile( + raise ProfileError( "Could not reajust coordinates beyond three values." "Only x =reajust[0] and y=reajust[1] can be accepted. ") @@ -1405,7 +1411,7 @@ def rewrite_station_profile (self, easting =None , self._logging.warning( "It seems you did not provide any easting" " values nor northing value.") - raise CSex.pyCSAMTError_profile( + raise ProfileError( "You may provide easting and northing values before" " writting a new station profile <*.stn> ") @@ -1551,12 +1557,12 @@ def stn_separation(self, easting =None , northing =None , try : easting = np.array([float(ss) for ss in easting]) northing =np.array([float(ss) for ss in northing]) - except : raise CSex.pyCSAMTError_profile( + except : raise ProfileError( "NoneType can not be computed." "Please provide the right coordinates!.") if easting.size != northing.size : - raise CSex.pyCSAMTError_profile( + raise ProfileError( "Both coordinates array must have the same size." "The first argument size is <{0}>, while the second is <{1}>.". format(easting.size, northing.size)) @@ -1617,14 +1623,14 @@ def compute_dipolelength_from_coords(easting=None, northing=None, reference_ellipsoid =kwargs.pop('reference_ellipsoid', 23) if easting is not None and northing is not None : - assert easting.size == northing.size , CSex.pyCSAMTError_profile( + assert easting.size == northing.size , ProfileError( 'Easting and Northing must have the same size.' ' Easting|northing size are currentlysize is <{0}|{1}>.'.\ format(easting.size, northing.size)) elif latitude is not None and longitude is not None : assert latitude.size == longitude.size , \ - CSex.pyCSAMTError_profile( + ProfileError( 'Latitude and longitude must have the same size.' ' Easting|northing size are currentlysize' ' is <{0}|{1}>.'.format(easting.size, @@ -1634,7 +1640,7 @@ def compute_dipolelength_from_coords(easting=None, northing=None, utm_zone , easting, northing = gis.ll_to_utm(reference_ellipsoid, latitude, longitude) else : - raise CSex.pyCSAMTError_profile('May input at least easting and ' + raise ProfileError('May input at least easting and ' 'northing coordinates values or' ' latitude and longitude values.') diff --git a/pycsamt/utils/decorator.py b/pycsamt/utils/decorator.py index d156f91..59bf19c 100644 --- a/pycsamt/utils/decorator.py +++ b/pycsamt/utils/decorator.py @@ -18,15 +18,15 @@ def new_func (*args, **kwargs): class deprecated(object): """ - Description: - used to mark functions, methods and classes deprecated, - and prints warning message when it called - decorators based on https://stackoverflow.com/a/40301488 + Description: + used to mark functions, methods and classes deprecated, + and prints warning message when it called + decorators based on https://stackoverflow.com/a/40301488 - Usage: - todo: write usage + Usage: + todo: write usage - Date: 20/06/2017 + Date: 20/06/2017 """ def __init__(self, reason): # pragma: no cover if inspect.isclass(reason) or inspect.isfunction(reason): @@ -148,17 +148,17 @@ def _check_gdal_data(self): class deprecated_to(object) : """ - Description: - used to replace deprecated functions or classes. - Deprecated functions or class - should be called others functions or classes. - - Usage: - .. todo:: use new function or class - to replace old function method or class - with multiple parameters. + Description: + used to replace deprecated functions or classes. + Deprecated functions or class + should be called others functions or classes. + + Usage: + .. todo:: use new function or class + to replace old function method or class + with multiple parameters. - Date: 18/10/2020 + Date: 18/10/2020 """ _logger = csamtpylog.get_csamtpy_logger(__name__) diff --git a/pycsamt/utils/exceptions.py b/pycsamt/utils/exceptions.py index 3c65402..8b055bb 100644 --- a/pycsamt/utils/exceptions.py +++ b/pycsamt/utils/exceptions.py @@ -6,135 +6,111 @@ """ #================================================================= -class pyCSAMTError_occam2d_plot(Exception): +class OccamPlotError(Exception): pass -class pyCSAMTError_occam2d(Exception): +class OccamError(Exception): pass -class pyCSAMTError_occam2d_iter2dat(Exception): +class I2datError(Exception): pass -class pyCSAMTError_profile(Exception): +class ProfileError(Exception): pass -class pyCSAMTError_J(Exception): +class JError(Exception): pass -class pyCSAMTError_site(Exception): +class SiteError(Exception): pass -class MTpyError_Z(Exception): +class ZError(Exception): pass -class MTpyError_inputarguments(Exception): +class InputError(Exception): pass -class pyCSAMTError_EDI(Exception): +class EDIError(Exception): pass -class pyCSAMTError_location(Exception): +class LocationError(Exception): pass -class pyCSAMTError_well_building(Exception): +class WellBuildingError(Exception): pass -class pyCSAMTError_float(Exception): - pass -class pyCSAMTError_frequency(Exception): - pass - -class pyCSAMTError_inputarguments(Exception): +class FrequencyError(Exception): pass -class pyCSAMTError_Header(Exception): +class HeaderError(Exception): pass -class pyCSAMTError_value(Exception): - pass -class pyCSAMTError_azimuth(Exception): +class AzimuthError(Exception): pass -class pyCSAMTError_ts_data(Exception): +class TSDataError(Exception): pass -class pyCSAMTError_config_file(Exception): +class ConfigFileError(Exception): pass - -class pyCSAMTError_file_handling(Exception): +class FileHanglingError(Exception): pass -class pyCSAMTError_avg_file(Exception): - pass - -class pyCSAMTError_AvgData(Exception): +class AVGError(Exception): pass -class pyCSAMTError_AVG(Exception): +class DataError(Exception): pass -class pyCSAMTError_plot_tip(Exception): +class PlotTipError(Exception): pass -class pyCSAMTError_plot(Exception): +class PlotError(Exception): pass -class pyCSAMTError_Z(Exception): - pass - - -class pyCSAMTError_Emag(Exception): +class EmagError(Exception): pass -class pyCSAMTError_Hmag(Exception): - pass - -class pyCSAMTError_Ephz(Exception): +class HmagError(Exception): pass -class pyCSAMTError_parameter_number(Exception): +class EphzError(Exception): pass -class pyCSAMTError_processing(Exception): +class ParamNumberError(Exception): pass -class pyCSAMTError_module_import(Exception): +class ProcessingError(Exception): pass -class pyCSAMTError_Phase(Exception): +class PhaseError(Exception): pass -class pyCSAMTError_rho(Exception): +class ResistivityError(Exception): pass -class pyCSAMTError_station(Exception): +class StationError(Exception): pass -class pyCSAMTError_structural(Exception): +class StructuralError(Exception): pass -class pyCSAMTError_strata(Exception): +class StrataError(Exception): pass -class pyCSAMTError_memory(Exception): +class GeoMemoryError(Exception): pass -class pyCSAMTError_plot_geoinputargument(Exception): +class GeoError(Exception): pass -class pyCSAMTError_geodrill_inputarguments(Exception): +class DrillError (Exception): + pass +class SQLError(Exception): pass -class pyCSAMTError_SQL(Exception): - pass -class pyCSAMTError_SQL_manager(Exception): - pass -class pyCSAMTError_SQL_interface(Exception): - pass -class pyCSAMTError_SQL_geoDataBase(Exception): +class DataBaseError(Exception): pass -class pyCSAMTError_SQL_update_geoinformation(Exception): +class ModEMError (Exception): pass - - diff --git a/pycsamt/utils/geo_utils.py b/pycsamt/utils/geo_utils.py index 26c9c4e..6d26743 100644 --- a/pycsamt/utils/geo_utils.py +++ b/pycsamt/utils/geo_utils.py @@ -16,7 +16,9 @@ import pycsamt.utils.func_utils as FU import pycsamt.utils.plot_utils as PU -import pycsamt.utils.exceptions as CSex +from pycsamt.utils.exceptions import ( + FileHanglingError, StrataError + ) from pycsamt.utils._csamtpylog import csamtpylog _logger=csamtpylog.get_csamtpy_logger(__name__) @@ -870,7 +872,7 @@ def set_agso_properties (download_files = True ): warnings.warn(f"Geological structure file {file_r} " f"is missing. {msg_}") _logger.warn( msg_) - raise CSex.pyCSAMTError_file_handling( + raise FileHanglingError( f"No property file {os.path.basename(file_r)!r}" f" is found. {msg}.") for f in __agso: @@ -1482,7 +1484,7 @@ def _assert_model_type(kind): if kind in ['crm', 'resmodel', 'occam', 'rawmodel', '1']: kind= 'crm' if kind not in ('nm', 'crm'): - raise CSex.pyCSAMTError_strata( + raise StrataError( f"Argument kind={kind!r} is wrong! Should be `nm`" "for stratigraphyic model and `crm` for occam2d model. ") return kind diff --git a/pycsamt/utils/gis_tools.py b/pycsamt/utils/gis_tools.py index 509e3e2..367bff8 100644 --- a/pycsamt/utils/gis_tools.py +++ b/pycsamt/utils/gis_tools.py @@ -44,7 +44,7 @@ _logger = csamtpylog.get_csamtpy_logger(__name__) -class GIS_ERROR(Exception): +class GISError(Exception): pass #=============================================== # Make sure lat and lon are in decimal degrees @@ -329,13 +329,13 @@ def project_point_ll2utm(lat, lon, datum='WGS84', utm_zone=None, epsg=None): if isinstance(datum, int): ogrerr = ll_cs.ImportFromEPSG(datum) if ogrerr != OGRERR_NONE: - raise GIS_ERROR("GDAL/osgeo ogr error code: {}".format(ogrerr)) + raise GISError("GDAL/osgeo ogr error code: {}".format(ogrerr)) elif isinstance(datum, str): ogrerr = ll_cs.SetWellKnownGeogCS(datum) if ogrerr != OGRERR_NONE: - raise GIS_ERROR("GDAL/osgeo ogr error code: {}".format(ogrerr)) + raise GISError("GDAL/osgeo ogr error code: {}".format(ogrerr)) else: - raise GIS_ERROR("""datum {0} not understood, needs to be EPSG as int + raise GISError("""datum {0} not understood, needs to be EPSG as int or a well known datum as a string""".format(datum)) # set utm coordinate system @@ -347,7 +347,7 @@ def project_point_ll2utm(lat, lon, datum='WGS84', utm_zone=None, epsg=None): if HAS_GDAL: ogrerr = utm_cs.ImportFromEPSG(epsg) if ogrerr != OGRERR_NONE: - raise GIS_ERROR("GDAL/osgeo ogr error code: {}".format(ogrerr)) + raise GISError("GDAL/osgeo ogr error code: {}".format(ogrerr)) else: pp = pyproj.Proj('+init=EPSG:%d'%(epsg)) # end if @@ -356,7 +356,7 @@ def project_point_ll2utm(lat, lon, datum='WGS84', utm_zone=None, epsg=None): if HAS_GDAL: ogrerr = utm_cs.CopyGeogCSFrom(ll_cs) if ogrerr != OGRERR_NONE: - raise GIS_ERROR("GDAL/osgeo ogr error code: {}".format(ogrerr)) + raise GISError("GDAL/osgeo ogr error code: {}".format(ogrerr)) # end if if utm_zone is None or not isinstance(None, str) or utm_zone.lower() == 'none': # get the UTM zone in the datum coordinate system, otherwise @@ -435,11 +435,11 @@ def project_point_utm2ll(easting, northing, utm_zone, datum='WGS84', epsg=3149): try: easting = float(easting) except ValueError: - raise GIS_ERROR("easting is not a float") + raise GISError("easting is not a float") try: northing = float(northing) except ValueError: - raise GIS_ERROR("northing is not a float") + raise GISError("northing is not a float") if HAS_GDAL: # set utm coordinate system diff --git a/pycsamt/utils/mtcalculator.py b/pycsamt/utils/mtcalculator.py index 526294d..5b20eb4 100644 --- a/pycsamt/utils/mtcalculator.py +++ b/pycsamt/utils/mtcalculator.py @@ -15,7 +15,7 @@ import numpy as np import math, cmath -import pycsamt.utils.exceptions as MTex +#from pycsamt.utils.exceptions import #================================================================= @@ -145,16 +145,16 @@ def make_log_increasing_array(z1_layer, target_depth, n_layers, increment_factor def invertmatrix_incl_errors(inmatrix, inmatrix_err=None): if inmatrix is None: - raise MTex.MTpyError_inputarguments('Matrix must be defined') + raise TypeError('Matrix must be defined') if (inmatrix_err is not None) and (inmatrix.shape != inmatrix_err.shape): - raise MTex.MTpyError_inputarguments('Matrix and err-matrix shapes do not match: %s - %s'%(str(inmatrix.shape), str(inmatrix_err.shape))) + raise TypeError('Matrix and err-matrix shapes do not match: %s - %s'%(str(inmatrix.shape), str(inmatrix_err.shape))) if (inmatrix.shape[-2] != inmatrix.shape[-1]): - raise MTex.MTpyError_inputarguments('Matrices must be square!') + raise TypeError('Matrices must be square!') if (inmatrix_err is not None) and (inmatrix_err.shape[-2] != inmatrix_err.shape[-1]) : - raise MTex.MTpyError_inputarguments('Matrices must be square!') + raise TypeError('Matrices must be square!') dim = inmatrix.shape[-1] @@ -162,14 +162,14 @@ def invertmatrix_incl_errors(inmatrix, inmatrix_err=None): det = np.linalg.det(inmatrix) if det == 0: - raise MTex.MTpyError_inputarguments('Matrix is singular - I cannot invert that!') + raise TypeError('Matrix is singular - I cannot invert that!') inv_matrix = np.zeros_like(inmatrix) if dim != 2: - raise MTex.MTpyError_inputarguments('Only 2D matrices supported yet') + raise TypeError('Only 2D matrices supported yet') inv_matrix = np.linalg.inv(inmatrix) @@ -215,7 +215,7 @@ def rhophi2z(rho, phi, freq): raise except: - raise MTex.MTpyError_inputarguments('ERROR - arguments must be two 2x2 arrays (real)') + raise TypeError('ERROR - arguments must be two 2x2 arrays (real)') z = np.zeros((2,2),'complex') for i in range(2): @@ -471,16 +471,16 @@ def old_z_error2r_phi_error(x,x_error,y, y_error): def rotatematrix_incl_errors(inmatrix, angle, inmatrix_err = None) : if inmatrix is None : - raise MTex.MTpyError_inputarguments('Matrix AND eror matrix must be defined') + raise TypeError('Matrix AND eror matrix must be defined') if (inmatrix_err is not None) and (inmatrix.shape != inmatrix_err.shape): - raise MTex.MTpyError_inputarguments('Matrix and err-matrix shapes do not match: %s - %s'%(str(inmatrix.shape), str(inmatrix_err.shape))) + raise TypeError('Matrix and err-matrix shapes do not match: %s - %s'%(str(inmatrix.shape), str(inmatrix_err.shape))) try: degreeangle = angle % 360 except: - raise MTex.MTpyError_inputarguments('"Angle" must be a valid number (in degrees)') + raise TypeError('"Angle" must be a valid number (in degrees)') phi = math.radians(degreeangle) @@ -525,15 +525,15 @@ def rotatevector_incl_errors(invector, angle, invector_err = None): #check for row or column vector if invector is None : - raise MTex.MTpyError_inputarguments('Vector AND error-vector must be defined') + raise TypeError('Vector AND error-vector must be defined') if (invector_err is not None) and (invector.shape != invector_err.shape): - raise MTex.MTpyError_inputarguments('Vector and errror-vector shapes do not match: %s - %s'%(str(invector.shape), str(invector_err.shape))) + raise TypeError('Vector and errror-vector shapes do not match: %s - %s'%(str(invector.shape), str(invector_err.shape))) try: degreeangle = angle%360 except: - raise MTex.MTpyError_inputarguments('"Angle" must be a valid number (in degrees)') + raise TypeError('"Angle" must be a valid number (in degrees)') phi = math.radians(degreeangle) @@ -570,10 +570,10 @@ def rotatevector_incl_errors(invector, angle, invector_err = None): def multiplymatrices_incl_errors(inmatrix1, inmatrix2, inmatrix1_err = None,inmatrix2_err = None ): if inmatrix1 is None or inmatrix2 is None: - raise MTex.MTpyError_inputarguments('ERROR - two 2x2 arrays needed as input') + raise TypeError('ERROR - two 2x2 arrays needed as input') if inmatrix1.shape != inmatrix2.shape: - raise MTex.MTpyError_inputarguments('ERROR - two 2x2 arrays with same dimensions needed as input') + raise TypeError('ERROR - two 2x2 arrays with same dimensions needed as input') prod = np.array(np.dot( np.matrix(inmatrix1), np.matrix(inmatrix2))) @@ -625,7 +625,7 @@ def reorient_data2D(x_values, y_values, x_sensor_angle = 0 , y_sensor_angle = 90 if len(x_values) != len(y_values): raise except: - raise MTex.MTpyError_inputarguments('ERROR - both input arrays must be of same length') + raise TypeError('ERROR - both input arrays must be of same length') if len(x_values) != len(y_values): l = min(len(x_values) , len(y_values)) @@ -641,7 +641,7 @@ def reorient_data2D(x_values, y_values, x_sensor_angle = 0 , y_sensor_angle = 90 x_angle = math.radians(x_sensor_angle) y_angle = math.radians(y_sensor_angle) except: - raise MTex.MTpyError_inputarguments('ERROR - both angles must be of type int or float') + raise TypeError('ERROR - both angles must be of type int or float') T = np.matrix( [[ np.real(cmath.rect(1,x_angle)), np.imag(cmath.rect(1,x_angle))],[np.real(cmath.rect(1,y_angle)), np.imag(cmath.rect(1,y_angle))]]) @@ -649,7 +649,7 @@ def reorient_data2D(x_values, y_values, x_sensor_angle = 0 , y_sensor_angle = 90 try: new_array = np.dot(in_array, T.I) except: - raise MTex.MTpyError_inputarguments('ERROR - angles must define independent axes to span 2D') + raise TypeError('ERROR - angles must define independent axes to span 2D') #print new_array.shape diff --git a/pycsamt/utils/plot_utils.py b/pycsamt/utils/plot_utils.py index c67a518..b1c9b10 100644 --- a/pycsamt/utils/plot_utils.py +++ b/pycsamt/utils/plot_utils.py @@ -20,7 +20,10 @@ # import matplotlib.cm as cm # import matplotlib.pyplot as plt from pycsamt.utils._p import _sensitive as SB -from pycsamt.utils import exceptions as CSex +from pycsamt.utils.exceptions import ( + PlotError , StationError, PlotTipError, + FrequencyError, ParamNumberError + ) from pycsamt.utils.decorator import ( deprecated, deprecated_to ) @@ -32,7 +35,7 @@ def control_delineate_curve(res_deline =None , phase_deline =None ): :param res_deline: resistivity value to delineate. :type res_deline: float, int, list - :param phase_deline: phase value to delineate. + :param phase_deline: phase value to delineate.s :type phase_deline: float, int, list """ @@ -43,7 +46,7 @@ def control_delineate_curve(res_deline =None , phase_deline =None ): if isinstance(xx_deline, (float, int, str)): try :xx_deline= float(xx_deline) except : - raise CSex.pyCSAMTError_plot( + raise PlotError( 'Value <{0}> to delineate <{1}> is unacceptable.'\ ' Please ckeck your value.'.format( xx_deline, fmt[ii])) @@ -63,7 +66,7 @@ def control_delineate_curve(res_deline =None , phase_deline =None ): for xx in xx_deline] except : - raise CSex.pyCSAMTError_plot( + raise PlotError( 'Value to delineate <{0}> is unacceptable.'\ ' Please ckeck your value.'.format(fmt[ii])) else : @@ -174,7 +177,7 @@ def find_path (path =None, ptol =0.7): :returns: specific path :rtype: str """ - if path is None : raise CSex.pyCSAMTError_plot_tip( + if path is None : raise PlotTipError( 'Can not find path to read.please provided a datapath . ') if path is not None : if os.path.isfile (path) is True : return 'isfile' @@ -214,7 +217,7 @@ def _sid (n_): try : n_=int(n_) except: - raise CSex.pyCSAMTError_station( + raise StationError( f'Station `{n_}` is wrong! Please provided ' 'the right id.') return n_ @@ -228,7 +231,7 @@ def _sid (n_): try: iter(id_) except: - raise CSex.pyCSAMTError_station( + raise StationError( f'No iterable station `{id_}` found.') else: id_= tuple([_sid (iid ) for iid in id_]) @@ -267,7 +270,7 @@ def get_stationid (stations , station_id) : if iid.lower() ==stn.lower() : sid.append(stn) if isinstance(iid, (int, float)): if int(iid) > len(stations): - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'maximum station number is <{0}>.' 'Can not plot beyond this limit.' ' Your stations go to <{1},...,{2}>'.\ @@ -299,7 +302,7 @@ def get_frequency_id (freq_array , frequency_id ): for freqid in frequency_id: if isinstance(freqid ,str) : try : freqid =np.float(freqid) - except : raise CSex.pyCSAMTError_plot_tip( + except : raise PlotTipError( 'Frequency value must be an integer of float not str ' ) if freq_array.min() > freqid > freq_array.max() : warnings.warn ('Can not find the id <{0}>. ' @@ -309,7 +312,7 @@ def get_frequency_id (freq_array , frequency_id ): freq_array==freq_array.min()),freq_array.min(), np.where(freq_array==freq_array.max()), freq_array.max())) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'frequency value <{0}Hz> is out of the range'.format(freqid)) freqID.append(freqid) @@ -321,7 +324,7 @@ def getcloser_frequency(freq_array, frequency_id): try : frequency_id= float(frequency_id) except: - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( f'Frequency must be a float value not {frequency_id}.') else: indexf = np.where(freq_array == frequency_id )[0] @@ -366,7 +369,7 @@ def slice_matrix (base_matrix , freq_array, doi=2000): warnings.warn ('Can not convert <{0}> into float number. '\ 'Depth of investigation value must' ' be float number.'.format(doi)) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Value provided <{0}> must be float' ' number not <{1}>'.format(doi, type(doi))) else : @@ -412,7 +415,7 @@ def delineate_curve ( dict_loc , value , atol=0.2, replace_value = np.nan): except: warnings.warn('Delineation value is float ' 'number not <{0}>'.format(type(value))) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Input delineation value is unacceptable.' ' Please enter a float number.') else : value =[value] @@ -424,7 +427,7 @@ def delineate_curve ( dict_loc , value , atol=0.2, replace_value = np.nan): except : warnings.warn('Delineation value is' ' float number not <{0}>'.format(type(value))) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Input delineation value is ' 'unacceptable. Please enter a float number.') try : @@ -432,14 +435,14 @@ def delineate_curve ( dict_loc , value , atol=0.2, replace_value = np.nan): if atol > 1 or atol <0: warnings.warn ('Tolerance value is <=1 (less or egal than 1). ' 'Please enter again a new value.') - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Tolerance value is assumed to be' ' 0<= atol <=1 .Please check your value.') except : warnings.warn('Tolerance value <{0}> provided is wrong.' ' Must be a float number 0<= atol <=1 ' ' not a <{1}>'.format(atol, type(value))) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Unacceptable type tolerance value.' ' Please enter a float number 0<= atol <=1.') @@ -519,7 +522,7 @@ def resetting_ticks ( get_xyticks, number_of_ticks=None ): if not isinstance(get_xyticks, (list, np.ndarray) ): warnings.warn ('Arguments get_xyticks must be a list' ' not <{0}>.'.format(type(get_xyticks))) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( '<{0}> found. "get_xyticks" must be a list' ' or (nd.array,1).'.format(type(get_xyticks))) @@ -535,7 +538,7 @@ def resetting_ticks ( get_xyticks, number_of_ticks=None ): ' is the times to see the ticks on x|y axis.'\ ' Must be integer not <{0}>.'.format( type(number_of_ticks))) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( '<{0}> detected. Must be integer.') number_of_ticks=int(number_of_ticks) @@ -591,7 +594,7 @@ def round_modulo10(value): ' is the times to see the ticks on x|y axis.'\ ' Must be integer not <{0}>.'.format( type(number_of_ticks))) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( '<{0}> detected. Must be integer.') number_of_ticks=int(number_of_ticks) @@ -669,7 +672,7 @@ def depth_of_investigation(doi): warnings.warn ('Can not convert <{0}> into float number. ' 'Depth of investigation ' 'value must be float number.'.format(doi)) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Value provided <{0}> must be float' ' number not <{1}>'.format(doi, type(doi))) else : @@ -696,13 +699,13 @@ def depth_of_investigation(doi): warnings.warn ('Provide minimal offset is out of the range ' '!Offset minimal for plottig is ={0}'.format( offset_start)) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Povided minimal offset out'' of the range ' '!Offset minimal for plottig is ={0}'.format(offset_start)) if offset_max > offset_end : warnings.warn ('Povided maximal offset is out of the range ' '!Offset maximal for plottig is ={0}'.format(offset_end)) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Povided maximal offset is out of the range !Offset' ' maximal for plottig is ={0}'.format(offset_end)) @@ -710,7 +713,7 @@ def depth_of_investigation(doi): mess ='depth <{0}> is out of depth range. Maximum depth for'\ ' is={1}.'.format(doi, depth_offsets.max()) warnings.warn(mess) - raise CSex.pyCSAMTError_plot_tip(mess) + raise PlotTipError(mess) # make sure that station offset and and depth are not in decrease oder sflip,dflip=0,0 if depth_offsets[0] > depth_offsets[-1] : @@ -829,7 +832,7 @@ def controle_delineate_curve(res_deline =None , phase_deline =None ): if xx_deline is not None : if isinstance(xx_deline, (float, int, str)): try :xx_deline= float(xx_deline) - except : raise CSex.pyCSAMTError_plot_tip( + except : raise PlotTipError( 'Value <{0}> to delineate <{1}> is unacceptable.' ' Please ckeck your value.'.format(xx_deline, fmt[ii])) @@ -845,7 +848,7 @@ def controle_delineate_curve(res_deline =None , phase_deline =None ): elif ii ==1 : xx_deline = [np.ceil(float(xx)) for xx in xx_deline] - except : raise CSex.pyCSAMTError_plot_tip( + except : raise PlotTipError( 'Value to delineate <{0}> is unacceptable.' ' Please ckeck your value.'.format(fmt[ii])) else : return xx_deline @@ -874,7 +877,7 @@ def depth_of_investigation(doi): warnings.warn ('Can not convert <{0}> into float number. ' 'Depth of investigation value must be float' ' number.'.format(doi)) - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Value provided <{0}> must be float number' ' not <{1}>'.format(doi, type(doi))) else : @@ -1259,12 +1262,12 @@ def find_local_maxima_minima (array): ' minmum and maximum must be an aray of float numbers.' if isinstance(array, (list, tuple)): # put on list try : array=np.array([float(ss) for ss in array]) - except : raise CSex.pyCSAMTError_parameter_number(error_mess) + except : raise ParamNumberError(error_mess) if isinstance(array, (float, str, int)): try : array =np.array([float(array)]) - except : CSex.pyCSAMTError_inputarguments(error_mess) + except : raise TypeError(error_mess) if len(array) == 1 : return (np.array([0]), array ) @@ -1462,18 +1465,18 @@ def average_rho_in_deep (dep_array, rho_array, step_descent) : try : step_descent =float(step_descent) except : - raise CSex.pyCSAMTError_plot_geoinputargument( + raise TypeError( 'Could not convert depth value ={} to float.' ' Please check your value.'.format(step_descent)) if step_descent < dep_array.min(): - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Value provided ={0} m is less than the minimum depth' ' ={1} m.'.format(step_descent, dep_array.min())) if step_descent > dep_array.max(): - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Value provided is = {0} m is greater than maximum' ' depth ={1}m.'.format(step_descent, dep_array.max())) @@ -1531,13 +1534,13 @@ def get_station_id_input_resistivities(station_rho_value, try : number_of_layer =int(number_of_layer[0]) except : - raise CSex.pyCSAMTError_plot_tip( + raise PlotTipError( 'Could not convert number of layer ={0} value' ' into integer.'.format(tuple(number_of_layer))) if isinstance(number_of_layer, (float, str)): try : int(number_of_layer) - except : CSex.pyCSAMTError_plot_tip( + except : PlotTipError( 'Could not convert number of layer ={} ' 'to integer.'.format(number_of_layer)) else : number_of_layer=int(number_of_layer) @@ -1548,7 +1551,7 @@ def get_station_id_input_resistivities(station_rho_value, if station_rho_value.dtype not in ['float', 'int']: station_rho_value= station_rho_value.astype('float64') - except : raise CSex.pyCSAMTError_plot_geoinputargument( + except : raise TypeError( 'Could not convert resistivities values ' 'to float number. Please provide a right values.') @@ -1587,7 +1590,7 @@ def build_resistivity_barplot(depth_values , res_values): ]) if len(depth_values) !=len(res_values) : warnings.warn(mess) - raise CSex.pyCSAMTError_geodrill_inputarguments( + raise TypeError( 'Error size. Depth and resistivity must have the same length.') # lop the resistivities array @@ -1669,7 +1672,7 @@ def annotate_tip(layer_thickness , layer_names): mess = 'None value is provided, Please specify at least '\ 'the thickness of one truth layer.' warnings.warn(mess) - raise CSex.pyCSAMTError_plot_tip(mess) + raise PlotTipError(mess) top = layer_thickness[0] bottom = layer_thickness[1] @@ -1846,4 +1849,4 @@ def plot_errorbar(ax, x_array, y_array, y_error=None, x_error=None, - \ No newline at end of file + diff --git a/pycsamt/utils/zcalculator.py b/pycsamt/utils/zcalculator.py index 5fc5945..db47582 100644 --- a/pycsamt/utils/zcalculator.py +++ b/pycsamt/utils/zcalculator.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Created on Thu Dec 3 16:44:29 2020 # Author: Kouadio K.Laurent -# Licence: LGPL +# Licence: GPL """ .. _module-ZCalculator::`pycsamt.utils.zcalculator` @@ -12,11 +12,18 @@ import numpy as np import pycsamt.utils._p as infos -from pycsamt.utils import exceptions as CSex from pycsamt.utils import func_utils as func from pycsamt.utils.decorator import (deprecated, deprecated_to) from pycsamt._csamtpylog import csamtpylog - +from pycsamt.utils.exceptions import ( + ZError , + EmagError, + FrequencyError, + ProcessingError, + ParamNumberError, + AVGError, + StationError + ) _logger =csamtpylog.get_csamtpy_logger(__name__) try: import scipy @@ -62,7 +69,7 @@ def mag_avg (mag_array , A_spacing, Txcur): try : mag_avg =np.array([float(ii) for ii in mag_array]) except : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Input first arguments is wrong. must" "be a list of ndarray of numbers.") @@ -89,7 +96,7 @@ def phz_avg (phz_array, to_degree): try : phz_array =[float(kk) for kk in phz_array ] except : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "input argument must be a number in array or list.") if to_degree : @@ -108,7 +115,7 @@ def param_rho (rho_array): try : phz_array =np.array([float(kk) for kk in rho_array ]) except : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Error inputs arguments . must be list or ndarray of numbers.') return phz_array.mean() @@ -126,7 +133,7 @@ def param_phz (pphz_array, to_degree =False): try : pphz_array =np.array([float(kk) for kk in pphz_array ]) except : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Error inputs arguments. must be list or ndarray of numbers.') if to_degree : return (pphz_array.mean()/1000) *180/np.pi, (pphz_array/1000) *180/np.pi @@ -164,12 +171,12 @@ def comp_rho (mag_E_field, mag_H_field, freq_array, A_spacing, Txcurr ): try : a_args =np.array([ float(ss) for ss in a_args ]) except : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Elemts composed of each array must be a number.") # if comp_rho.__code__.co_argcount ==5 : # if mag_E_field.ndim !=1 or mag_H_field.ndim !=1 or freq_array.ndim !=1 : - # raise CSex.pyCSAMTError_inputarguments + # raise TypeError # ("dimension of Input arguments must me equal to 1.") Emag =mag_E_field /(A_spacing * Txcurr) @@ -204,13 +211,13 @@ def comp_phz (comphz_array, units ='deg'): if type(comphz_array) is list : comphz_array=np.array(comphz_array) if comphz_array.dtype not in ["float", "int"] : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( "Type provided is wrong ! number must be float") try : comphz_array=np.array([float(kk) for kk in comphz_array ]) except : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'Error inputs arguments. must be list or ndarray of numbers.') @@ -250,7 +257,7 @@ def compute_components_Z_Phz(magn_E_field , magn_H_field, phz_E_field, Raises ------ - CSex.pyCSAMTError_z(), + ZError(), Exceptions if units entered by the user doesnt match or are messy. Returns @@ -319,14 +326,14 @@ def compute_components_Z_Phz(magn_E_field , magn_H_field, phz_E_field, if keys.lower() in values.keys(): magn_E_field = magn_E_field * values[keys.lower()] else : - raise CSex.pyCSAMTError_Emag( + raise EmagError( "Input units is not valid. try : {0}".format( list(values.keys()))) if keys.lower() ==units_H_field.lower() : if keys.lower() in values.keys(): magn_H_field= magn_H_field* values[keys.lower()] else : - raise CSex.pyCSAMTError_Emag( + raise EmagError( "Input units is not valid. try : {0}".format( list(values.keys()))) @@ -336,14 +343,14 @@ def compute_components_Z_Phz(magn_E_field , magn_H_field, phz_E_field, phz_E_field = phz_E_field* values[keys.lower()] phz_H_field = phz_H_field * values[keys.lower()] else : - raise CSex.pyCSAMTError_Emag( + raise EmagError( "Input units is not valid. try : {0}".format( list(values.keys()))) if keys.lower() ==unit_freq.lower(): if keys.lower() in values.keys(): freq_value= freq_value * values[keys.lower()] else : - raise CSex.pyCSAMTError_Emag( + raise EmagError( "Input units is not valid. try : {0}".format( list(values.keys()))) @@ -477,7 +484,7 @@ def get_reffreq_index(freq_array, reffreq_value): """ freq_array=np.array([float(ss) for ss in freq_array]) if float(reffreq_value) not in freq_array : - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( 'Reference frequency must be a value of frequency array.') for ii, freq in enumerate(freq_array): @@ -594,7 +601,7 @@ def find_reference_frequency(freq_array =None, reffreq_value =None , if type(reffreq_value) is not int or type(reffreq_value) is not float: try : reffreq_value= np.int(reffreq_value) except: - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( "Reference frequency must be either float value or integer.") if freq_array.min() > reffreq_value > freq_array.max(): @@ -602,7 +609,7 @@ def find_reference_frequency(freq_array =None, reffreq_value =None , 'range. see more infos about reference' ' frequency.|{0}|'.format( infos.notion.reference_frequency)) - raise CSex.pyCSAMTError_frequency ( + raise FrequencyError ( "Input reference frequency is out the frequency range. " "Please put value between the frequency range. " "Frequency range is [{0} Hz to {1} Hz].".format( @@ -692,7 +699,7 @@ def get_data_from_reference_frequency(array_loc, freq_array, reffreq_value): if type(reffreq_value) is str : try: reffreq_value=np.float(reffreq_value) except: - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( "Reference value must be float or int value, not str!") #find the size of array loc : seem to be the size of stationsNames . stn_size =np.array(list(array_loc.keys())).size @@ -717,7 +724,7 @@ def get_data_from_reference_frequency(array_loc, freq_array, reffreq_value): reffreq_value=reffreq_value, stnNames=sorted(array_loc.keys())) if reffreq_array is None : - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( 'Something wrong happened during reference array computing.' ' Please check the your inputs data size, and your station size.') return reffreq_array @@ -789,7 +796,7 @@ def hanning (dipole_length=50., number_of_points=7, large_band=False): dipole_length = np.float(dipole_length) number_of_points = np.int(np.floor(number_of_points)) except : - raise CSex.pyCSAMTError_processing('Dipole length and number of'\ + raise ProcessingError('Dipole length and number of'\ ' points must be a float number.') window_width = number_of_points * dipole_length @@ -849,7 +856,7 @@ def weight_beta (dipole_length =50. , number_of_points = 7, window_width=None): warnings.warn('Please see more ' 'info about coeff.Beta :<{0}>'.format(infos.notion.weighted_Beta)) - raise CSex.pyCSAMTError_processing('Window width type must be float number.') + raise ProcessingError('Window width type must be float number.') xk = window_width / 2 num_pk =np.arange(dipole_length/2, window_width , dipole_length) @@ -877,16 +884,16 @@ def compute_adaptative_moving_average ( z_array =None , weighted_window=None , """ if z_array is None : - raise CSex.pyCSAMTError_Z('NoneType Impedance can not be computed.') + raise ZError('NoneType Impedance can not be computed.') if weighted_window is None : if dipole_length is None or number_of_points is None : - raise CSex.pyCSAMTError_inputarguments('Please check ' + raise TypeError('Please check ' 'your Inputs values ! NoneType values of dipole length ' ' or number_of_stations can not be computed.!') try : dipole_length , number_of_points = np.float(dipole_length) , np.int(number_of_points) except : - raise CSex.pyCSAMTError_float( + raise TypeError( 'Dipole length, number of points must be a float, and ' ' int number respectively.!') @@ -1015,7 +1022,7 @@ def see_extraband( apply_on_half =False): return np.array(hv) if (x_point_value - dipole_length/2 < 0 or x_point_value +dipole_length/2 > window_width): - raise CSex.pyCSAMTError_processing('Hanning x point value is outside the window.') + raise ProcessingError('Hanning x point value is outside the window.') if bandwidth_values is True : if on_half :return see_extraband(apply_on_half=True) @@ -1184,10 +1191,10 @@ def compute_FLMA ( z_array =None , weighted_window=None , #reference_freq= kwargs.pop('reference_freq', 8192.) if z_array is None : - raise CSex.pyCSAMTError_Z('NoneType Impedance can not be computed.') + raise ZError('NoneType Impedance can not be computed.') if weighted_window is None : if dipole_length is None or number_of_points is None : - raise CSex.pyCSAMTError_inputarguments('Please check ' + raise TypeError('Please check ' 'your Inputs values ! NoneType values of dipole length ' ' or number_of_stations can not be computed.!') try : @@ -1195,7 +1202,7 @@ def compute_FLMA ( z_array =None , weighted_window=None , number_of_points= np.int(number_of_points) except : - raise CSex.pyCSAMTError_float( + raise TypeError( 'Dipole length, number of points must be a float, and ' ' int number respectively.!') @@ -1287,19 +1294,19 @@ def compute_TMA (data_array=None, number_of_TMApoints=5. ): """ if data_array is None : - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'NoneType arguments can not be computed!.') if data_array.dtype not in ['float', 'int']: try :data_array=np.array([float(dd) for dd in data_array]) except : - raise CSex.pyCSAMTError_float( + raise TypeError( 'Data must be on array_like float number.!') if type(number_of_TMApoints) is not int : try : number_of_TMApoints=np.int(number_of_TMApoints) except : - raise CSex.pyCSAMTError_parameter_number( + raise ParamNumberError( 'TMA filter point must be integer.') roll_TMA =[] @@ -1422,18 +1429,18 @@ def compute_trimming_moving_average (data_array=None, number_of_TMApoints=None ) """ if data_array is None or number_of_TMApoints is None : - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'NoneType arguments can not be computed!.') if data_array.dtype not in ['float', 'int']: try :data_array=np.array([float(dd) for dd in data_array]) except : - raise CSex.pyCSAMTError_AvgData( + raise AVGError( 'Data must be on array_like float number.!') if type(number_of_TMApoints) is not int : try : number_of_TMApoints=np.int(number_of_TMApoints) except : - raise CSex.pyCSAMTError_parameter_number( + raise ParamNumberError( 'TMA filter point must be integer.') roll_TMA,window =[], np.int(np.trunc(number_of_TMApoints/2)), @@ -1524,7 +1531,7 @@ def compute_AMA(reference_freq=None, z_array =None, """ if reference_freq is None : - raise CSex.pyCSAMTError_frequency( + raise FrequencyError( 'Please add your reference frequency!') mu0 = 4* np.pi * 1e-7 @@ -1538,7 +1545,7 @@ def compute_AMA(reference_freq=None, z_array =None, if z_array is None : warnings.warn('`Impedance` is None. could not compute' ' `apparent resistivities`') - raise CSex.pyCSAMTError_processing('`Impedance` is None.' + raise ProcessingError('`Impedance` is None.' 'Could not compute `resistivities`.' ' Please provided at least an `impedance array`.') @@ -1549,7 +1556,7 @@ def compute_AMA(reference_freq=None, z_array =None, if phase is None and reference_freq is None: warnings.warn('Could not compute `impedance` without phase values' ' and reference frequency!') - raise CSex.pyCSAMTError_processing( + raise ProcessingError( 'None Type could not be computed. ' 'Please provided phase values |reference frequency') @@ -1743,7 +1750,7 @@ def get_array_from_reffreq ( array_loc, freq_array,reffreq_value, """ if stnNames is None : - raise CSex.pyCSAMTError_station('You may at least specify ' + raise StationError('You may at least specify ' 'the array or list of stations-Names or station id.') arrayObj,freqObj=array_loc ,freq_array @@ -1791,13 +1798,13 @@ def relocate_on_dict_arrays(data_array, number_of_frequency, if station_names is not None : if type(station_names) is list : assert len(station_names) == (data_array.size / number_of_frequency),\ - CSex.pyCSAMTError_station( + StationError( 'Number of Station provided must be <{0}> not' ' <{1}>'.format(np.int(data_array.size / number_of_frequency), len(station_names))) else : assert station_names.size == data_array.size / number_of_frequency, \ - CSex.pyCSAMTError_station( + StationError( 'Number of Station provided must be <{0}>' ' not <{1}>'.format(np.int(data_array.size / number_of_frequency), station_names.size)) @@ -1830,10 +1837,10 @@ def dipole_center_position (dipole_position=None): """ if dipole_position is None : - raise CSex.pyCSAMTError_inputarguments( + raise TypeError( 'NoneType can not be compute. Please provide the right values.') try : dipole_position= np.array([float(pp) for pp in dipole_position]) - except : raise CSex.pyCSAMTError_float( + except : raise TypeError( 'Cannot convert position values. Position must be a' ' number not str.Please check your data!.') # temp_pospp =[(dipole_position[pp]+dipole_position[pp-1])/2 for \ @@ -1877,7 +1884,7 @@ def get_matrix_from_dict(dict_array, flip_freq =False , """ if not isinstance(dict_array, dict): - raise CSex.pyCSAMTError_processing('Main argument `dict array`' + raise ProcessingError('Main argument `dict array`' ' should be dictionnary' ', not {0}'.type(dict_array)) @@ -1887,7 +1894,7 @@ def get_matrix_from_dict(dict_array, flip_freq =False , try : freq_array=freq_array.astype('float') except : - raise CSex.pyCSAMTError_float( + raise TypeError( 'Could not convert array to value !') # now check the order if freq_array[0] < freq_array[-1]: @@ -2031,4 +2038,4 @@ def truncated_data (data, number_of_reccurence, **kwargs): new_resistivities = np.abs(znew) ** 2 / (4* np.pi*1e-7 * 2* np.pi * reference_frequency ) print(new_resistivities) - \ No newline at end of file + diff --git a/pycsamt/view/__init__.py b/pycsamt/view/__init__.py index a26187f..a405bfa 100644 --- a/pycsamt/view/__init__.py +++ b/pycsamt/view/__init__.py @@ -8,4 +8,12 @@ PlotSlices ) - +__all__=[ + "Plot1d", + "Plot2d", + "plot_dataAndFits", + "PlotPTMaps", + "PlotRMSMaps", + "PlotResponse", + "PlotSlices" + ] diff --git a/pycsamt/view/plot.py b/pycsamt/view/plot.py index 15aad29..6e24315 100644 --- a/pycsamt/view/plot.py +++ b/pycsamt/view/plot.py @@ -32,7 +32,6 @@ avg as CSMATavg , get_ediObjs ) -import pycsamt.utils.exceptions as CSex import pycsamt.utils.func_utils as func import pycsamt.utils.plot_utils as mplotus import pycsamt.utils.zcalculator as Zcc @@ -44,7 +43,7 @@ from pycsamt.utils._p import suit from pycsamt.processing import Processing from pycsamt._csamtpylog import csamtpylog - +from pycsamt.utils.exceptions import PlotError try : from pycsamt.__init__ import imtpy if imtpy : @@ -938,7 +937,7 @@ def plot_topo_sep_azim(self,fn = None , profile_fn=None , elif profile_fn is None and fn is None : - raise CSex.pyCSAMTError_plot( + raise PlotError( 'None path is found. Please spceify you data path.') @@ -1055,7 +1054,7 @@ def plot_azimuth(axes =None, set_xlabel=True): if suit.topography[0].find(plot_type.lower())< 0 : if suit.azimuth[0].find(plot_type.lower()) < 0 : if plot_type.lower() not in ['1','2','3','123','*']: - raise CSex.pyCSAMTError_plot( + raise PlotError( 'Argument provided for plot_type is not acceptable.' ' Please use 1,2,3 or the first ' 'two letter of "Topography, Separation|Station ' @@ -1063,7 +1062,7 @@ def plot_azimuth(axes =None, set_xlabel=True): elif isinstance(plot_type, int): if plot_type not in [1,2,3,123]: - raise CSex.pyCSAMTError_plot( + raise PlotError( 'Only 1|2|3 is need to plot. Try again !') plot_type=str(plot_type) @@ -1091,7 +1090,7 @@ def plot_azimuth(axes =None, set_xlabel=True): if set_stnnames ==True : if add_stnnames is not None : if len (add_stnnames) != station_pk.size : - raise CSex.pyCSAMTError_station( + raise PlotError( 'Stations names provided must' ' be the same length as survey points.' ' Number of survey points are' @@ -1141,7 +1140,7 @@ def plot_azimuth(axes =None, set_xlabel=True): if set_stnnames : if add_stnnames is not None : if len (add_stnnames) != station_pk.size : - raise CSex.pyCSAMTError_station( + raise PlotError( 'Stations names provided must' ' be the same length as survey points.' ' Number of survey points are' @@ -1262,7 +1261,7 @@ def plot_static_correction(self, data_fn, profile_fn =None , 'trimming moving-average (TMA).' ' and fixed-length-moving-average(FLMA)' 'Please Try to use the availabe filters.') - raise CSex.pyCSAMTError_plot( + raise PlotError( 'Input filter <{0}> is not available.' ' Please use `tma` or `flma` filters!') @@ -1702,7 +1701,7 @@ def retreive_single_siteOrFreqdata(stnOrFreq): f'frequency range ={freq_array}.') self._logging.debug( f'{stnOrFreq} not found in frequency range.') - raise CSex.pyCSAMTError_frequency( + raise TypeError( 'frequency must be a float ' 'number not `{}`.'.format(type(stnOrFreq))) else: @@ -1807,11 +1806,11 @@ def plot_freqVSRhoPhase (self, fn = None , profile_fn =None , csamt_obj = CSAMT(data_fn = fn , profile_fn = profile_fn ) if csamt_obj.freq is None : - raise CSex.pyCSAMTError_plot('Need absolutely frequency' + raise PlotError('Need absolutely frequency' ' value before plotting.' 'Please add your frequency data.') if csamt_obj.resistivity is None : - raise CSex.pyCSAMTError_plot( + raise PlotError( 'Error plotting.Provide your resistivity values.') #--- assert stations length ------ @@ -1823,7 +1822,7 @@ def plot_freqVSRhoPhase (self, fn = None , profile_fn =None , ' Please provided new stations ' 'list with the same length.'.format( len(rename_stations), len(stations))) - raise CSex.pyCSAMTError_station( + raise PlotError( 'New stations provided ' 'must have the same length with default stations. ') stations = rename_stations @@ -2068,7 +2067,7 @@ def depth1D (freq_selected): axis2.set_xticks(ticks= csamt_stndis_obj, minor=False ) if rename_station is not None : assert len(rename_station)==len(csamt_stn_num_obj),\ - CSex.pyCSAMTError_plot("Error plot !rename_station and station" + PlotError("Error plot !rename_station and station" " name must have the same lenght.") csamt_stn_num_obj= rename_station @@ -2507,7 +2506,7 @@ def plotRMS(self, fn=None ,target =1. ,savefig =None, **kwargs): ' Can not plot "None" value.') self._logging.warn( 'Error plotting R.M.S VS Iteration. Can not plot "None" value.') - raise CSex.pyCSAMTError_plot( + raise PlotError( 'Error Plot R.M.S . ' 'Compulsory need R.M.S value and ' 'Iteration value. Please check your value. ') @@ -2825,7 +2824,7 @@ def plot_multiStations(self, X =None , Y=None, path =None, warnings.warn(mess.format(len(self.Y),len(self.Y))) self._logging.error(mess.format(len(self.Y),len(self.Y))) - raise CSex.pyCSAMTError_plot(mess.format(len(self.Y), + raise PlotError(mess.format(len(self.Y), len(self.Y))) if isinstance(self.X, np.ndarray) and isinstance(self.Y, np.ndarray): #case where user profide only one line with easting and northings @@ -2857,7 +2856,7 @@ def plot_multiStations(self, X =None , Y=None, path =None, 'or provide X and Y as lists of coordinates values.']) self._logging.error(msg) warnings.warn(msg) - raise CSex.pyCSAMTError_profile(msg) + raise PlotError(msg) print('---> {0:02} *station profiles* detected !'.format( len(profiles_path))) @@ -3593,7 +3592,7 @@ def penetration2D(self, fn=None , profile_fn =None, if stnnames is not None : assert len(stnnames ) ==len(csamt_stn_obj),\ - CSex.pyCSAMTError_station('Station provided must have the ' + PlotError('Station provided must have the ' 'same lenght with default stations.') csamt_stn_obj = stnnames @@ -4094,7 +4093,7 @@ def plot_occam2dModel(self, model_fn =None, iter_fn=None , warnings.warn( 'Iteration, mesh , data files are essential for plotting.'+ mess) self._logging.error - raise CSex.pyCSAMTError_occam2d_plot(mess) + raise PlotError(mess) # scaled data and x values plots if depth_scale is not None : self.depth_scale= str(depth_scale).lower() @@ -5006,7 +5005,7 @@ def plot_Pseudolog(self, station_id= 'S00', iter_fn=None , warnings.warn(mess) self._logging.error(mess) - raise CSex.pyCSAMTError_plot_geoinputargument(mess) + raise PlotError(mess) #----Finish ascertainement then build object and read ---- elif p==2 or f==4 : if f==4 : # priority to Occam2D data files @@ -5599,7 +5598,7 @@ def plot_dataAndFits(data_fn =None, stations =None, **kws): # manage station and create duplicate list if data_fn is None: - raise CSex.pyCSAMTError_AVG( + raise TypeError( 'No `avg` file detected. Please provide a file.') elif isinstance(data_fn, str): data_fn = [data_fn] @@ -6181,4 +6180,4 @@ def __init__(self, model_fn, data_fn=None, **kws): - \ No newline at end of file +