goad-py 0.4.1__tar.gz → 0.4.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.

Potentially problematic release.


This version of goad-py might be problematic. Click here for more details.

Files changed (120) hide show
  1. {goad_py-0.4.1 → goad_py-0.4.2}/PKG-INFO +1 -1
  2. {goad_py-0.4.1 → goad_py-0.4.2}/pyproject.toml +1 -1
  3. {goad_py-0.4.1 → goad_py-0.4.2}/src/beam.rs +1 -1
  4. {goad_py-0.4.1 → goad_py-0.4.2}/src/geom.rs +116 -5
  5. {goad_py-0.4.1 → goad_py-0.4.2}/src/problem.rs +4 -1
  6. {goad_py-0.4.1 → goad_py-0.4.2}/src/settings.rs +1 -1
  7. {goad_py-0.4.1 → goad_py-0.4.2}/tests/fixed_orientation_tests.rs +1 -1
  8. {goad_py-0.4.1 → goad_py-0.4.2}/.github/workflows/python.yml +0 -0
  9. {goad_py-0.4.1 → goad_py-0.4.2}/.github/workflows/rust.yml +0 -0
  10. {goad_py-0.4.1 → goad_py-0.4.2}/.gitignore +0 -0
  11. {goad_py-0.4.1 → goad_py-0.4.2}/Cargo.lock +0 -0
  12. {goad_py-0.4.1 → goad_py-0.4.2}/Cargo.toml +0 -0
  13. {goad_py-0.4.1 → goad_py-0.4.2}/LICENSE +0 -0
  14. {goad_py-0.4.1 → goad_py-0.4.2}/README-python.md +0 -0
  15. {goad_py-0.4.1 → goad_py-0.4.2}/README.md +0 -0
  16. {goad_py-0.4.1 → goad_py-0.4.2}/blender/addon/__init__.py +0 -0
  17. {goad_py-0.4.1 → goad_py-0.4.2}/blender/addon/goad_py/__init__.py +0 -0
  18. {goad_py-0.4.1 → goad_py-0.4.2}/blender/addon.zip +0 -0
  19. {goad_py-0.4.1 → goad_py-0.4.2}/blender/build.sh +0 -0
  20. {goad_py-0.4.1 → goad_py-0.4.2}/blender/dev.blend +0 -0
  21. {goad_py-0.4.1 → goad_py-0.4.2}/config/default.toml +0 -0
  22. {goad_py-0.4.1 → goad_py-0.4.2}/config_editor.sh +0 -0
  23. {goad_py-0.4.1 → goad_py-0.4.2}/examples/clip.rs +0 -0
  24. {goad_py-0.4.1 → goad_py-0.4.2}/examples/clip_test.rs +0 -0
  25. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/8col.obj +0 -0
  26. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/clip_test.obj +0 -0
  27. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/concave1.obj +0 -0
  28. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/concave2.obj +0 -0
  29. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/cone.obj +0 -0
  30. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/cube.obj +0 -0
  31. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/cube2.obj +0 -0
  32. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/cube_inside_cube.obj +0 -0
  33. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/cube_inside_hex.obj +0 -0
  34. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/cube_inside_ico.obj +0 -0
  35. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/cubes.obj +0 -0
  36. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex.obj +0 -0
  37. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex2.obj +0 -0
  38. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex3.obj +0 -0
  39. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex4.obj +0 -0
  40. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex5.obj +0 -0
  41. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex6.obj +0 -0
  42. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex7.obj +0 -0
  43. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex_20_30_30.obj +0 -0
  44. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex_distort.obj +0 -0
  45. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex_hollow.obj +0 -0
  46. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/hex_indented.obj +0 -0
  47. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/icosphere1.obj +0 -0
  48. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/multiple.obj +0 -0
  49. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/multiple2.obj +0 -0
  50. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/multiple3.obj +0 -0
  51. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/octo.obj +0 -0
  52. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/octo2.obj +0 -0
  53. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/para.obj +0 -0
  54. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/para_rough1.obj +0 -0
  55. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/plane_xy.obj +0 -0
  56. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/plane_yz.obj +0 -0
  57. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/plate.obj +0 -0
  58. {goad_py-0.4.1 → goad_py-0.4.2}/examples/data/plate_distort.obj +0 -0
  59. {goad_py-0.4.1 → goad_py-0.4.2}/examples/diff-debug.rs +0 -0
  60. {goad_py-0.4.1 → goad_py-0.4.2}/examples/diff-test.rs +0 -0
  61. {goad_py-0.4.1 → goad_py-0.4.2}/examples/distortion.rs +0 -0
  62. {goad_py-0.4.1 → goad_py-0.4.2}/examples/multi-problem.rs +0 -0
  63. {goad_py-0.4.1 → goad_py-0.4.2}/examples/problem-diff.rs +0 -0
  64. {goad_py-0.4.1 → goad_py-0.4.2}/examples/problem1.rs +0 -0
  65. {goad_py-0.4.1 → goad_py-0.4.2}/examples/projection-debug.rs +0 -0
  66. {goad_py-0.4.1 → goad_py-0.4.2}/examples/projection1.rs +0 -0
  67. {goad_py-0.4.1 → goad_py-0.4.2}/examples/projection2.rs +0 -0
  68. {goad_py-0.4.1 → goad_py-0.4.2}/examples/projection_multi.rs +0 -0
  69. {goad_py-0.4.1 → goad_py-0.4.2}/examples/propagate-summation.rs +0 -0
  70. {goad_py-0.4.1 → goad_py-0.4.2}/examples/propagate.rs +0 -0
  71. {goad_py-0.4.1 → goad_py-0.4.2}/examples/remainder.rs +0 -0
  72. {goad_py-0.4.1 → goad_py-0.4.2}/examples/simplify.rs +0 -0
  73. {goad_py-0.4.1 → goad_py-0.4.2}/examples/vector_scale.rs +0 -0
  74. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/.gitignore +0 -0
  75. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/CLAUDE.md +0 -0
  76. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/Cargo.toml +0 -0
  77. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/DISTRIBUTION.md +0 -0
  78. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/README-python.md +0 -0
  79. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/build_and_test.sh +0 -0
  80. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/build_wheels_local.sh +0 -0
  81. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/convergence.py +0 -0
  82. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/convergence_example.py +0 -0
  83. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/goad_py.pyi +0 -0
  84. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/multiproblem_example.py +0 -0
  85. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/publish_test.sh +0 -0
  86. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/python/goad_py/__init__.py +0 -0
  87. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/python/goad_py/convergence.py +0 -0
  88. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/python/goad_py/goad_py.pyi +0 -0
  89. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/release.sh +0 -0
  90. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/simple_example.py +0 -0
  91. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/src/lib.rs +0 -0
  92. {goad_py-0.4.1 → goad_py-0.4.2}/goad-py/test_wheels.sh +0 -0
  93. {goad_py-0.4.1 → goad_py-0.4.2}/python/goad_py/__init__.py +0 -0
  94. {goad_py-0.4.1 → goad_py-0.4.2}/python/goad_py/convergence.py +0 -0
  95. {goad_py-0.4.1 → goad_py-0.4.2}/python/goad_py/goad_py.pyi +0 -0
  96. {goad_py-0.4.1 → goad_py-0.4.2}/setup.sh +0 -0
  97. {goad_py-0.4.1 → goad_py-0.4.2}/src/_quickstart.rs +0 -0
  98. {goad_py-0.4.1 → goad_py-0.4.2}/src/bins.rs +0 -0
  99. {goad_py-0.4.1 → goad_py-0.4.2}/src/clip.rs +0 -0
  100. {goad_py-0.4.1 → goad_py-0.4.2}/src/containment.rs +0 -0
  101. {goad_py-0.4.1 → goad_py-0.4.2}/src/diff.rs +0 -0
  102. {goad_py-0.4.1 → goad_py-0.4.2}/src/distortion.rs +0 -0
  103. {goad_py-0.4.1 → goad_py-0.4.2}/src/field.rs +0 -0
  104. {goad_py-0.4.1 → goad_py-0.4.2}/src/fresnel.rs +0 -0
  105. {goad_py-0.4.1 → goad_py-0.4.2}/src/helpers.rs +0 -0
  106. {goad_py-0.4.1 → goad_py-0.4.2}/src/lib.rs +0 -0
  107. {goad_py-0.4.1 → goad_py-0.4.2}/src/main.rs +0 -0
  108. {goad_py-0.4.1 → goad_py-0.4.2}/src/multiproblem.rs +0 -0
  109. {goad_py-0.4.1 → goad_py-0.4.2}/src/orientation.rs +0 -0
  110. {goad_py-0.4.1 → goad_py-0.4.2}/src/output.rs +0 -0
  111. {goad_py-0.4.1 → goad_py-0.4.2}/src/params.rs +0 -0
  112. {goad_py-0.4.1 → goad_py-0.4.2}/src/powers.rs +0 -0
  113. {goad_py-0.4.1 → goad_py-0.4.2}/src/result.rs +0 -0
  114. {goad_py-0.4.1 → goad_py-0.4.2}/src/snell.rs +0 -0
  115. {goad_py-0.4.1 → goad_py-0.4.2}/template/custom_bins.toml +0 -0
  116. {goad_py-0.4.1 → goad_py-0.4.2}/template/goad_pbs.sh +0 -0
  117. {goad_py-0.4.1 → goad_py-0.4.2}/tests/helpers.rs +0 -0
  118. {goad_py-0.4.1 → goad_py-0.4.2}/tests/test_data/fixed_hex_30_20_20_mueller_scatgrid +0 -0
  119. {goad_py-0.4.1 → goad_py-0.4.2}/tests/test_data/fixed_hex_30_30_30_mueller_scatgrid +0 -0
  120. {goad_py-0.4.1 → goad_py-0.4.2}/tests/test_data/hex.obj +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: goad-py
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Topic :: Scientific/Engineering :: Physics
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "goad-py"
7
- version = "0.4.1"
7
+ version = "0.4.2"
8
8
  description = "Physical optics light scattering computation"
9
9
  authors = [{name = "Harry Ballington", email = "ballington@uni-wuppertal.de"}]
10
10
  license = {text = "GPL-3.0"}
@@ -360,7 +360,7 @@ fn get_ampl(
360
360
  let arg = dist * wavenumber * n1.re; // optical path length
361
361
  ampl *= Complex::new(arg.cos(), arg.sin()); // apply distance phase factor
362
362
 
363
- let dist_sqrt = dist.signum() * dist.abs().sqrt(); // TODO: improve this
363
+ let dist_sqrt = dist.abs().sqrt(); // TODO: improve this
364
364
 
365
365
  let absorbed_intensity = Field::ampl_intensity(&ampl)
366
366
  * (1.0 - (-2.0 * wavenumber * n1.im * dist_sqrt).exp().powi(2));
@@ -721,6 +721,14 @@ impl Face {
721
721
  ) -> Result<Self> {
722
722
  Ok(Face::Simple(FaceData::new(exterior, parent_id, indices)?))
723
723
  }
724
+
725
+ /// Get a reference to the exterior vertices of the face
726
+ pub fn exterior_ref(&self) -> &[Point3<f32>] {
727
+ match self {
728
+ Face::Simple(data) => &data.exterior,
729
+ Face::Complex { data, .. } => &data.exterior,
730
+ }
731
+ }
724
732
 
725
733
  pub fn new_complex(
726
734
  exterior: Vec<Point3<f32>>,
@@ -1156,11 +1164,16 @@ impl Geom {
1156
1164
  }
1157
1165
  }
1158
1166
 
1159
- Ok(Self {
1167
+ let geom = Self {
1160
1168
  num_shapes: shapes.len(),
1161
1169
  shapes,
1162
1170
  containment_graph,
1163
- })
1171
+ };
1172
+
1173
+ // Validate the loaded geometry
1174
+ geom.validate()?;
1175
+
1176
+ Ok(geom)
1164
1177
  }
