From fbdd2b0b401d183ae26ee44ca687adf3da96f1fd Mon Sep 17 00:00:00 2001 From: thurinj Date: Thu, 23 Oct 2025 22:51:15 +0900 Subject: [PATCH 1/8] Patched **kwargs inconsistencies This should solve issue #332. --- mtuq/graphics/header.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mtuq/graphics/header.py b/mtuq/graphics/header.py index 6e6f5db0..d85ce36f 100644 --- a/mtuq/graphics/header.py +++ b/mtuq/graphics/header.py @@ -543,8 +543,9 @@ def create_moment_tensor_header(process_bw, process_sw, misfit_bw, misfit_sw, best_misfit_bw, best_misfit_sw, model, solver, mt, lune_dict, origin, data_bw=None, data_sw=None, mt_grid=None, event_name=None, **kwargs): """Create a complete moment tensor header""" + process_sw_supp = kwargs.pop('process_sw_supp', None) header_info = prepare_moment_tensor_header_info( - origin, mt, lune_dict, process_bw, process_sw, kwargs.get('process_sw_supp', None), + origin, mt, lune_dict, process_bw, process_sw, process_sw_supp, misfit_bw, misfit_sw, best_misfit_bw, best_misfit_sw, model, solver, data_bw=data_bw, data_sw=data_sw, mt_grid=mt_grid, event_name=event_name, **kwargs) header = build_moment_tensor_header(header_info) @@ -555,8 +556,9 @@ def create_force_header(process_bw, process_sw, misfit_bw, misfit_sw, best_misfit_bw, best_misfit_sw, model, solver, force, force_dict, origin, data_bw=None, data_sw=None, force_grid=None, event_name=None, **kwargs): """Create a complete force header""" + process_sw_supp = kwargs.pop('process_sw_supp', None) header_info = prepare_force_header_info( - origin, force, force_dict, process_bw, process_sw, kwargs.get('process_sw_supp', None), + origin, force, force_dict, process_bw, process_sw, process_sw_supp, misfit_bw, misfit_sw, best_misfit_bw, best_misfit_sw, model, solver, data_bw=data_bw, data_sw=data_sw, force_grid=force_grid, event_name=event_name, **kwargs) header = build_force_header(header_info) From 4bcccd80219b1474395ebef6fc13d8cdd6aa118a Mon Sep 17 00:00:00 2001 From: thurinj Date: Mon, 27 Oct 2025 16:44:47 +0900 Subject: [PATCH 2/8] Added rescaling mecanism for line to avoid line clipping out of header in waveform figures. This triggers per-line (only the line that would overflow is rescaled). I tested it on the mtuq examples and it looks okay to me. Also tested with plot_data_greens3 and everything looks fine as well. I turned it on by default, but we could change that if needed. --- mtuq/graphics/header.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/mtuq/graphics/header.py b/mtuq/graphics/header.py index d85ce36f..ade03ff2 100644 --- a/mtuq/graphics/header.py +++ b/mtuq/graphics/header.py @@ -10,6 +10,10 @@ from matplotlib import pyplot from matplotlib.font_manager import FontProperties +try: + from matplotlib.textpath import TextPath +except ImportError: + TextPath = None from mtuq.graphics.beachball import plot_beachball, _plot_beachball_matplotlib from mtuq.graphics._pygmt import exists_pygmt, plot_force_pygmt from mtuq.graphics._matplotlib import plot_force_matplotlib @@ -38,6 +42,8 @@ class HeaderStyle: text_left_margin: float = HEADER_TEXT_LEFT_MARGIN font_size: int = HEADER_TEXT_FONT_SIZE text_vspace: float = HEADER_TEXT_VSPACE + text_right_margin: float = HEADER_TEXT_LEFT_MARGIN + min_font_size: int = 10 @dataclass @@ -98,12 +104,15 @@ class TextBlock(HeaderBlock): """A text block for displaying formatted text in headers.""" def __init__(self, text: str, fontsize=HEADER_TEXT_FONT_SIZE, bold=False, - italic=False, vspace=HEADER_TEXT_VSPACE, **kwargs): + italic=False, vspace=HEADER_TEXT_VSPACE, auto_shrink=True, + min_fontsize=None, **kwargs): self.text = text self.fontsize = fontsize self.bold = bold self.italic = italic self.vspace = vspace + self.auto_shrink = auto_shrink + self.min_fontsize = min_fontsize self.kwargs = kwargs def render(self, ax, info, px, py, *, height: float, width: float, @@ -118,9 +127,30 @@ def render(self, ax, info, px, py, *, height: float, width: float, fontsize = self.fontsize or style.font_size vspace = self.vspace or style.text_vspace + text_value = self.text.format(**info.__dict__) + + # Compute available width in axis units and shrink fonts if required + max_width = max(width - px - style.text_right_margin * height, 0.) + target_min = self.min_fontsize if self.min_fontsize is not None else style.min_font_size + + if self.auto_shrink and max_width > 0 and TextPath is not None: + font_for_metrics = font.copy() + font_for_metrics.set_size(fontsize) + try: + text_path = TextPath((0, 0), text_value, prop=font_for_metrics) + text_width = text_path.get_extents().width / 72.0 + except Exception: + text_width = 0.0 + + if text_width > max_width and text_width > 0.0: + scale = max_width / text_width + fontsize = max(target_min, fontsize * scale) + + font.set_size(fontsize) + # Use attribute access for info container - ax.text(px, py, self.text.format(**info.__dict__), fontproperties=font, - fontsize=fontsize, **self.kwargs) + ax.text(px, py, text_value, fontproperties=font, fontsize=fontsize, + **self.kwargs) return py - vspace From dcb51f5582063816a3dc31e6d587522f6ffcf489 Mon Sep 17 00:00:00 2001 From: thurinj Date: Tue, 9 Dec 2025 10:12:18 +0900 Subject: [PATCH 3/8] Update github workflow to fix HDF5 issue on recent tests. --- .github/workflows/python-app.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index f8847e84..8a9ae976 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -21,6 +21,10 @@ jobs: run: sudo apt-get update && sudo apt-get install -y libfabric-bin - uses: actions/checkout@v2 + + - name: Install HDF5 headers + run: sudo apt-get update && sudo apt-get install -y libhdf5-dev + - name: Set up Python 3.9 uses: actions/setup-python@v2 with: From 8a9169d5f2a456be43bf343ffe76a5b1bda50628 Mon Sep 17 00:00:00 2001 From: thurinj Date: Tue, 9 Dec 2025 10:17:10 +0900 Subject: [PATCH 4/8] Updated to the master version of the python-app workflow file. --- .github/workflows/python-app.yaml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index 8a9ae976..0051bd59 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -21,14 +21,8 @@ jobs: run: sudo apt-get update && sudo apt-get install -y libfabric-bin - uses: actions/checkout@v2 - - - name: Install HDF5 headers - run: sudo apt-get update && sudo apt-get install -y libhdf5-dev - - - name: Set up Python 3.9 + - name: Set up Python 3 uses: actions/setup-python@v2 - with: - python-version: 3.9 - name: pip installation # test pip installation first (much faster than conda) @@ -38,27 +32,26 @@ jobs: - name: Basic tests run: | python tests/check_import.py - python tests/check_entry_points.py bash tests/check_examples.bash - name: Advanced tests run: | bash data/examples/unpack.bash bash data/tests/download.bash - python tests/benchmark_cap_vs_mtuq.py --no_figures python tests/test_grid_search_mt.py --no_figures python tests/test_grid_search_mt_depth.py --no_figures python tests/test_greens_SPECFEM3D_SAC.py --no_figures python tests/test_time_shifts.py --no_figures + python tests/benchmark_cap_vs_mtuq.py --no_figures - name: Graphics run: | python tests/test_graphics.py - # unfortunately, these Conda installation tests exceed the resource limits + # unfortunately, these Conda installation tests sometimes exceed the resource limits # for GitHub workflows # (https://github.com/actions/runner-images/issues/6680) # (https://github.com/conda/conda/issues/8650) #- name: conda installation # timeout-minutes: 60 # run: | - # bash tests/conda_install.bash + # bash tests/install_conda.bash From a676eadb3d8cf42e75fa20636ffe2fa9a94dfa2c Mon Sep 17 00:00:00 2001 From: thurinj Date: Tue, 9 Dec 2025 10:23:50 +0900 Subject: [PATCH 5/8] Add HDF5 headers installation to workflow -- now from the current master version --- .github/workflows/python-app.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index 0051bd59..20345a2d 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -21,6 +21,10 @@ jobs: run: sudo apt-get update && sudo apt-get install -y libfabric-bin - uses: actions/checkout@v2 + + - name: Install HDF5 headers + run: sudo apt-get update && sudo apt-get install -y libhdf5-dev + - name: Set up Python 3 uses: actions/setup-python@v2 From c8cc4a74942b83f011a0fae73875a5ece565b5f0 Mon Sep 17 00:00:00 2001 From: thurinj Date: Tue, 9 Dec 2025 10:37:35 +0900 Subject: [PATCH 6/8] Update Python version to 3.13 in workflow and pyproject Python 3.14 (released oct of this year) seems to cause issues with the continuous integration tests, so I fixed it to 3.13. So far the tests are working on my machine. --- .github/workflows/python-app.yaml | 9 +++------ pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index 20345a2d..d92b935b 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -21,12 +21,10 @@ jobs: run: sudo apt-get update && sudo apt-get install -y libfabric-bin - uses: actions/checkout@v2 - - - name: Install HDF5 headers - run: sudo apt-get update && sudo apt-get install -y libhdf5-dev - - name: Set up Python 3 uses: actions/setup-python@v2 + with: + python-version: '3.13' - name: pip installation # test pip installation first (much faster than conda) @@ -57,5 +55,4 @@ jobs: #- name: conda installation # timeout-minutes: 60 # run: | - # bash tests/install_conda.bash - + # bash tests/install_conda.bash \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 6ba91dec..9743dd2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ description = "moment tensor (mt) uncertainty quantification (uq)" authors = [ { name = "Ryan Modrak" } ] -requires-python = ">=3" +requires-python = ">=3,<3.14" keywords = ["seismology"] classifiers = [ "Development Status :: 4 - Beta", From e3a374f2c2163f75f9074fd7e441eaea92c3b442 Mon Sep 17 00:00:00 2001 From: thurinj Date: Wed, 10 Dec 2025 00:55:27 +0900 Subject: [PATCH 7/8] Fixed the normalized misfit handling in waveform plots. Changed default behavior for collect_synthetics (but also collect_attributes, even if not really used much in the code -- for consistency). I also noticed that if the normalized misfit per trace was not stored as attributes in the level0, then it would divide the raw misfit by the total (normalized) misfit. So in the 2009 eq event, this translates to doing ~1e-9 / ~1e0 'hybrid' had not been properly implemented, so I did a light rework of the calculate_norm_data function. --- mtuq/misfit/waveform/__init__.py | 12 +++++++++--- mtuq/misfit/waveform/_stats.py | 9 +++++---- mtuq/misfit/waveform/level0.py | 9 ++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/mtuq/misfit/waveform/__init__.py b/mtuq/misfit/waveform/__init__.py index 62d0063d..fa6df8f2 100644 --- a/mtuq/misfit/waveform/__init__.py +++ b/mtuq/misfit/waveform/__init__.py @@ -233,7 +233,7 @@ def __call__(self, data, greens, sources, progress_handle=Null(), normalize=normalize, ext='Cython') - def collect_attributes(self, data, greens, source, normalize=False): + def collect_attributes(self, data, greens, source, normalize=None): """ Collects misfit, time shifts and other attributes corresponding to each trace """ @@ -250,6 +250,9 @@ def collect_attributes(self, data, greens, source, normalize=False): source, components=data.get_components(), stats=data.get_stats(), mode='map', inplace=True) + if normalize is None: + normalize = self.normalize + # attaches attributes to synthetics _ = level0.misfit( data, greens, iterable(source), self.norm, self.time_shift_groups, @@ -270,7 +273,7 @@ def collect_attributes(self, data, greens, source, normalize=False): return deepcopy(attrs) - def collect_synthetics(self, data, greens, source, normalize=False, mode=2): + def collect_synthetics(self, data, greens, source, normalize=None, mode=2): """ Collects synthetics with misfit, time shifts and other attributes attached """ @@ -310,11 +313,14 @@ def collect_synthetics(self, data, greens, source, normalize=False, mode=2): source, components=components, stats=data.get_stats(), mode='map', inplace=True) + if normalize is None: + normalize = self.normalize + # attaches attributes to synthetics _ = level0.misfit( data, greens, iterable(source), self.norm, self.time_shift_groups, self.time_shift_min, self.time_shift_max, msg_handle=Null(), - normalize=False, set_attributes=True) + normalize=normalize, set_attributes=True) return deepcopy(synthetics) diff --git a/mtuq/misfit/waveform/_stats.py b/mtuq/misfit/waveform/_stats.py index b31db475..88a81b2e 100644 --- a/mtuq/misfit/waveform/_stats.py +++ b/mtuq/misfit/waveform/_stats.py @@ -35,7 +35,8 @@ def calculate_norm_data(data, norm, components, apply_weights=True): dt = stream[0].stats.delta for _k in indices: - d = stream[_k].data + trace = stream[_k] + d = trace.data if norm=='L1': value = np.sum(np.abs(d))*dt @@ -44,14 +45,14 @@ def calculate_norm_data(data, norm, components, apply_weights=True): value = np.sum(d**2)*dt elif norm=='hybrid': - value = np.sqrt(np.sum(r**2))*dt + value = np.sqrt(np.sum(d**2))*dt # optionally, applies user-supplied weights attached during # process_data() if apply_weights: try: - value *= d[_k].weight - except: + value *= trace.weight + except Exception: pass norm_data += value diff --git a/mtuq/misfit/waveform/level0.py b/mtuq/misfit/waveform/level0.py index 2469fa2e..32b67f7d 100644 --- a/mtuq/misfit/waveform/level0.py +++ b/mtuq/misfit/waveform/level0.py @@ -113,7 +113,14 @@ def misfit(data, greens, sources, norm, time_shift_groups, s[_k].attrs.norm = norm - s[_k].attrs.misfit = value + if normalize: + try: + s[_k].attrs.misfit = value / norm_data + print('Normalized misfit stored in trace attributes.') + except Exception: + s[_k].attrs.misfit = value + else: + s[_k].attrs.misfit = value s[_k].attrs.idx_start = idx_start s[_k].attrs.idx_stop = idx_stop From 510b76df350d9c6772a7d6c7bd51a30f355126ea Mon Sep 17 00:00:00 2001 From: thurinj Date: Wed, 10 Dec 2025 19:57:21 +0900 Subject: [PATCH 8/8] Removed unnecessary debug print left from implementation. --- mtuq/misfit/waveform/level0.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mtuq/misfit/waveform/level0.py b/mtuq/misfit/waveform/level0.py index 32b67f7d..85855eb9 100644 --- a/mtuq/misfit/waveform/level0.py +++ b/mtuq/misfit/waveform/level0.py @@ -116,7 +116,6 @@ def misfit(data, greens, sources, norm, time_shift_groups, if normalize: try: s[_k].attrs.misfit = value / norm_data - print('Normalized misfit stored in trace attributes.') except Exception: s[_k].attrs.misfit = value else: