gtrack 0.3.0__tar.gz → 0.3.2__tar.gz

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.
Files changed (26) hide show
  1. {gtrack-0.3.0 → gtrack-0.3.2}/PKG-INFO +1 -1
  2. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/__init__.py +4 -1
  3. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/hpc_integration.py +0 -10
  4. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/point_rotation.py +49 -0
  5. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack.egg-info/PKG-INFO +1 -1
  6. {gtrack-0.3.0 → gtrack-0.3.2}/pyproject.toml +1 -1
  7. {gtrack-0.3.0 → gtrack-0.3.2}/tests/test_point_rotation.py +61 -0
  8. {gtrack-0.3.0 → gtrack-0.3.2}/README.md +0 -0
  9. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/boundaries.py +0 -0
  10. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/config.py +0 -0
  11. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/geometry.py +0 -0
  12. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/initial_conditions.py +0 -0
  13. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/io_formats.py +0 -0
  14. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/logging.py +0 -0
  15. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/mesh.py +0 -0
  16. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/mor_seeds.py +0 -0
  17. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/polygon_filter.py +0 -0
  18. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack/spatial.py +0 -0
  19. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack.egg-info/SOURCES.txt +0 -0
  20. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack.egg-info/dependency_links.txt +0 -0
  21. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack.egg-info/requires.txt +0 -0
  22. {gtrack-0.3.0 → gtrack-0.3.2}/gtrack.egg-info/top_level.txt +0 -0
  23. {gtrack-0.3.0 → gtrack-0.3.2}/setup.cfg +0 -0
  24. {gtrack-0.3.0 → gtrack-0.3.2}/tests/test_core.py +0 -0
  25. {gtrack-0.3.0 → gtrack-0.3.2}/tests/test_geometry.py +0 -0
  26. {gtrack-0.3.0 → gtrack-0.3.2}/tests/test_regression.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gtrack
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: GPlates-based Tracking of Lithosphere and Kinematics
5
5
  Author: S. Ghelichkhani
6
6
  License: MIT
@@ -46,7 +46,7 @@ Example
46
46
  ... cloud = tracker.step_to(age)
47
47
  """
48
48
 
49
- __version__ = "0.2.0"
49
+ __version__ = "0.3.1"
50
50
 
51
51
  # Logging configuration (import first to configure before other modules)
52
52
  from .logging import (
@@ -58,6 +58,9 @@ from .logging import (
58
58
  get_logger,
59
59
  )
60
60
 
61
+ # Configure logging with default level (WARNING) immediately on import
62
+ configure_logging()
63
+
61
64
  # Core seafloor age functionality
62
65
  from .config import TracerConfig
63
66
  from .hpc_integration import SeafloorAgeTracker
@@ -75,17 +75,11 @@ class SeafloorAgeTracker:
75
75
  topology_files: Union[str, List[str]],
76
76
  continental_polygons: Optional[str] = None,
77
77
  config: Optional[TracerConfig] = None,
78
- verbose: bool = True,
79
78
  ):
80
79
  from .geometry import ensure_list
81
80
 
82
81
  self._config = config if config else TracerConfig()
83
82
 
84
- # Handle deprecated verbose flag
85
- if verbose:
86
- from .logging import enable_verbose
87
- enable_verbose()
88
-
89
83
  # Handle single file or Path as list
90
84
  rotation_files = ensure_list(rotation_files)
91
85
  topology_files = ensure_list(topology_files)
@@ -794,7 +788,6 @@ class SeafloorAgeTracker:
794
788
  topology_files: Union[str, List[str]],
795
789
  continental_polygons: Optional[str] = None,
796
790
  config: Optional[TracerConfig] = None,
797
- verbose: bool = True,
798
791
  ) -> PointCloud:
799
792
  """
800
793
  One-shot computation of seafloor ages (functional interface).
@@ -816,8 +809,6 @@ class SeafloorAgeTracker:
816
809
  Path to continental polygon file.
817
810
  config : TracerConfig, optional
818
811
  Configuration parameters.
819
- verbose : bool, default=True
820
- Print progress information.
821
812
 
822
813
  Returns
823
814
  -------
@@ -839,7 +830,6 @@ class SeafloorAgeTracker:
839
830
  topology_files=topology_files,
840
831
  continental_polygons=continental_polygons,
841
832
  config=config,
842
- verbose=verbose,
843
833
  )
844
834
 
845
835
  tracker.initialize(starting_age)
@@ -148,6 +148,55 @@ class PointCloud:
148
148
  xyz = LatLon2XYZ(latlon)
149
149
  return cls(xyz=xyz, properties=properties or {})
150
150
 
151
+ @classmethod
152
+ def from_xyz(
153
+ cls,
154
+ xyz: np.ndarray,
155
+ properties: Optional[Dict[str, np.ndarray]] = None,
156
+ normalize_to_earth: bool = False
157
+ ) -> "PointCloud":
158
+ """
159
+ Create PointCloud from Cartesian XYZ coordinates.
160
+
161
+ This is a convenience classmethod that provides symmetry with from_latlon.
162
+ It can optionally normalize points to Earth's surface.
163
+
164
+ Parameters
165
+ ----------
166
+ xyz : np.ndarray
167
+ Cartesian coordinates, shape (N, 3).
168
+ properties : dict, optional
169
+ Properties to attach to the points.
170
+ normalize_to_earth : bool, default=False
171
+ If True, normalize points to Earth's radius (~6.3781e6 m).
172
+ Useful when input points are on a unit sphere.
173
+
174
+ Returns
175
+ -------
176
+ PointCloud
177
+ New PointCloud with the given XYZ coordinates.
178
+
179
+ Examples
180
+ --------
181
+ >>> # Points already at Earth's radius
182
+ >>> xyz = np.array([[6378100, 0, 0], [0, 6378100, 0]])
183
+ >>> cloud = PointCloud.from_xyz(xyz)
184
+ >>>
185
+ >>> # Points on unit sphere, scale to Earth
186
+ >>> xyz_unit = np.array([[1, 0, 0], [0, 1, 0]])
187
+ >>> cloud = PointCloud.from_xyz(xyz_unit, normalize_to_earth=True)
188
+ """
189
+ from .geometry import EARTH_RADIUS
190
+ xyz = np.asarray(xyz)
191
+
192
+ if normalize_to_earth:
193
+ # Normalize to unit sphere, then scale to Earth's radius
194
+ norms = np.linalg.norm(xyz, axis=1, keepdims=True)
195
+ norms = np.maximum(norms, 1e-10) # Avoid division by zero
196
+ xyz = xyz / norms * EARTH_RADIUS
197
+
198
+ return cls(xyz=xyz, properties=properties or {})
199
+
151
200
  def add_property(self, name: str, values: np.ndarray) -> None:
152
201
  """
153
202
  Add or update a property.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gtrack
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: GPlates-based Tracking of Lithosphere and Kinematics
5
5
  Author: S. Ghelichkhani
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gtrack"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "GPlates-based Tracking of Lithosphere and Kinematics"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -48,6 +48,67 @@ class TestPointCloud:
48
48
  distances = np.linalg.norm(cloud.xyz, axis=1)
49
49
  np.testing.assert_allclose(distances, EARTH_RADIUS, rtol=1e-10)
50
50
 
51
+ def test_from_xyz_basic(self):
52
+ """Test creating PointCloud via from_xyz classmethod."""
53
+ xyz = normalize_to_sphere(np.random.randn(50, 3))
54
+ cloud = PointCloud.from_xyz(xyz)
55
+
56
+ assert cloud.n_points == 50
57
+ np.testing.assert_allclose(cloud.xyz, xyz)
58
+ assert cloud.plate_ids is None
59
+ assert len(cloud.properties) == 0
60
+
61
+ def test_from_xyz_with_properties(self):
62
+ """Test from_xyz passes properties correctly."""
63
+ xyz = normalize_to_sphere(np.random.randn(30, 3))
64
+ props = {'depth': np.random.rand(30), 'temp': np.random.rand(30)}
65
+ cloud = PointCloud.from_xyz(xyz, properties=props)
66
+
67
+ assert cloud.n_points == 30
68
+ assert 'depth' in cloud.properties
69
+ assert 'temp' in cloud.properties
70
+
71
+ def test_from_xyz_normalize_to_earth(self):
72
+ """Test from_xyz with normalize_to_earth scales to Earth's radius."""
73
+ # Points on unit sphere
74
+ xyz_unit = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
75
+ cloud = PointCloud.from_xyz(xyz_unit, normalize_to_earth=True)
76
+
77
+ distances = np.linalg.norm(cloud.xyz, axis=1)
78
+ np.testing.assert_allclose(distances, EARTH_RADIUS, rtol=1e-10)
79
+
80
+ def test_from_xyz_normalize_arbitrary_radius(self):
81
+ """Test from_xyz normalizes points at arbitrary radii to Earth's radius."""
82
+ # Points at radius 100
83
+ xyz = np.array([[100.0, 0.0, 0.0], [0.0, 0.0, 50.0]])
84
+ cloud = PointCloud.from_xyz(xyz, normalize_to_earth=True)
85
+
86
+ distances = np.linalg.norm(cloud.xyz, axis=1)
87
+ np.testing.assert_allclose(distances, EARTH_RADIUS, rtol=1e-10)
88
+
89
+ # Direction should be preserved
90
+ direction_original = xyz / np.linalg.norm(xyz, axis=1, keepdims=True)
91
+ direction_result = cloud.xyz / np.linalg.norm(cloud.xyz, axis=1, keepdims=True)
92
+ np.testing.assert_allclose(direction_result, direction_original, atol=1e-10)
93
+
94
+ def test_from_xyz_no_normalize(self):
95
+ """Test from_xyz without normalization preserves coordinates exactly."""
96
+ xyz = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
97
+ cloud = PointCloud.from_xyz(xyz, normalize_to_earth=False)
98
+
99
+ np.testing.assert_array_equal(cloud.xyz, xyz)
100
+
101
+ def test_from_xyz_near_zero_vector(self):
102
+ """Test from_xyz handles near-zero vectors without crashing."""
103
+ xyz = np.array([[1e-15, 1e-15, 1e-15], [1.0, 0.0, 0.0]])
104
+ cloud = PointCloud.from_xyz(xyz, normalize_to_earth=True)
105
+
106
+ # Should not raise; second point should be at Earth's radius
107
+ distances = np.linalg.norm(cloud.xyz, axis=1)
108
+ np.testing.assert_allclose(distances[1], EARTH_RADIUS, rtol=1e-10)
109
+ # First point (near-zero) should still produce a finite result
110
+ assert np.all(np.isfinite(cloud.xyz[0]))
111
+
51
112
  def test_latlon_property(self):
52
113
  """Test that latlon property correctly converts from XYZ."""
53
114
  original_latlon = np.array([
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes