gtrack 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gtrack/__init__.py +137 -0
- gtrack/boundaries.py +396 -0
- gtrack/config.py +202 -0
- gtrack/geometry.py +348 -0
- gtrack/hpc_integration.py +851 -0
- gtrack/initial_conditions.py +255 -0
- gtrack/io_formats.py +477 -0
- gtrack/logging.py +193 -0
- gtrack/mesh.py +101 -0
- gtrack/mor_seeds.py +390 -0
- gtrack/point_rotation.py +836 -0
- gtrack/polygon_filter.py +223 -0
- gtrack/spatial.py +397 -0
- gtrack-0.3.0.dist-info/METADATA +66 -0
- gtrack-0.3.0.dist-info/RECORD +17 -0
- gtrack-0.3.0.dist-info/WHEEL +5 -0
- gtrack-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Initial age calculation for icosahedral mesh points.
|
|
3
|
+
|
|
4
|
+
This module provides functions to compute initial seafloor ages
|
|
5
|
+
from distance to the nearest ridge, matching GPlately's approach.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from typing import Callable, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
import pygplates
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def compute_initial_ages(
|
|
15
|
+
ocean_points: pygplates.MultiPointOnSphere,
|
|
16
|
+
resolved_topologies: List,
|
|
17
|
+
shared_boundary_sections: List,
|
|
18
|
+
initial_ocean_mean_spreading_rate: float = 75.0,
|
|
19
|
+
fill_value: float = 5000.0,
|
|
20
|
+
age_distance_law: Optional[Callable[[np.ndarray, float], np.ndarray]] = None,
|
|
21
|
+
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
22
|
+
"""
|
|
23
|
+
Compute initial seafloor ages from distance to nearest ridge.
|
|
24
|
+
|
|
25
|
+
For each ocean point, finds the nearest mid-ocean ridge within the same
|
|
26
|
+
plate polygon and calculates age using the distance/spreading-rate formula.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
ocean_points : pygplates.MultiPointOnSphere
|
|
31
|
+
Ocean basin points (after filtering out continental points).
|
|
32
|
+
resolved_topologies : list
|
|
33
|
+
Resolved topologies from pygplates.resolve_topologies().
|
|
34
|
+
shared_boundary_sections : list
|
|
35
|
+
Shared boundary sections from pygplates.resolve_topologies().
|
|
36
|
+
initial_ocean_mean_spreading_rate : float, default=75.0
|
|
37
|
+
Mean spreading rate in mm/yr (numerically equal to km/Myr).
|
|
38
|
+
Default is GPlately's value of 75 mm/yr.
|
|
39
|
+
fill_value : float, default=5000.0
|
|
40
|
+
Distance value (km) for points in plates without ridges.
|
|
41
|
+
Corresponds to ~133 Myr at 75 mm/yr spreading rate.
|
|
42
|
+
age_distance_law : callable, optional
|
|
43
|
+
Custom function to convert distances to ages.
|
|
44
|
+
Signature: (distances_km, spreading_rate_mm_yr) -> ages_myr
|
|
45
|
+
If None, uses default: age = distance / (rate / 2)
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
lons : np.ndarray
|
|
50
|
+
Longitudes of ocean points in degrees.
|
|
51
|
+
lats : np.ndarray
|
|
52
|
+
Latitudes of ocean points in degrees.
|
|
53
|
+
ages : np.ndarray
|
|
54
|
+
Computed ages in Myr.
|
|
55
|
+
|
|
56
|
+
Notes
|
|
57
|
+
-----
|
|
58
|
+
The default age formula is:
|
|
59
|
+
age_myr = distance_km / (spreading_rate_mm_yr / 2)
|
|
60
|
+
|
|
61
|
+
The division by 2 accounts for half-spreading rate (each plate moves
|
|
62
|
+
at half the full spreading rate). The units work because:
|
|
63
|
+
75 mm/yr = 75 km/Myr (numerically equal)
|
|
64
|
+
|
|
65
|
+
Examples
|
|
66
|
+
--------
|
|
67
|
+
>>> # Resolve topologies at starting time
|
|
68
|
+
>>> resolved_topologies = []
|
|
69
|
+
>>> shared_boundary_sections = []
|
|
70
|
+
>>> pygplates.resolve_topologies(
|
|
71
|
+
... topology_features, rotation_model, resolved_topologies,
|
|
72
|
+
... starting_age, shared_boundary_sections
|
|
73
|
+
... )
|
|
74
|
+
>>> lons, lats, ages = compute_initial_ages(
|
|
75
|
+
... ocean_points, resolved_topologies, shared_boundary_sections
|
|
76
|
+
... )
|
|
77
|
+
"""
|
|
78
|
+
all_lons = []
|
|
79
|
+
all_lats = []
|
|
80
|
+
all_distances = []
|
|
81
|
+
|
|
82
|
+
# Create point features from MultiPointOnSphere for the query
|
|
83
|
+
point_feature = pygplates.Feature()
|
|
84
|
+
point_feature.set_geometry(ocean_points)
|
|
85
|
+
point_features = [point_feature]
|
|
86
|
+
|
|
87
|
+
# Process each plate topology
|
|
88
|
+
for topology in resolved_topologies:
|
|
89
|
+
plate_id = topology.get_resolved_feature().get_reconstruction_plate_id()
|
|
90
|
+
|
|
91
|
+
# Find mid-ocean ridges that bound this plate
|
|
92
|
+
mid_ocean_ridges_on_plate = []
|
|
93
|
+
for shared_boundary_section in shared_boundary_sections:
|
|
94
|
+
if (
|
|
95
|
+
shared_boundary_section.get_feature().get_feature_type()
|
|
96
|
+
== pygplates.FeatureType.create_gpml("MidOceanRidge")
|
|
97
|
+
):
|
|
98
|
+
for shared_subsegment in shared_boundary_section.get_shared_sub_segments():
|
|
99
|
+
sharing_resolved_topologies = (
|
|
100
|
+
shared_subsegment.get_sharing_resolved_topologies()
|
|
101
|
+
)
|
|
102
|
+
for resolved_polygon in sharing_resolved_topologies:
|
|
103
|
+
if (
|
|
104
|
+
resolved_polygon.get_feature().get_reconstruction_plate_id()
|
|
105
|
+
== plate_id
|
|
106
|
+
):
|
|
107
|
+
mid_ocean_ridges_on_plate.append(
|
|
108
|
+
shared_subsegment.get_resolved_geometry()
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Process points within this plate
|
|
112
|
+
for pf in point_features:
|
|
113
|
+
for points in pf.get_geometries():
|
|
114
|
+
for point in points:
|
|
115
|
+
if topology.get_resolved_geometry().is_point_in_polygon(point):
|
|
116
|
+
lat, lon = point.to_lat_lon()
|
|
117
|
+
|
|
118
|
+
if len(mid_ocean_ridges_on_plate) > 0:
|
|
119
|
+
# Find minimum distance to any ridge
|
|
120
|
+
min_distance = None
|
|
121
|
+
for ridge in mid_ocean_ridges_on_plate:
|
|
122
|
+
distance = pygplates.GeometryOnSphere.distance(
|
|
123
|
+
point, ridge, min_distance
|
|
124
|
+
)
|
|
125
|
+
if distance is not None:
|
|
126
|
+
min_distance = distance
|
|
127
|
+
|
|
128
|
+
# Convert to km
|
|
129
|
+
distance_km = min_distance * pygplates.Earth.mean_radius_in_kms
|
|
130
|
+
else:
|
|
131
|
+
# No ridges bounding this plate - use fill value
|
|
132
|
+
distance_km = fill_value
|
|
133
|
+
|
|
134
|
+
all_lons.append(lon)
|
|
135
|
+
all_lats.append(lat)
|
|
136
|
+
all_distances.append(distance_km)
|
|
137
|
+
|
|
138
|
+
# Convert to arrays
|
|
139
|
+
lons = np.array(all_lons)
|
|
140
|
+
lats = np.array(all_lats)
|
|
141
|
+
distances = np.array(all_distances)
|
|
142
|
+
|
|
143
|
+
# Compute ages using provided or default formula
|
|
144
|
+
if age_distance_law is not None:
|
|
145
|
+
ages = age_distance_law(distances, initial_ocean_mean_spreading_rate)
|
|
146
|
+
else:
|
|
147
|
+
ages = default_age_distance_law(distances, initial_ocean_mean_spreading_rate)
|
|
148
|
+
|
|
149
|
+
return lons, lats, ages
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def default_age_distance_law(
|
|
153
|
+
distances_km: np.ndarray,
|
|
154
|
+
spreading_rate_mm_yr: float
|
|
155
|
+
) -> np.ndarray:
|
|
156
|
+
"""
|
|
157
|
+
Default age-distance law: age = distance / (spreading_rate / 2).
|
|
158
|
+
|
|
159
|
+
This is GPlately's formula, where the half-spreading rate is used
|
|
160
|
+
because each plate moves at half the full spreading rate.
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
distances_km : np.ndarray
|
|
165
|
+
Distances to nearest ridge in km.
|
|
166
|
+
spreading_rate_mm_yr : float
|
|
167
|
+
Full spreading rate in mm/yr (numerically equal to km/Myr).
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
np.ndarray
|
|
172
|
+
Ages in Myr.
|
|
173
|
+
|
|
174
|
+
Notes
|
|
175
|
+
-----
|
|
176
|
+
The formula works because 1 mm/yr = 1 km/Myr numerically:
|
|
177
|
+
75 mm/yr × 1e6 yr/Myr × 1e-6 km/mm = 75 km/Myr
|
|
178
|
+
"""
|
|
179
|
+
half_spreading_rate = spreading_rate_mm_yr / 2.0
|
|
180
|
+
return distances_km / half_spreading_rate
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def compute_initial_ages_kdtree(
|
|
184
|
+
ocean_lats: np.ndarray,
|
|
185
|
+
ocean_lons: np.ndarray,
|
|
186
|
+
ridge_lats: np.ndarray,
|
|
187
|
+
ridge_lons: np.ndarray,
|
|
188
|
+
initial_ocean_mean_spreading_rate: float = 75.0,
|
|
189
|
+
earth_radius_km: float = 6371.0,
|
|
190
|
+
age_distance_law: Optional[Callable[[np.ndarray, float], np.ndarray]] = None,
|
|
191
|
+
) -> np.ndarray:
|
|
192
|
+
"""
|
|
193
|
+
Compute initial ages using a fast KDTree approach.
|
|
194
|
+
|
|
195
|
+
This is a simpler alternative that doesn't use plate polygon queries.
|
|
196
|
+
It finds the globally nearest ridge point for each ocean point.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
ocean_lats : np.ndarray
|
|
201
|
+
Ocean point latitudes in degrees.
|
|
202
|
+
ocean_lons : np.ndarray
|
|
203
|
+
Ocean point longitudes in degrees.
|
|
204
|
+
ridge_lats : np.ndarray
|
|
205
|
+
Ridge point latitudes in degrees.
|
|
206
|
+
ridge_lons : np.ndarray
|
|
207
|
+
Ridge point longitudes in degrees.
|
|
208
|
+
initial_ocean_mean_spreading_rate : float, default=75.0
|
|
209
|
+
Mean spreading rate in mm/yr.
|
|
210
|
+
earth_radius_km : float, default=6371.0
|
|
211
|
+
Earth radius in km.
|
|
212
|
+
age_distance_law : callable, optional
|
|
213
|
+
Custom function to convert distances to ages.
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
ages : np.ndarray
|
|
218
|
+
Computed ages in Myr.
|
|
219
|
+
|
|
220
|
+
Notes
|
|
221
|
+
-----
|
|
222
|
+
This method is faster but less accurate than the plate-based approach
|
|
223
|
+
because it doesn't respect plate boundaries. Use for quick estimates
|
|
224
|
+
or when plate topology information is not available.
|
|
225
|
+
"""
|
|
226
|
+
from scipy.spatial import cKDTree
|
|
227
|
+
|
|
228
|
+
# Convert to Cartesian XYZ on unit sphere
|
|
229
|
+
def latlon_to_xyz(lats, lons):
|
|
230
|
+
lats_rad = np.radians(lats)
|
|
231
|
+
lons_rad = np.radians(lons)
|
|
232
|
+
x = np.cos(lats_rad) * np.cos(lons_rad)
|
|
233
|
+
y = np.cos(lats_rad) * np.sin(lons_rad)
|
|
234
|
+
z = np.sin(lats_rad)
|
|
235
|
+
return np.column_stack([x, y, z])
|
|
236
|
+
|
|
237
|
+
ocean_xyz = latlon_to_xyz(ocean_lats, ocean_lons)
|
|
238
|
+
ridge_xyz = latlon_to_xyz(ridge_lats, ridge_lons)
|
|
239
|
+
|
|
240
|
+
# Build KDTree and query
|
|
241
|
+
tree = cKDTree(ridge_xyz)
|
|
242
|
+
chord_distances, _ = tree.query(ocean_xyz, k=1)
|
|
243
|
+
|
|
244
|
+
# Convert chord distance to great circle distance
|
|
245
|
+
# chord = 2 * sin(angle/2), so angle = 2 * arcsin(chord/2)
|
|
246
|
+
angles_rad = 2 * np.arcsin(np.clip(chord_distances / 2, 0, 1))
|
|
247
|
+
distances_km = angles_rad * earth_radius_km
|
|
248
|
+
|
|
249
|
+
# Compute ages
|
|
250
|
+
if age_distance_law is not None:
|
|
251
|
+
ages = age_distance_law(distances_km, initial_ocean_mean_spreading_rate)
|
|
252
|
+
else:
|
|
253
|
+
ages = default_age_distance_law(distances_km, initial_ocean_mean_spreading_rate)
|
|
254
|
+
|
|
255
|
+
return ages
|