1165
1178
 
1166
1179
  fn shapes_from_models(models: Vec<Model>) -> Result<Vec<Shape>> {
@@ -1220,12 +1233,49 @@ impl Geom {
1220
1233
  (Point3::from(min), Point3::from(max))
1221
1234
  }
1222
1235
 
1236
+ /// Computes the scale factor that would be used to rescale the geometry
1237
+ /// so that the largest dimension is 1.0
1238
+ pub fn compute_scale_factor(&self) -> f32 {
1239
+ let bounds = self.bounds();
1240
+ let max_dim = bounds.1.iter().fold(0.0, |acc: f32, &x| acc.max(x));
1241
+ 1.0 / max_dim
1242
+ }
1243
+
1244
+ /// Validates the geometry to ensure all faces will work correctly after scaling
1245
+ fn validate(&self) -> Result<()> {
1246
+ // Compute the scale factor that will be applied
1247
+ let scale_factor = self.compute_scale_factor();
1248
+
1249
+ // Validate each shape and face
1250
+ for (shape_idx, shape) in self.shapes.iter().enumerate() {
1251
+ for (face_idx, face) in shape.faces.iter().enumerate() {
1252
+ let vertices = face.exterior_ref();
1253
+
1254
+ // Check that we have at least one valid vertex pair after scaling
1255
+ validate_vertex_pair_exists(vertices, scale_factor)
1256
+ .map_err(|e| anyhow::anyhow!(
1257
+ "Shape {} Face {}: {}",
1258
+ shape_idx, face_idx, e
1259
+ ))?;
1260
+
1261
+ // Check planarity for faces with >3 vertices
1262
+ if vertices.len() > 3 {
1263
+ validate_planarity(vertices)
1264
+ .map_err(|e| anyhow::anyhow!(
1265
+ "Shape {} Face {}: {}",
1266
+ shape_idx, face_idx, e
1267
+ ))?;
1268
+ }
1269
+ }
1270
+ }
1271
+
1272
+ Ok(())
1273
+ }
1274
+
1223
1275
  /// Rescales the geometry so that the largest dimension is 1. Returns the
1224
1276
  /// scaling factor.
1225
1277
  pub fn rescale(&mut self) -> f32 {
1226
- let bounds = self.bounds();
1227
- let max_dim = bounds.1.iter().fold(0.0, |acc: f32, &x| acc.max(x));
1228
- let scale = 1.0 / max_dim;
1278
+ let scale = self.compute_scale_factor();
1229
1279
 
1230
1280
  for shape in self.shapes.iter_mut() {
1231
1281
  shape.rescale(scale);
@@ -1522,3 +1572,64 @@ pub fn translate(verts: &[Point3<f32>], center_of_mass: &Point3<f32>) -> Vec<Vec
1522
1572
  .map(|point| point.coords - center_of_mass.coords)
1523
1573
  .collect()
1524
1574
  }
1575
+
1576
+ /// Validates that a face has at least one pair of vertices with sufficient separation after scaling
1577
+ fn validate_vertex_pair_exists(vertices: &[Point3<f32>], scale_factor: f32) -> Result<()> {
1578
+ let min_original_distance = settings::VEC_LENGTH_THRESHOLD / scale_factor;
1579
+
1580
+ for i in 0..vertices.len() {
1581
+ for j in (i + 1)..vertices.len() {
1582
+ if (vertices[j] - vertices[i]).magnitude() > min_original_distance {
1583
+ return Ok(());
1584
+ }
1585
+ }
1586
+ }
1587
+
1588
+ Err(anyhow::anyhow!(
1589
+ "No vertex pair will have distance > {} after scaling by {} (minimum required: {})",
1590
+ settings::VEC_LENGTH_THRESHOLD, scale_factor, min_original_distance
1591
+ ))
1592
+ }
1593
+
1594
+ /// Validates that all vertices of a face lie on the same plane (within tolerance)
1595
+ fn validate_planarity(vertices: &[Point3<f32>]) -> Result<()> {
1596
+ let midpoint = vertices.iter().fold(Point3::origin(), |acc, v| acc + v.coords) / vertices.len() as f32;
1597
+
1598
+ // Find first valid vertex pair (we know one exists from previous validation)
1599
+ let mut v1 = None;
1600
+ let mut v2 = None;
1601
+ for i in 0..vertices.len() {
1602
+ for j in (i + 1)..vertices.len() {
1603
+ // Using a very small threshold just to avoid degenerate cases
1604
+ if (vertices[j] - vertices[i]).magnitude() > 1e-6 {
1605
+ v1 = Some(vertices[i]);
1606
+ v2 = Some(vertices[j]);
1607
+ break;
1608
+ }
1609
+ }
1610
+ if v1.is_some() {
1611
+ break;
1612
+ }
1613
+ }
1614
+
1615
+ let v1 = v1.unwrap();
1616
+ let v2 = v2.unwrap();
1617
+
1618
+ let u = v2 - v1;
1619
+ let v = midpoint - v1;
1620
+ let normal = u.cross(&v).normalize();
1621
+
1622
+ // Check all vertices are on the same plane
1623
+ for (i, &vertex) in vertices.iter().enumerate() {
1624
+ let distance = (vertex - v1).dot(&normal).abs();
1625
+
1626
+ if distance > settings::COLINEAR_THRESHOLD {
1627
+ return Err(anyhow::anyhow!(
1628
+ "Non-planar face: vertex {} deviates {} from plane (threshold: {})",
1629
+ i, distance, settings::COLINEAR_THRESHOLD
1630
+ ));
1631
+ }
1632
+ }
1633
+
1634
+ Ok(())
1635
+ }
@@ -211,7 +211,10 @@ impl Problem {
211
211
 
212
212
  // TODO move this outside the function
213
213
  for (i, ampl) in ampl_far_field.iter().enumerate() {
214
- total_ampl_far_field[i] += ampl;
214
+ // Check if any component of the amplitude matrix contains NaN
215
+ if ampl.iter().all(|c| c.re.is_finite() && c.im.is_finite()) {
216
+ total_ampl_far_field[i] += ampl;
217
+ }
215
218
  }
216
219
  }
217
220
 
@@ -19,7 +19,7 @@ pub const CLIP_TOLERANCE: f32 = 1e6;
19
19
  /// Minimum absolute value of the dot product of two vectors to be considered colinear.
20
20
  pub const COLINEAR_THRESHOLD: f32 = 0.001;
21
21
  /// Minimum vector length (in geometry units) to be considered non-degenerate.
22
- pub const VEC_LENGTH_THRESHOLD: f32 = 0.01;
22
+ pub const VEC_LENGTH_THRESHOLD: f32 = 0.001;
23
23
  /// Minimum distance traversed by ray to intersection. Intersections closer than this are ignored.
24
24
  pub const RAYCAST_MINIMUM_DISTANCE: f32 = 0.01;
25
25
  /// Tolerance for diffraction computations, used to avoid divide by zero errors.
@@ -11,7 +11,7 @@ use num_complex::Complex32;
11
11
  pub mod helpers;
12
12
  // Tolerance for comparing Mueller matrix elements
13
13
  const FRAC_TOL: f32 = 1e-4; // fractional error
14
- const ABS_TOL: f32 = 1e3; // absolute error
14
+ const ABS_TOL: f32 = 1e4; // absolute error
15
15
 
16
16
  #[test]
17
17
  fn fixed_hex_30_30_30() {
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
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
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
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
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