-
-
Save pukpr/98427ca6bf5b220a31fa919955f50422 to your computer and use it in GitHub Desktop.
| #!/usr/bin/env python3 | |
| import sys | |
| import json | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| from pathlib import Path | |
| def read_lpap(subdir): | |
| """ | |
| Read lpap list from lt.exe.p in the given subdirectory. | |
| Returns a list of tuples (period_days, amplitude, phase_rad). | |
| """ | |
| json_path = Path(subdir) / "lt.exe.p" | |
| if not json_path.is_file(): | |
| print(f"Warning: {json_path} not found, returning empty list.") | |
| return [] | |
| with open(json_path, 'r') as f: | |
| data = json.load(f) | |
| triplets = data.get("lpap", []) | |
| components = [] | |
| for t in triplets: | |
| if len(t) >= 3: | |
| try: | |
| period = float(t[0]) # first field: period in days | |
| amp = float(t[1]) # second field: amplitude | |
| phase = float(t[2]) # third field: phase in radians | |
| components.append((period, amp, phase)) | |
| except ValueError: | |
| # skip if conversion fails | |
| continue | |
| return components | |
| def compute_composite(components, t_days): | |
| """ | |
| Given a list of (period, amplitude, phase) and a time array (days), | |
| return the sum of all sine waves: Σ A_i * sin(2π * t / P_i + φ_i) | |
| """ | |
| composite = np.zeros_like(t_days, dtype=float) | |
| for period, amp, phase in components: | |
| if period > 0: | |
| composite += amp * np.sin(2.0 * np.pi * t_days / period + phase) | |
| return composite | |
| def main(): | |
| if len(sys.argv) != 3: | |
| print("Usage: python plot_composite.py <subdir1> <subdir2>") | |
| sys.exit(1) | |
| subdir1, subdir2 = sys.argv[1], sys.argv[2] | |
| # Read component data from both directories | |
| comps1 = read_lpap(subdir1) | |
| comps2 = read_lpap(subdir2) | |
| if not comps1 and not comps2: | |
| print("No data found in either file.") | |
| return | |
| # Time axis: from 1962‑01‑01 to 1972‑01‑01 (10 years) | |
| start_year = 1962.0 | |
| duration_years = 10.0 | |
| days_per_year = 365.2422 | |
| num_points = 20000 | |
| t_days = np.linspace(0.0, duration_years * days_per_year, num_points) | |
| t_years = start_year + t_days / days_per_year | |
| # Compute composite signals | |
| if comps1: | |
| signal1 = compute_composite(comps1, t_days) | |
| else: | |
| signal1 = np.zeros_like(t_days) | |
| if comps2: | |
| signal2 = compute_composite(comps2, t_days) | |
| else: | |
| signal2 = np.zeros_like(t_days) | |
| # Compute correlation coefficient (Pearson) | |
| if np.any(signal1) and np.any(signal2): | |
| corr = np.corrcoef(signal1, signal2)[0, 1] | |
| else: | |
| corr = float('nan') | |
| # Plot | |
| fig, ax = plt.subplots(figsize=(12, 6)) | |
| if comps1: | |
| ax.plot(t_years, signal1, linestyle='-', color='blue', label=f'Composite from {subdir1}', linewidth=1.5) | |
| if comps2: | |
| ax.plot(t_years, signal2, linestyle='--', color='red', label=f'Composite from {subdir2}', linewidth=1.5) | |
| ax.set_xlabel('Year') | |
| ax.set_ylabel('Amplitude') | |
| ax.set_title(f'Composite sine waves (10‑year span starting 1962) - Correlation: {corr:.4f}') | |
| ax.legend() | |
| ax.grid(True, linestyle='--', alpha=0.6) | |
| plt.tight_layout() | |
| plt.show() | |
| if __name__ == "__main__": | |
| main() | |
| #!/usr/bin/env python3 | |
| """ | |
| Plot column 1 vs column 4 from lte_results.csv files in two subdirs. | |
| Each series is normalized to unit variance. | |
| Pearson's r over the overlapping time segment is computed and shown in the title. | |
| """ | |
| import sys | |
| import os | |
| import glob | |
| import numpy as np | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| def main(): | |
| if len(sys.argv) < 3: | |
| print("Usage: python lte_results_manifold.py <dir1> <dir2> [output_png]") | |
| sys.exit(1) | |
| output_png = sys.argv[3] if len(sys.argv) > 3 else None | |
| COLUMN=3 | |
| TITLE='Manifold' | |
| # COLUMN=2 | |
| # TITLE='Index' | |
| # Collect one CSV per directory (take the first found if multiple) | |
| csv_files = [] | |
| for d in sys.argv[1:3]: | |
| found = glob.glob(os.path.join(d, "lte_results.csv")) | |
| if found: | |
| csv_files.append(found[0]) # pick first | |
| if len(csv_files) != 2: | |
| print("Need exactly one lte_results.csv in each of the two directories.") | |
| sys.exit(1) | |
| # Read both files | |
| df1 = pd.read_csv(csv_files[0], header=None) | |
| df2 = pd.read_csv(csv_files[1], header=None) | |
| x1, y1 = df1[0].values, df1[COLUMN].values | |
| x2, y2 = df2[0].values, df2[COLUMN].values | |
| # Normalize each y to unit variance (one‑liner) | |
| y1 = (y1 - y1.mean()) / y1.std() | |
| y2 = (y2 - y2.mean()) / y2.std() | |
| # ---- ONE‑LINER for Pearson's r over the shared time segment ---- | |
| # Defines common x as the union of points inside the overlap, interpolates both, | |
| # computes correlation, and stores it in 'r' | |
| x_min, x_max = max(x1.min(), x2.min()), min(x1.max(), x2.max()) | |
| common_x = np.unique(np.concatenate([x1[(x1 >= x_min) & (x1 <= x_max)], x2[(x2 >= x_min) & (x2 <= x_max)]])) | |
| r = np.corrcoef(np.interp(common_x, x1, y1), np.interp(common_x, x2, y2))[0, 1] if len(common_x) > 1 else np.nan | |
| # ----------------------------------------------------------------- | |
| # Plot both series | |
| fig, ax = plt.subplots(figsize=(10, 6)) | |
| # Add vertical gray dotted grid lines every 3.81 years | |
| spacing = 3.81 | |
| grid_years = np.arange(1880, 2021, spacing) | |
| for g in grid_years: | |
| ax.axvline(g, color='green', linestyle=':', lw=0.8) | |
| ax.plot(x1, y1, label=sys.argv[1], color='red', linestyle='-', linewidth=1) | |
| ax.plot(x2, y2, label=sys.argv[2], color='blue', linestyle='--', linewidth=1) | |
| # Place r in the title (with 3 decimals) | |
| title = f"{TITLE} — Pearson r = {r:.3f}" if not np.isnan(r) else "No overlapping time segment" | |
| ax.set_title(title) | |
| ax.set_xlabel("Year") | |
| ax.set_ylabel(TITLE) | |
| ax.legend() | |
| # ax.grid(True, linestyle='--', alpha=0.7) | |
| plt.tight_layout() | |
| if output_png: | |
| plt.savefig(output_png, dpi=100, bbox_inches='tight') | |
| print(f"Saved: {output_png}") | |
| else: | |
| plt.show() | |
| if __name__ == "__main__": | |
| main() | |
The analysis of the tidal and LTE parameters across the requested models reveals distinct patterns correlated with
their geographic ocean basins and longitudes. Below is a detailed breakdown of the differences in tidal parameters
(from lpap) and LTE parameters (from lt.exe.p) relative to their geolocations defined in ID.yml.
1. LTE Parameter Analysis
The LTE parameters show a clear "oceanic basin signature," particularly in the impulse response parameters (impA,
impB) and the asymmetry (asym).
┌────────┬──────────┬──────────┬───────────┬────────┬────────┬────────┬────────┬───────┐
│ Model │ Basin │ Latitude │ Longitude │ impA │ impB │ asym │ ltep │ offs │
├────────┼──────────┼──────────┼───────────┼────────┼────────┼────────┼────────┼───────┤
│ nino4 │ Pacific │ 0.0 │ 175.0W │ -0.335 │ -0.002 │ -0.875 │ -1.113 │ 0.037 │
│ pdo │ Pacific │ 45.0N │ 160.0W │ -0.410 │ -0.002 │ -0.909 │ -1.179 │ 0.037 │
│ emi │ Pacific │ 0.5N │ 145.0W │ -0.311 │ -0.002 │ -0.864 │ -1.157 │ 0.037 │
│ nino34 │ Pacific │ 0.0 │ 142.0W │ -0.212 │ -0.033 │ -0.893 │ -1.156 │ 0.038 │
│ 155 │ Pacific │ 21.3N │ 157.9W │ -0.324 │ -0.002 │ -0.878 │ -1.209 │ 0.037 │
│ 245 │ Pacific │ 33.7N │ 118.3W │ -0.472 │ -0.000 │ -0.891 │ -1.167 │ 0.037 │
│ amo │ Atlantic │ 35.0N │ 41.0W │ -0.089 │ -1.691 │ -0.861 │ -1.107 │ 0.042 │
│ tna │ Atlantic │ 40.0N │ 35.0W │ -0.050 │ -1.397 │ -0.871 │ -1.204 │ 0.042 │
│ nao │ Atlantic │ 55.0N │ 25.0W │ 0.000 │ -1.060 │ -0.837 │ -1.120 │ 0.038 │
│ iode │ Indian │ 5.0S │ 100.0E │ -0.179 │ -0.001 │ -0.931 │ -1.179 │ 0.040 │
│ 111 │ Indian │ 32.1S │ 115.7E │ -0.379 │ -0.002 │ -0.883 │ -1.115 │ 0.038 │
│ 88 │ Baltic │ 64.0N │ 20.9E │ -0.096 │ -0.110 │ -0.859 │ -1.093 │ 0.036 │
│ baltic │ Baltic │ 58.0N │ 18.0E │ 0.034 │ -0.273 │ -0.905 │ -1.114 │ 0.034 │
└────────┴──────────┴──────────┴───────────┴────────┴────────┴────────┴────────┴───────┘
Key Findings:
* Impulse Response Basis Shift: There is a fundamental shift in the impulse response components between basins.
Pacific and Indian Ocean models are primarily driven by the impA component (with impB near zero), while Atlantic
models are dominated by impB (with impA near zero). This likely reflects the orthogonal orientation and different
resonance characteristics of the Atlantic vs. Pacific basins.
* Asymmetry (asym): All models show strong negative asymmetry (ranging from -0.83 to -0.93), which is a
characteristic of the non-linear response in the LTE (Length of Day / Tidal) solver. The Indian Ocean (iode) and
high-latitude Pacific (pdo) show the strongest asymmetry.
* Global Parameters: delB is identical across all models (0.7917), suggesting it is a fixed physical constant in the
model configuration. ltep values are also remarkably stable, generally clustering between -1.09 and -1.21,
regardless of the geographic location.
* Offset (offs): The Atlantic and Indian Ocean basins show slightly higher offsets (0.040 - 0.042) compared to the
Pacific and Baltic models (0.034 - 0.037).
2. Tidal Parameter Analysis (lpap)
The tidal parameters for the ~27.32 day cycle (Component 0) show high consistency in amplitude but significant
variation in phase, which correlates with longitude.
┌────────┬───────────┬─────────────┬───────────────────────────┐
│ Model │ Longitude │ Phase (rad) │ Normalized Phase (mod 2π) │
├────────┼───────────┼─────────────┼───────────────────────────┤
│ iode │ 100.0E │ 6.475 │ 0.192 │
│ 111 │ 115.7E │ 12.535 │ 6.252 │
│ nino4 │ 185.0E │ 6.288 │ 6.288 │
│ pdo │ 200.0E │ 6.389 │ 6.389 │
│ 155 │ 202.1E │ 6.448 │ 6.448 │
│ emi │ 215.0E │ 6.443 │ 6.443 │
│ nino34 │ 218.0E │ 12.262 │ 5.979 │
│ 245 │ 241.7E │ 6.917 │ 0.634 │
│ amo │ 319.0E │ 10.554 │ 4.271 │
│ tna │ 325.0E │ 10.397 │ 4.114 │
│ nao │ 335.0E │ 11.208 │ 4.925 │
│ 88 │ 20.9E │ -1.277 │ 5.006 │
│ baltic │ 18.0E │ 10.776 │ 4.493 │
└────────┴───────────┴─────────────┴───────────────────────────┘
Key Findings:
* Phase Migration: The phases of the lunar tidal components migrate systematically with longitude. The phase shift
between the Western Pacific (nino4) and the American West Coast (245) reflects the physical propagation of the
tidal potential.
* Amplitudes: For most models, the tidal amplitudes are kept at a "reference" level (e.g., 0.1047 for Component 0),
with the exception of the baltic composite model, which uses a significantly higher amplitude (0.1700), likely due
to the enclosed and resonant nature of the Baltic Sea affecting the composite average.
* Consistency: The periods are identical across all models, confirming they are locked to the same astronomical
forcing frequencies. The "very good fits" mentioned are achieved by fine-tuning the phases to the local geographic
response while maintaining global LTE physics.
The tidal factor amplitudes appear to cluster according to Atlantic and Pacific location, but that may not be as strict, as these were all seeded from a fit close by.

The latent hidden manifold is not that different between the ocean basins (the vertical lines are 3..8 years apart corresponding to the Mf tidal factor)
but does align much more within the basin
The ongoing premise is that these are all very slight or subtle amplitude and phase variations that give rise to variations in the climate and MSL oscillations. This is in keeping with conventional tidal analysis where the rank ordering is largely the same around the world, but where phase plays a significant role.
The ordering here corresponds to that of LOD, but the ~7-day harmonics of the Mf and Mf' are amplified, so the calibration to LOD is only a starting point here. The non-linearity of the LTE modulation is possibly missing some of this amplification. It's also possible that this is on a continuum with the much stronger diurnal tides, i.e. 7 days is clearly closer to 1-day than 13.66 days, so that stronger acceleration of shorter tidal factor periods play a role.










































Ratan MSL #88
NINO4
NAO
AMO
IODE
PDO
EMI
TNA
Baltic
NINO34
Honolulu MSL #155
Los Angeles MSL #245
Fremantle MSL #111