DeepSDFStruct 1.7.2__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.
Files changed (165) hide show
  1. DeepSDFStruct/SDF.py +1734 -0
  2. DeepSDFStruct/__init__.py +66 -0
  3. DeepSDFStruct/deep_sdf/__init__.py +69 -0
  4. DeepSDFStruct/deep_sdf/create_screenshots_from_plyfiles.py +65 -0
  5. DeepSDFStruct/deep_sdf/data.py +258 -0
  6. DeepSDFStruct/deep_sdf/metrics/__init__.py +14 -0
  7. DeepSDFStruct/deep_sdf/metrics/mesh_to_analytical.py +24 -0
  8. DeepSDFStruct/deep_sdf/models.py +197 -0
  9. DeepSDFStruct/deep_sdf/networks/__init__.py +8 -0
  10. DeepSDFStruct/deep_sdf/networks/analytic_round_cross.py +105 -0
  11. DeepSDFStruct/deep_sdf/networks/deep_sdf_decoder.py +155 -0
  12. DeepSDFStruct/deep_sdf/networks/hierarchical_deep_sdf_decoder.py +139 -0
  13. DeepSDFStruct/deep_sdf/networks/hierarchical_positional_sdf_decoder.py +190 -0
  14. DeepSDFStruct/deep_sdf/networks/resnet_positional_sdf_decoder.py +108 -0
  15. DeepSDFStruct/deep_sdf/nn_utils.py +70 -0
  16. DeepSDFStruct/deep_sdf/plotting.py +143 -0
  17. DeepSDFStruct/deep_sdf/reconstruction.py +165 -0
  18. DeepSDFStruct/deep_sdf/training.py +840 -0
  19. DeepSDFStruct/deep_sdf/workspace.py +363 -0
  20. DeepSDFStruct/design_of_experiments.py +235 -0
  21. DeepSDFStruct/flexicubes/__init__.py +15 -0
  22. DeepSDFStruct/flexicubes/flexicubes.py +1005 -0
  23. DeepSDFStruct/flexicubes/tables.py +2338 -0
  24. DeepSDFStruct/flexisquares/__init__.py +15 -0
  25. DeepSDFStruct/flexisquares/flexisquares.py +967 -0
  26. DeepSDFStruct/flexisquares/tables.py +63 -0
  27. DeepSDFStruct/lattice_structure.py +254 -0
  28. DeepSDFStruct/local_shapes.py +226 -0
  29. DeepSDFStruct/mesh.py +1184 -0
  30. DeepSDFStruct/optimization.py +357 -0
  31. DeepSDFStruct/parametrization.py +215 -0
  32. DeepSDFStruct/pretrained_models.py +128 -0
  33. DeepSDFStruct/sampling.py +670 -0
  34. DeepSDFStruct/sdf_operations.py +990 -0
  35. DeepSDFStruct/sdf_primitives.py +1416 -0
  36. DeepSDFStruct/splinepy_unitcells/__init__.py +35 -0
  37. DeepSDFStruct/splinepy_unitcells/chi_3D.py +1617 -0
  38. DeepSDFStruct/splinepy_unitcells/cross_lattice.py +165 -0
  39. DeepSDFStruct/splinepy_unitcells/double_lattice_extruded.py +298 -0
  40. DeepSDFStruct/splinepy_unitcells/snappy_3d.py +544 -0
  41. DeepSDFStruct/torch_spline.py +446 -0
  42. DeepSDFStruct/trained_models/analytic_round_cross/LatentCodes/latest.pth +0 -0
  43. DeepSDFStruct/trained_models/analytic_round_cross/ModelParameters/latest.pth +0 -0
  44. DeepSDFStruct/trained_models/analytic_round_cross/OptimizerParameters/latest.pth +0 -0
  45. DeepSDFStruct/trained_models/analytic_round_cross/specs.json +45 -0
  46. DeepSDFStruct/trained_models/chi_and_cross/LatentCodes/latest.pth +0 -0
  47. DeepSDFStruct/trained_models/chi_and_cross/Logs.pth +0 -0
  48. DeepSDFStruct/trained_models/chi_and_cross/ModelParameters/latest.pth +0 -0
  49. DeepSDFStruct/trained_models/chi_and_cross/OptimizerParameters/latest.pth +0 -0
  50. DeepSDFStruct/trained_models/chi_and_cross/specs.json +76 -0
  51. DeepSDFStruct/trained_models/primitives/LatentCodes/latest.pth +0 -0
  52. DeepSDFStruct/trained_models/primitives/ModelParameters/latest.pth +0 -0
  53. DeepSDFStruct/trained_models/primitives/OptimizerParameters/latest.pth +0 -0
  54. DeepSDFStruct/trained_models/primitives/specs.json +46 -0
  55. DeepSDFStruct/trained_models/primitives_2d/LatentCodes/latest.pth +0 -0
  56. DeepSDFStruct/trained_models/primitives_2d/ModelParameters/latest.pth +0 -0
  57. DeepSDFStruct/trained_models/primitives_2d/OptimizerParameters/latest.pth +0 -0
  58. DeepSDFStruct/trained_models/primitives_2d/specs.json +46 -0
  59. DeepSDFStruct/trained_models/round_cross/LatentCodes/latest.pth +0 -0
  60. DeepSDFStruct/trained_models/round_cross/Logs.pth +0 -0
  61. DeepSDFStruct/trained_models/round_cross/ModelParameters/latest.pth +0 -0
  62. DeepSDFStruct/trained_models/round_cross/OptimizerParameters/latest.pth +0 -0
  63. DeepSDFStruct/trained_models/round_cross/specs.json +45 -0
  64. DeepSDFStruct/trained_models/test_experiment/LatentCodes/latent_code_data_map.json +106 -0
  65. DeepSDFStruct/trained_models/test_experiment/specs.json +45 -0
  66. DeepSDFStruct/trained_models/test_experiment/training_summary.json +10 -0
  67. DeepSDFStruct/trained_models/test_experiment_hierarchical/LatentCodes/latent_code_data_map.json +106 -0
  68. DeepSDFStruct/trained_models/test_experiment_hierarchical/specs.json +45 -0
  69. DeepSDFStruct/trained_models/test_experiment_hierarchical/training_summary.json +10 -0
  70. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latent_code_data_map.json +5006 -0
  71. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latest.pth +0 -0
  72. DeepSDFStruct/trained_models/test_experiment_homogenization/Logs.png +0 -0
  73. DeepSDFStruct/trained_models/test_experiment_homogenization/ModelParameters/latest.pth +0 -0
  74. DeepSDFStruct/trained_models/test_experiment_homogenization/OptimizerParameters/latest.pth +0 -0
  75. DeepSDFStruct/trained_models/test_experiment_homogenization/specs.json +83 -0
  76. DeepSDFStruct/trained_models/test_experiment_homogenization/training_summary.json +10 -0
  77. DeepSDFStruct/utils.py +96 -0
  78. DeepSDFStruct/visualization/latent_viewer.py +211 -0
  79. DeepSDFStruct/visualization/latent_viewer_cli.py +8 -0
  80. benchmarks/generate_sdf_showcase.py +622 -0
  81. benchmarks/sdf_from_mesh_benchmark.py +52 -0
  82. benchmarks/sdf_from_mesh_comparison.md +33 -0
  83. benchmarks/sdf_showcase/operations/boolean/difference_sphere_box.png +0 -0
  84. benchmarks/sdf_showcase/operations/boolean/union_sphere_box.png +0 -0
  85. benchmarks/sdf_showcase/operations/transformations/circular_array_sphere.png +0 -0
  86. benchmarks/sdf_showcase/operations/transformations/dilate_sphere.png +0 -0
  87. benchmarks/sdf_showcase/operations/transformations/mirror_sphere.png +0 -0
  88. benchmarks/sdf_showcase/operations/transformations/repeat_sphere.png +0 -0
  89. benchmarks/sdf_showcase/operations/transformations/revolve_circle.png +0 -0
  90. benchmarks/sdf_showcase/operations/transformations/shell_sphere.png +0 -0
  91. benchmarks/sdf_showcase/operations/transformations/twist_torus.png +0 -0
  92. benchmarks/sdf_showcase/primitives/2D/circle.png +0 -0
  93. benchmarks/sdf_showcase/primitives/2D/equilateral_triangle.png +0 -0
  94. benchmarks/sdf_showcase/primitives/2D/hexagon.png +0 -0
  95. benchmarks/sdf_showcase/primitives/2D/polygon_pentagon.png +0 -0
  96. benchmarks/sdf_showcase/primitives/2D/rectangle.png +0 -0
  97. benchmarks/sdf_showcase/primitives/2D/rounded_rectangle.png +0 -0
  98. benchmarks/sdf_showcase/primitives/3D/box.png +0 -0
  99. benchmarks/sdf_showcase/primitives/3D/capped_cone.png +0 -0
  100. benchmarks/sdf_showcase/primitives/3D/capped_cylinder.png +0 -0
  101. benchmarks/sdf_showcase/primitives/3D/capsule.png +0 -0
  102. benchmarks/sdf_showcase/primitives/3D/cone.png +0 -0
  103. benchmarks/sdf_showcase/primitives/3D/corner_spheres.png +0 -0
  104. benchmarks/sdf_showcase/primitives/3D/cross_ms.png +0 -0
  105. benchmarks/sdf_showcase/primitives/3D/cylinder.png +0 -0
  106. benchmarks/sdf_showcase/primitives/3D/dodecahedron.png +0 -0
  107. benchmarks/sdf_showcase/primitives/3D/ellipsoid.png +0 -0
  108. benchmarks/sdf_showcase/primitives/3D/icosahedron.png +0 -0
  109. benchmarks/sdf_showcase/primitives/3D/octahedron.png +0 -0
  110. benchmarks/sdf_showcase/primitives/3D/pyramid.png +0 -0
  111. benchmarks/sdf_showcase/primitives/3D/rounded_box.png +0 -0
  112. benchmarks/sdf_showcase/primitives/3D/rounded_cone.png +0 -0
  113. benchmarks/sdf_showcase/primitives/3D/rounded_cylinder.png +0 -0
  114. benchmarks/sdf_showcase/primitives/3D/sphere.png +0 -0
  115. benchmarks/sdf_showcase/primitives/3D/tetrahedron.png +0 -0
  116. benchmarks/sdf_showcase/primitives/3D/torus.png +0 -0
  117. benchmarks/sdf_showcase/primitives/3D/wireframe_box.png +0 -0
  118. deepsdfstruct-1.7.2.dist-info/METADATA +44 -0
  119. deepsdfstruct-1.7.2.dist-info/RECORD +165 -0
  120. deepsdfstruct-1.7.2.dist-info/WHEEL +5 -0
  121. deepsdfstruct-1.7.2.dist-info/licenses/LICENSE +208 -0
  122. deepsdfstruct-1.7.2.dist-info/licenses/NOTICE +67 -0
  123. deepsdfstruct-1.7.2.dist-info/scm_file_list.json +178 -0
  124. deepsdfstruct-1.7.2.dist-info/scm_version.json +8 -0
  125. deepsdfstruct-1.7.2.dist-info/top_level.txt +4 -0
  126. docs/Makefile +20 -0
  127. docs/api_reference.rst +6 -0
  128. docs/conf.py +60 -0
  129. docs/index.rst +136 -0
  130. docs/make.bat +35 -0
  131. docs/qr_code_github.png +0 -0
  132. docs/qr_code_paper.png +0 -0
  133. docs/readme_images/example_output_01.png +0 -0
  134. docs/readme_images/example_output_02.png +0 -0
  135. docs/readme_images/example_output_03.png +0 -0
  136. docs/readme_images/example_output_04.png +0 -0
  137. tests/data/chairs/1005.obj +282 -0
  138. tests/data/chairs/1024.obj +222 -0
  139. tests/data/chairs/README.md +1 -0
  140. tests/data/circular_balls_test_case.stl +0 -0
  141. tests/data/cone.stl +1794 -0
  142. tests/data/example_disconnectd_mesh.inp +1784 -0
  143. tests/data/example_line_mesh.vtk +0 -0
  144. tests/data/stanford_bunny.stl +0 -0
  145. tests/data/sweep_test_case.stl +0 -0
  146. tests/test_DOE.py +49 -0
  147. tests/test_elongate_sdf.py +195 -0
  148. tests/test_flexisquares.py +187 -0
  149. tests/test_generate_dataset.py +76 -0
  150. tests/test_lattice_evaluation.py +137 -0
  151. tests/test_mesh_export.py +137 -0
  152. tests/test_mesh_functions.py +49 -0
  153. tests/test_networks.py +71 -0
  154. tests/test_pretrained_models.py +40 -0
  155. tests/test_reconstruction.py +101 -0
  156. tests/test_sdf_from_mesh.py +52 -0
  157. tests/test_sdf_functions.py +117 -0
  158. tests/test_sdf_primitives.py +668 -0
  159. tests/test_sdf_trimesh_comparison.py +482 -0
  160. tests/test_splinepy_unitcells.py +64 -0
  161. tests/test_structural_optimization.py +178 -0
  162. tests/test_torch_spline.py +216 -0
  163. tests/test_train_model.py +110 -0
  164. tests/test_training_data_ids.py +41 -0
  165. tests/tmp_outputs/temp_file.txt +0 -0
@@ -0,0 +1,63 @@
1
+ """
2
+ FlexiSquares Lookup Tables
3
+ ==========================
4
+
5
+ This module contains precomputed lookup tables for the FlexiSquares algorithm.
6
+ These tables encode the topology of all possible Marching Squares configurations.
7
+ """
8
+
9
+ # (0,1) ─ 2 ─ (1,1)
10
+ # | |
11
+ # 0 1
12
+ # | |
13
+ # (0,0) ─ 3 ─ (1,0)
14
+
15
+ # (2) ─ 2 ─ (3)
16
+ # | |
17
+ # 0 1
18
+ # | |
19
+ # (0) ─ 3 ─ (1)
20
+
21
+ # Edges are:
22
+ # e0: bottom → top along left side → (0,0)-(0,1) → corners (0,2)
23
+ # e1: bottom → top along right side → (1,0)-(1,1) → corners (1,3)
24
+ # e2: left → right along bottom → (0,0)-(1,0) → corners (0,1)
25
+ # e3: left → right along top → (0,1)-(1,1) → corners (2,3)
26
+ dmc_table = [
27
+ [[-1, -1], [-1, -1]], # 0: 0000 no intersections
28
+ [[0, 3], [-1, -1]], # 1: 0001
29
+ [[1, 3], [-1, -1]], # 2: 0010
30
+ [[0, 1], [-1, -1]], # 3: 0011
31
+ [[2, 0], [-1, -1]], # 4: 0100
32
+ [[2, 3], [-1, -1]], # 5: 0101
33
+ [[0, 3], [2, 1]], # 6: 0110 # ambigous (two lines)
34
+ [[1, 2], [-1, -1]], # 7: 0111
35
+ [[1, 2], [-1, -1]], # 8: 1000
36
+ [[0, 2], [3, 1]], # 9: 1001 # ambiguous (two lines)
37
+ [[2, 3], [-1, -1]], # 10: 1010
38
+ [[0, 2], [-1, -1]], # 11: 1011
39
+ [[0, 1], [-1, -1]], # 12: 1100
40
+ [[3, 1], [-1, -1]], # 13: 1101
41
+ [[0, 3], [-1, -1]], # 14: 1110
42
+ [[-1, -1], [-1, -1]], # 15: 1111 full fill (no contour)
43
+ ]
44
+ # connect center to ->
45
+ tet_table = [
46
+ [[-1, -1], [-1, -1]], # 0: 0000 no intersections
47
+ [[-1, -1], [-1, -1]], # 1: 0001
48
+ [[-1, -1], [-1, -1]], # 2: 0010
49
+ [[0, 1], [-1, -1]], # 3: 0011
50
+ [[-1, -1], [-1, -1]], # 4: 0100
51
+ [[2, 0], [-1, -1]], # 5: 0101
52
+ [[1, 2], [-1, -1]], # 6: 0110 # ambigous (two lines)
53
+ [[2, 0], [0, 1]], # 7: 0111
54
+ [[-1, -1], [-1, -1]], # 8: 1000
55
+ [[0, 3], [-1, -1]], # 9: 1001 # ambiguous (two lines)
56
+ [[1, 3], [-1, -1]], # 10: 1010
57
+ [[1, 3], [0, 1]], # 11: 1011
58
+ [[3, 2], [-1, -1]], # 12: 1100
59
+ [[3, 2], [2, 0]], # 13: 1101
60
+ [[1, 3], [3, 2]], # 14: 1110
61
+ [[-1, -1], [-1, -1]], # 15: 1111 full fill (no contour)
62
+ ]
63
+ num_vd_table = [0, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 0]
@@ -0,0 +1,254 @@
1
+ """
2
+ Lattice Structure Generation
3
+ =============================
4
+
5
+ This module provides tools for creating periodic lattice structures with
6
+ deformable geometries. Lattices are built by tiling a unit cell (microtile)
7
+ in a regular pattern and applying optional smooth deformations via B-splines.
8
+
9
+ The main class, LatticeSDFStruct, enables creation of complex microstructured
10
+ materials with spatially-varying properties, useful for applications in:
11
+ - Topology optimization
12
+ - Additive manufacturing
13
+ - Metamaterial design
14
+ - Lightweight structural components
15
+
16
+ Key Features
17
+ ------------
18
+ - Periodic tiling of arbitrary unit cell geometries
19
+ - Smooth deformations via spline-based mapping
20
+ - Spatially-varying parametrization (e.g., varying strut thickness)
21
+ - Support for 2D and 3D lattices
22
+ - Integration with boundary conditions and capping
23
+ """
24
+
25
+ import logging
26
+
27
+
28
+ import numpy as _np
29
+ import torch as _torch
30
+
31
+ from splinepy import BSpline as _BSpline
32
+ from .SDF import SDFBase as _SDFBase
33
+ import DeepSDFStruct
34
+ import gustaf as gus
35
+
36
+ logger = logging.getLogger(DeepSDFStruct.__name__)
37
+
38
+
39
+ class LatticeSDFStruct(_SDFBase):
40
+ """Helper class to facilitate the construction of periodic lattice SDF structures.
41
+
42
+ This class creates periodic lattice structures by tiling a unit cell geometry
43
+ (microtile) in a regular pattern and optionally deforming the result through
44
+ a spline-based mapping. The microtile can be parametrized to have spatially-
45
+ varying properties (e.g., thickness that varies across the structure).
46
+
47
+ The lattice is defined in parametric space [0,1]^d and can be mapped to
48
+ physical space through a deformation spline. Boundary conditions can be
49
+ applied to cap the structure at domain boundaries.
50
+
51
+ Parameters
52
+ ----------
53
+ tiling : list of int or int, optional
54
+ Number of repetitions of the microtile in each parametric dimension.
55
+ If an int, uses the same tiling in all dimensions.
56
+ microtile : SDFBase, optional
57
+ The unit cell geometry to be tiled. Should be defined in the
58
+ unit cube [0,1]^d.
59
+ parametrization : torch.nn.Module, optional
60
+ Function that provides spatially-varying parameters for the microtile
61
+ (e.g., varying strut thickness). Takes parametric coordinates and
62
+ returns parameter values.
63
+ cap_border_dict : CapBorderDict, optional
64
+ Dictionary specifying whether material should be added or removed
65
+ at domain faces (for capping the structure at boundaries).
66
+ cap_outside_of_unitcube : bool, default True
67
+ If True, caps geometry outside the unit cube.
68
+
69
+ Attributes
70
+ ----------
71
+ tiling : list of int
72
+ Number of tiles in each dimension.
73
+ microtile : SDFBase
74
+ The unit cell geometry.
75
+ geometric_dim : int
76
+ Geometric dimensionality (2 or 3).
77
+ parametric_dimension : int
78
+ Parametric dimensionality (equal to geometric_dim).
79
+
80
+ Notes
81
+ -----
82
+ The microtile should ideally have periodic boundary conditions to ensure
83
+ smooth connections between adjacent tiles. For parametrized microtiles,
84
+ the microtile must provide:
85
+ - `evaluation_points`: Points where parameters are evaluated
86
+ - `para_dim`: Dimensionality of the parameter space
87
+ - `_set_param`: Method to update parameters
88
+
89
+ Examples
90
+ --------
91
+ >>> from DeepSDFStruct.lattice_structure import LatticeSDFStruct
92
+ >>> from DeepSDFStruct.sdf_primitives import SphereSDF
93
+ >>> from DeepSDFStruct.torch_spline import TorchSpline
94
+ >>> import torch
95
+ >>>
96
+ >>> # Create a simple unit cell
97
+ >>> unit_cell = SphereSDF(center=[0.5, 0.5, 0.5], radius=0.3)
98
+ >>>
99
+ >>> # Create lattice with 3x3x3 tiling
100
+ >>> lattice = LatticeSDFStruct(
101
+ ... tiling=[3, 3, 3],
102
+ ... microtile=unit_cell
103
+ ... )
104
+ >>>
105
+ >>> # Query lattice SDF
106
+ >>> points = torch.rand(100, 3)
107
+ >>> distances = lattice(points)
108
+ """
109
+
110
+ def __init__(
111
+ self,
112
+ tiling: list[int] | int | None = None,
113
+ microtile: _SDFBase | None = None,
114
+ parametrization: _torch.nn.Module | None = None,
115
+ bounds=None,
116
+ ):
117
+ """Helper class to facilitate the construction of microstructures.
118
+
119
+ Parameters
120
+ ----------
121
+ deformation_function : spline
122
+ Outer function that describes the contour of the microstructured
123
+ geometry
124
+ tiling : list of integers
125
+ microtiles per parametric dimension
126
+ microtile : SDFBase
127
+ Representation of the building block defined in the unit cube
128
+ parametrization_function : Callable (optional)
129
+ Function to describe spline parameters
130
+ """
131
+ if not isinstance(parametrization, _torch.nn.Module):
132
+ raise TypeError("Parametrization must be of type _Parametrization")
133
+ super().__init__(parametrization=parametrization)
134
+ self.geometric_dim = len(tiling)
135
+ assert (
136
+ microtile.geometric_dim == self.geometric_dim
137
+ ), f"dimension of microtile ({self.microtile.geometric_dim}) does not match the tiling ({tiling})"
138
+ self.tiling = tiling
139
+ self.microtile = microtile
140
+ if bounds is not None:
141
+ if isinstance(bounds, (list, tuple)):
142
+ bounds = _torch.tensor(
143
+ bounds, device=microtile.get_device(), dtype=microtile.get_dtype()
144
+ )
145
+ else:
146
+ match self.geometric_dim:
147
+ case 2:
148
+ bounds = _torch.tensor(
149
+ [[0.0, 0.0], [1.0, 1.0]],
150
+ device=microtile.get_device(),
151
+ dtype=microtile.get_dtype(),
152
+ )
153
+ case 3:
154
+ bounds = _torch.tensor(
155
+ [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]],
156
+ device=microtile.get_device(),
157
+ dtype=microtile.get_dtype(),
158
+ )
159
+ case _:
160
+ raise ValueError(
161
+ f"Geometric dimension must be either 2 or 3. Got {self.geometric_dim}"
162
+ )
163
+ self.register_buffer("bounds", bounds)
164
+
165
+ @property
166
+ def parametric_dimension(self):
167
+ return len(self.tiling)
168
+
169
+ def _get_domain_bounds(self):
170
+ return self.get_buffer("bounds")
171
+
172
+ def _compute(self, samples: _torch.Tensor):
173
+ """Function, that - if required - parametrizes the microtiles.
174
+
175
+ In order to use said function, the Microtile needs to provide a couple
176
+ of attributes:
177
+
178
+ - evaluation_points - a list of points defined in the unit cube
179
+ that will be evaluated in the parametrization function to provide
180
+ the required set of data points
181
+ - para_dim - dimensionality of the parametrization
182
+ function and number of design variables for said microtile
183
+
184
+ Parameters
185
+ ----------
186
+ None
187
+
188
+ Returns
189
+ -------
190
+ : Callable
191
+ Function that describes the local tile parameters
192
+ """
193
+ bounds = self._get_domain_bounds()
194
+ if self.parametrization is not None:
195
+ samples_parameter_space = (samples - bounds[0]) / (bounds[1] - bounds[0])
196
+ samples_parameter_space = _torch.clamp(samples_parameter_space, 0.0, 1.0)
197
+ parameters = self.parametrization(samples_parameter_space)
198
+ self.microtile._set_param(parameters)
199
+
200
+ queries_transformed = _torch.zeros_like(samples)
201
+ for i_dim, t in enumerate(self.tiling):
202
+ queries_transformed[:, i_dim] = transform(
203
+ samples[:, i_dim], t, bounds=bounds[:, i_dim]
204
+ )
205
+ sdf_values = self.microtile(queries_transformed)
206
+
207
+ return sdf_values
208
+
209
+ def _sanity_check(self):
210
+ """Check all members and consistency of user data.
211
+
212
+ Parameters
213
+ ----------
214
+ updated_properties : bool
215
+ Sets the updated_properties variable to value, which indicates,
216
+ wheither the microstructure needs to be rebuilt
217
+
218
+ Returns
219
+ -------
220
+ passes: bool
221
+ """
222
+ pass
223
+
224
+ def plot_samples(self, samples, sdf_values):
225
+ vp = gus.Vertices(vertices=samples)
226
+ vp.vertex_data["distance"] = sdf_values
227
+ vp.show_options["cmap"] = "coolwarm"
228
+ gus.show(vp, axes=1)
229
+
230
+ def plot_intermesh(self, verts: _torch.Tensor, faces: _torch.Tensor):
231
+ gus_faces = gus.Faces(vertices=verts.cpu().detach(), faces=faces.cpu().detach())
232
+ gus.show(gus_faces, axes=1)
233
+
234
+
235
+ def constantLatvec(value):
236
+ return _BSpline([0, 0, 0], [[-1, 1], [-1, 1], [-1, 1]], [value])
237
+
238
+
239
+ def transform(x, t, bounds=[0, 1]):
240
+ # transform x from [bounds[0], bounds[1]] to [-1,1]
241
+ x_norm = (x - bounds[0]) / (bounds[1] - bounds[0])
242
+ x_transformed = 2 * _torch.abs(t * x_norm / 2 - _torch.floor((t * x_norm + 1) / 2))
243
+ return 2 * x_transformed - 1
244
+
245
+
246
+ def check_tiling_input(tiling):
247
+ if isinstance(tiling, list) or isinstance(tiling, tuple):
248
+ if len(tiling) != 3:
249
+ raise ValueError("Tiling must be a list of 3 integers")
250
+ tiling = _np.array(tiling)
251
+ elif isinstance(tiling, int):
252
+ tiling = _np.array([tiling, tiling, tiling])
253
+ else:
254
+ raise ValueError("Tiling must be a list or an integer")
@@ -0,0 +1,226 @@
1
+ """
2
+ Local Shapes for Microstructure Design
3
+ ======================================
4
+
5
+ This module provides tools for creating microstructured geometries with
6
+ localized shape variations. Unlike uniform lattices, local shapes allow
7
+ different regions of a structure to have distinct geometric properties,
8
+ enabling graded and functionally-graded materials.
9
+
10
+ Key Features
11
+ ------------
12
+
13
+ LocalShapesSDF
14
+ A specialized SDF class for microstructures with spatially-varying
15
+ unit cell shapes. Enables:
16
+ - Region-specific unit cell geometries
17
+ - Smooth transitions between different shapes
18
+ - Integration with parametrization functions
19
+ - Support for 2D and 3D microstructures
20
+
21
+ The module extends the lattice structure capabilities to support more
22
+ complex, heterogeneous microstructural designs where different regions
23
+ may require different geometric features (e.g., varying strut patterns,
24
+ different connectivity, or distinct topologies).
25
+
26
+ This is particularly useful for:
27
+ - Multi-material structures
28
+ - Functionally graded materials
29
+ - Transition zones between different lattice types
30
+ - Adaptive microstructures
31
+
32
+ Examples
33
+ --------
34
+ Create a microstructure with local shape variations::
35
+
36
+ from DeepSDFStruct.local_shapes import LocalShapesSDF
37
+ from DeepSDFStruct.sdf_primitives import SphereSDF
38
+
39
+ # Define unit cell
40
+ unit_cell = SphereSDF([0.5, 0.5, 0.5], radius=0.3)
41
+
42
+ # Create local shapes structure
43
+ local_struct = LocalShapesSDF(
44
+ tiling=[3, 3, 3],
45
+ unit_cell=unit_cell,
46
+ parametrization=param_func,
47
+ bounds=torch.tensor([[-1, -1, -1], [1, 1, 1]])
48
+ )
49
+ """
50
+
51
+ import logging
52
+
53
+
54
+ import numpy as _np
55
+ import torch as _torch
56
+
57
+ from .SDF import SDFBase as _SDFBase
58
+ from .SDF import CapBorderDict
59
+ import DeepSDFStruct
60
+ from DeepSDFStruct.lattice_structure import check_tiling_input, transform
61
+
62
+ logger = logging.getLogger(DeepSDFStruct.__name__)
63
+
64
+
65
+ class LocalShapesSDF(_SDFBase):
66
+ """Helper class to facilitatae the construction of Lattice SDF Structures."""
67
+
68
+ def __init__(
69
+ self,
70
+ tiling: list[int] | int | None = None,
71
+ unit_cell: _SDFBase | None = None,
72
+ parametrization: _torch.nn.Module | None = None,
73
+ bounds=_torch.tensor([[-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]]),
74
+ ):
75
+ """Helper class to facilitatae the construction of microstructures.
76
+
77
+ Parameters
78
+ ----------
79
+ deformation_function : spline
80
+ Outer function that describes the contour of the microstructured
81
+ geometry
82
+ tiling : list of integers
83
+ microtiles per parametric dimension
84
+ microtile : SDFBase
85
+ Representation of the building block defined in the unit cube
86
+ parametrization_function : Callable (optional)
87
+ Function to describe spline parameters
88
+ """
89
+ if not isinstance(parametrization, _torch.nn.Module):
90
+ raise TypeError("Parametrization must be of type _Parametrization")
91
+ check_tiling_input(tiling)
92
+ super().__init__(parametrization=parametrization)
93
+ self.tiling = (
94
+ [tiling] * unit_cell.geometric_dim if isinstance(tiling, int) else tiling
95
+ )
96
+ self.unit_cell = unit_cell
97
+ self.bounds = bounds
98
+
99
+ @property
100
+ def tiling(self):
101
+ """Number of microtiles per parametric dimension.
102
+
103
+ Parameters
104
+ ----------
105
+ None
106
+
107
+ Returns
108
+ -------
109
+ tiling : list<int>
110
+ """
111
+ if hasattr(self, "_tiling"):
112
+ return self._tiling
113
+ else:
114
+ return None
115
+
116
+ @tiling.setter
117
+ def tiling(self, tiling):
118
+ """Setter for the tiling attribute, defining the number of microtiles
119
+ per parametric dimension.
120
+
121
+ Parameters
122
+ ----------
123
+ tiling : int / list<int>
124
+ Number of tiles for each dimension respectively
125
+ Returns
126
+ -------
127
+ None
128
+ """
129
+ if (
130
+ not isinstance(tiling, list)
131
+ and not isinstance(tiling, int)
132
+ and not isinstance(tiling, tuple)
133
+ ):
134
+ raise ValueError(
135
+ "Tiling mus be either list of integers of integer " "value"
136
+ )
137
+ self._tiling = tiling
138
+
139
+ @property
140
+ def unit_cell(self):
141
+ """Microtile that is either a spline, a list of splines, or a class
142
+ that provides a `create_tile` function."""
143
+ if hasattr(self, "_microtile"):
144
+ return self._microtile
145
+ else:
146
+ self._logi(
147
+ "microtile is empty. "
148
+ "Please checkout splinepy.microstructure.tiles.show() for "
149
+ "predefined tile collections."
150
+ )
151
+ return None
152
+
153
+ @unit_cell.setter
154
+ def unit_cell(self, microtile):
155
+ """Setter for microtile.
156
+
157
+ Microtile must be either a spline, a list of splines, or a class that
158
+ provides (at least) a `create_tile` function and a `dim` member.
159
+
160
+ Parameters
161
+ ----------
162
+ microtile : spline / list<splines> / user-object
163
+ arbitrary long list of splines that define the microtile
164
+
165
+ Returns
166
+ -------
167
+ None
168
+ """
169
+ # place single tiles into a list to provide common interface
170
+ if not isinstance(microtile, _SDFBase):
171
+ raise TypeError(f"Microtile must be SDF, not {type(microtile)}")
172
+ # Assign Microtile object to member variable
173
+ self._microtile = microtile
174
+
175
+ def _get_domain_bounds(self):
176
+ return self.bounds
177
+
178
+ def _compute(self, samples: _torch.Tensor):
179
+ """Function, that - if required - parametrizes the microtiles.
180
+
181
+ In order to use said function, the Microtile needs to provide a couple
182
+ of attributes:
183
+
184
+ - evaluation_points - a list of points defined in the unit cube
185
+ that will be evaluated in the parametrization function to provide
186
+ the required set of data points
187
+ - para_dim - dimensionality of the parametrization
188
+ function and number of design variables for said microtile
189
+
190
+ Parameters
191
+ ----------
192
+ None
193
+
194
+ Returns
195
+ -------
196
+ : Callable
197
+ Function that describes the local tile parameters
198
+ """
199
+ if self.parametrization is not None:
200
+ parameters = self.parametrization(samples)
201
+ self.unit_cell._set_param(parameters)
202
+
203
+ queries_transformed = _torch.zeros_like(samples)
204
+ if self.unit_cell.geometric_dim == 2:
205
+ tx, ty = self._tiling
206
+ queries_transformed[:, 0] = transform(
207
+ samples[:, 0], tx, bounds=self.bounds[:, 0]
208
+ )
209
+ queries_transformed[:, 1] = transform(
210
+ samples[:, 1], ty, bounds=self.bounds[:, 1]
211
+ )
212
+ elif self.unit_cell.geometric_dim == 3:
213
+ tx, ty, tz = self._tiling
214
+ queries_transformed[:, 0] = transform(
215
+ samples[:, 0], tx, bounds=self.bounds[:, 0]
216
+ )
217
+ queries_transformed[:, 1] = transform(
218
+ samples[:, 1], ty, bounds=self.bounds[:, 1]
219
+ )
220
+ queries_transformed[:, 2] = transform(
221
+ samples[:, 2], tz, bounds=self.bounds[:, 2]
222
+ )
223
+
224
+ sdf_values = self.unit_cell(queries_transformed)
225
+
226
+ return sdf_values