luminarycloud 0.19.0__py3-none-any.whl → 0.22.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.
Files changed (110) hide show
  1. luminarycloud/__init__.py +5 -1
  2. luminarycloud/_client/client.py +7 -0
  3. luminarycloud/_client/http_client.py +10 -8
  4. luminarycloud/_feature_flag.py +22 -0
  5. luminarycloud/_helpers/_create_simulation.py +7 -2
  6. luminarycloud/_helpers/_upload_mesh.py +1 -0
  7. luminarycloud/_helpers/_wait_for_mesh.py +6 -5
  8. luminarycloud/_helpers/_wait_for_simulation.py +3 -3
  9. luminarycloud/_helpers/download.py +3 -1
  10. luminarycloud/_helpers/pagination.py +62 -0
  11. luminarycloud/_helpers/proto_decorator.py +13 -5
  12. luminarycloud/_helpers/upload.py +18 -12
  13. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2.py +55 -0
  14. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2.pyi +52 -0
  15. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2_grpc.py +72 -0
  16. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2_grpc.pyi +35 -0
  17. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +168 -124
  18. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +133 -4
  19. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +66 -0
  20. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +20 -0
  21. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +8 -8
  22. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +5 -5
  23. luminarycloud/_proto/api/v0/luminarycloud/mesh/mesh_pb2.py +74 -73
  24. luminarycloud/_proto/api/v0/luminarycloud/mesh/mesh_pb2.pyi +17 -3
  25. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +96 -25
  26. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +235 -1
  27. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.py +34 -0
  28. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.pyi +12 -0
  29. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.py +16 -16
  30. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.pyi +7 -3
  31. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.py +97 -61
  32. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.pyi +77 -4
  33. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.py +34 -0
  34. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.pyi +12 -0
  35. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +33 -31
  36. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +23 -2
  37. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +126 -27
  38. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +183 -0
  39. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.py +99 -0
  40. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.pyi +30 -0
  41. luminarycloud/_proto/assistant/assistant_pb2.py +74 -41
  42. luminarycloud/_proto/assistant/assistant_pb2.pyi +64 -2
  43. luminarycloud/_proto/assistant/assistant_pb2_grpc.py +33 -0
  44. luminarycloud/_proto/assistant/assistant_pb2_grpc.pyi +10 -0
  45. luminarycloud/_proto/base/base_pb2.py +20 -7
  46. luminarycloud/_proto/base/base_pb2.pyi +38 -0
  47. luminarycloud/_proto/cad/shape_pb2.py +39 -19
  48. luminarycloud/_proto/cad/shape_pb2.pyi +86 -34
  49. luminarycloud/_proto/cad/transformation_pb2.py +60 -16
  50. luminarycloud/_proto/cad/transformation_pb2.pyi +138 -32
  51. luminarycloud/_proto/client/simulation_pb2.py +501 -348
  52. luminarycloud/_proto/client/simulation_pb2.pyi +607 -11
  53. luminarycloud/_proto/geometry/geometry_pb2.py +77 -63
  54. luminarycloud/_proto/geometry/geometry_pb2.pyi +42 -3
  55. luminarycloud/_proto/hexmesh/hexmesh_pb2.py +24 -18
  56. luminarycloud/_proto/hexmesh/hexmesh_pb2.pyi +23 -2
  57. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +10 -10
  58. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +5 -5
  59. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2.py +29 -0
  60. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2.pyi +7 -0
  61. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.py +70 -0
  62. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.pyi +30 -0
  63. luminarycloud/_proto/quantity/quantity_options_pb2.py +6 -6
  64. luminarycloud/_proto/quantity/quantity_options_pb2.pyi +10 -1
  65. luminarycloud/_proto/quantity/quantity_pb2.py +176 -167
  66. luminarycloud/_proto/quantity/quantity_pb2.pyi +11 -5
  67. luminarycloud/enum/__init__.py +1 -0
  68. luminarycloud/enum/gpu_type.py +2 -0
  69. luminarycloud/enum/quantity_type.py +9 -0
  70. luminarycloud/enum/vis_enums.py +23 -3
  71. luminarycloud/exceptions.py +7 -1
  72. luminarycloud/feature_modification.py +45 -35
  73. luminarycloud/geometry.py +107 -9
  74. luminarycloud/geometry_version.py +57 -3
  75. luminarycloud/mesh.py +1 -2
  76. luminarycloud/meshing/mesh_generation_params.py +8 -8
  77. luminarycloud/params/enum/_enum_wrappers.py +562 -30
  78. luminarycloud/params/simulation/adaptive_mesh_refinement_.py +4 -0
  79. luminarycloud/params/simulation/material/material_solid_.py +15 -1
  80. luminarycloud/params/simulation/physics/__init__.py +0 -1
  81. luminarycloud/params/simulation/physics/periodic_pair_.py +12 -31
  82. luminarycloud/physics_ai/architectures.py +58 -0
  83. luminarycloud/physics_ai/inference.py +13 -13
  84. luminarycloud/physics_ai/solution.py +3 -1
  85. luminarycloud/physics_ai/training_jobs.py +37 -0
  86. luminarycloud/pipelines/__init__.py +11 -3
  87. luminarycloud/pipelines/api.py +248 -16
  88. luminarycloud/pipelines/arguments.py +15 -0
  89. luminarycloud/pipelines/core.py +113 -96
  90. luminarycloud/pipelines/{operators.py → stages.py} +96 -39
  91. luminarycloud/project.py +15 -47
  92. luminarycloud/simulation.py +69 -5
  93. luminarycloud/simulation_param.py +0 -9
  94. luminarycloud/simulation_template.py +2 -1
  95. luminarycloud/types/matrix3.py +12 -0
  96. luminarycloud/vis/__init__.py +17 -0
  97. luminarycloud/vis/data_extraction.py +20 -4
  98. luminarycloud/vis/interactive_report.py +110 -0
  99. luminarycloud/vis/interactive_scene.py +29 -2
  100. luminarycloud/vis/report.py +252 -0
  101. luminarycloud/vis/visualization.py +127 -5
  102. luminarycloud/volume_selection.py +132 -69
  103. {luminarycloud-0.19.0.dist-info → luminarycloud-0.22.0.dist-info}/METADATA +1 -1
  104. {luminarycloud-0.19.0.dist-info → luminarycloud-0.22.0.dist-info}/RECORD +105 -97
  105. luminarycloud/params/simulation/physics/periodic_pair/__init__.py +0 -2
  106. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/__init__.py +0 -2
  107. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/rotational_periodicity_.py +0 -31
  108. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/translational_periodicity_.py +0 -29
  109. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type_.py +0 -25
  110. {luminarycloud-0.19.0.dist-info → luminarycloud-0.22.0.dist-info}/WHEEL +0 -0
@@ -1,7 +1,7 @@
1
1
  """
2
2
  @generated by mypy-protobuf. Do not edit manually!
3
3
  isort:skip_file
4
- Copyright 2020-2025 Luminary Cloud, Inc. All Rights Reserved.
4
+ Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
5
5
  Generated by quantities.py. DO NOT EDIT
6
6
  """
7
7
  import builtins
@@ -34,15 +34,18 @@ class _QuantityTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._E
34
34
  VOLUME: _QuantityType.ValueType # 30891
35
35
  TIME: _QuantityType.ValueType # 20394
36
36
  ENERGY: _QuantityType.ValueType # 30269
37
- SPECIFIC_ENTHALPY: _QuantityType.ValueType # 15033
37
+ ENTHALPY: _QuantityType.ValueType # 15033
38
+ TOTAL_ENTHALPY: _QuantityType.ValueType # 55281
39
+ ISENTROPIC_ENTHALPY: _QuantityType.ValueType # 53299
40
+ ISENTROPIC_TOTAL_ENTHALPY: _QuantityType.ValueType # 39573
38
41
  RADIANS: _QuantityType.ValueType # 61564
39
42
  DEGREE: _QuantityType.ValueType # 42822
40
43
  POWER: _QuantityType.ValueType # 64408
41
44
  POWER_PER_UNIT_VOLUME: _QuantityType.ValueType # 51
42
45
  THERMAL_EXPANSION_COEFFICIENT: _QuantityType.ValueType # 16951
43
46
  TEMPERATURE: _QuantityType.ValueType # 3
44
- DENSITY: _QuantityType.ValueType # 1
45
47
  PRESSURE: _QuantityType.ValueType # 2
48
+ DENSITY: _QuantityType.ValueType # 1
46
49
  VELOCITY: _QuantityType.ValueType # 5
47
50
  VELOCITY_MAGNITUDE: _QuantityType.ValueType # 30
48
51
  EDDY_VISCOSITY: _QuantityType.ValueType # 4
@@ -199,15 +202,18 @@ AREA: QuantityType.ValueType # 10
199
202
  VOLUME: QuantityType.ValueType # 30891
200
203
  TIME: QuantityType.ValueType # 20394
201
204
  ENERGY: QuantityType.ValueType # 30269
202
- SPECIFIC_ENTHALPY: QuantityType.ValueType # 15033
205
+ ENTHALPY: QuantityType.ValueType # 15033
206
+ TOTAL_ENTHALPY: QuantityType.ValueType # 55281
207
+ ISENTROPIC_ENTHALPY: QuantityType.ValueType # 53299
208
+ ISENTROPIC_TOTAL_ENTHALPY: QuantityType.ValueType # 39573
203
209
  RADIANS: QuantityType.ValueType # 61564
204
210
  DEGREE: QuantityType.ValueType # 42822
205
211
  POWER: QuantityType.ValueType # 64408
206
212
  POWER_PER_UNIT_VOLUME: QuantityType.ValueType # 51
207
213
  THERMAL_EXPANSION_COEFFICIENT: QuantityType.ValueType # 16951
208
214
  TEMPERATURE: QuantityType.ValueType # 3
209
- DENSITY: QuantityType.ValueType # 1
210
215
  PRESSURE: QuantityType.ValueType # 2
216
+ DENSITY: QuantityType.ValueType # 1
211
217
  VELOCITY: QuantityType.ValueType # 5
212
218
  VELOCITY_MAGNITUDE: QuantityType.ValueType # 30
213
219
  EDDY_VISCOSITY: QuantityType.ValueType # 4
@@ -23,6 +23,7 @@ from .vis_enums import (
23
23
  CameraProjection,
24
24
  ColorMapPreset,
25
25
  EntityType,
26
+ FieldAssociation,
26
27
  FieldComponent,
27
28
  RenderStatusType,
28
29
  ExtractStatusType,
@@ -10,3 +10,5 @@ class GPUType(IntEnum):
10
10
  UNSPECIFIED = simulationpb.SimulationOptions.GPU_TYPE_UNSPECIFIED
11
11
  V100 = simulationpb.SimulationOptions.GPU_TYPE_V100
12
12
  A100 = simulationpb.SimulationOptions.GPU_TYPE_A100
13
+ T4 = simulationpb.SimulationOptions.GPU_TYPE_T4
14
+ H100 = simulationpb.SimulationOptions.GPU_TYPE_H100
@@ -60,6 +60,10 @@ class QuantityType(IntEnum):
60
60
  TOTAL_MOMENT_COEFFICIENT
61
61
  TOTAL_PRESSURE
62
62
  TOTAL_TEMPERATURE
63
+ ENTHALPY
64
+ TOTAL_ENTHALPY
65
+ ISENTROPIC_ENTHALPY
66
+ ISENTROPIC_TOTAL_ENTHALPY
63
67
  VELOCITY
64
68
  VELOCITY_MAGNITUDE
65
69
  VELOCITY_TIME_AVERAGE
@@ -116,6 +120,10 @@ class QuantityType(IntEnum):
116
120
  TEMPERATURE = quantitypb.TEMPERATURE
117
121
  TOTAL_PRESSURE = quantitypb.TOTAL_PRESSURE
118
122
  TOTAL_TEMPERATURE = quantitypb.TOTAL_TEMPERATURE
123
+ ENTHALPY = quantitypb.ENTHALPY
124
+ TOTAL_ENTHALPY = quantitypb.TOTAL_ENTHALPY
125
+ ISENTROPIC_ENTHALPY = quantitypb.ISENTROPIC_ENTHALPY
126
+ ISENTROPIC_TOTAL_ENTHALPY = quantitypb.ISENTROPIC_TOTAL_ENTHALPY
119
127
  VELOCITY = quantitypb.VELOCITY
120
128
  VELOCITY_MAGNITUDE = quantitypb.VELOCITY_MAGNITUDE
121
129
  Y_PLUS = quantitypb.Y_PLUS
@@ -173,6 +181,7 @@ class QuantityType(IntEnum):
173
181
  SENSITIVITY = quantitypb.SENSITIVITY
174
182
  NORMAL_SENSITIVITY = quantitypb.NORMAL_SENSITIVITY
175
183
  SMOOTHED_NORMAL_SENSITIVITY = quantitypb.SMOOTHED_NORMAL_SENSITIVITY
184
+ ENTROPY = quantitypb.ENTROPY
176
185
 
177
186
  # Quantities needed for table upload
178
187
  LENGTH = quantitypb.LENGTH
@@ -15,11 +15,8 @@ class VisQuantity(IntEnum):
15
15
  """
16
16
 
17
17
  NONE = quantitypb.INVALID_QUANTITY_TYPE
18
- ABSOLUTE_PRESSURE = quantitypb.ABSOLUTE_PRESSURE
19
18
  PRESSURE = quantitypb.PRESSURE
20
- DENSITY = quantitypb.DENSITY
21
19
  TEMPERATURE = quantitypb.TEMPERATURE
22
- MACH = quantitypb.MACH
23
20
  Q_CRITERION = quantitypb.Q_CRITERION
24
21
  VELOCITY = quantitypb.VELOCITY
25
22
  WALL_SHEAR_STRESS = quantitypb.WALL_SHEAR_STRESS
@@ -39,6 +36,10 @@ class VisQuantity(IntEnum):
39
36
  HEAT_TRANSFER_COEFFICIENT = quantitypb.HEAT_TRANSFER_COEFFICIENT
40
37
  GRID_VELOCITY = quantitypb.GRID_VELOCITY
41
38
  # Derived fields
39
+ ABSOLUTE_PRESSURE = quantitypb.ABSOLUTE_PRESSURE
40
+ MACH = quantitypb.MACH
41
+ DENSITY = quantitypb.DENSITY
42
+ ENTROPY = quantitypb.ENTROPY
42
43
  RELATIVE_VELOCITY = quantitypb.RELATIVE_VELOCITY
43
44
  RELATIVE_MACH = quantitypb.RELATIVE_MACH
44
45
  PRESSURE_COEFFICIENT = quantitypb.PRESSURE_COEFFICIENT
@@ -71,6 +72,7 @@ class VisQuantity(IntEnum):
71
72
  MASS_FLUX_TIME_AVERAGE = quantitypb.MASS_FLUX_TIME_AVERAGE
72
73
  Q_CRITERION_TIME_AVERAGE = quantitypb.Q_CRITERION_TIME_AVERAGE
73
74
  HEAT_FLUX_TIME_AVERAGE = quantitypb.HEAT_FLUX_TIME_AVERAGE
75
+ DEBUG_QUANTITY = quantitypb.DEBUG_QUANTITY
74
76
 
75
77
 
76
78
  # Return the text name for the VisQuantity including the units, as it appears in the UI
@@ -306,3 +308,21 @@ class SceneMode(str, Enum):
306
308
  INLINE = "inline"
307
309
  SIDE_PANEL = "side_panel"
308
310
  FULLSCREEN = "fullscreen"
311
+
312
+
313
+ class FieldAssociation(IntEnum):
314
+ """
315
+ An enum to specifiy the association of a field used in the range query.
316
+
317
+ .. warning:: This feature is experimental and may change or be removed in the future.
318
+
319
+ Attributes
320
+ ----------
321
+ POINTS
322
+ Field values are associated with the points of the mesh.
323
+ CELLS
324
+ Field values are associated with the cells of the mesh.
325
+ """
326
+
327
+ POINTS = 0
328
+ CELLS = 1
@@ -22,6 +22,12 @@ class SDKException(Exception):
22
22
  return [self.message]
23
23
 
24
24
 
25
+ class Timeout(SDKException):
26
+ """Raised when some long-running operation doesn't finish before its deadline."""
27
+
28
+ pass
29
+
30
+
25
31
  class RpcError(SDKException):
26
32
  """Raised when an RPC error occurs."""
27
33
 
@@ -70,6 +76,6 @@ class FailedPreconditionError(RpcError):
70
76
 
71
77
 
72
78
  class DeadlineExceededError(RpcError):
73
- """Raised when the deadline expired before the operation could complete. I.e. it timed out."""
79
+ """Raised when the gRPC deadline expired before the operation could complete. I.e. it timed out."""
74
80
 
75
81
  pass
@@ -1,12 +1,12 @@
1
1
  # Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
2
2
  from enum import Enum, auto
3
- from typing import Dict, Iterable, List, Optional, cast
3
+ from typing import Dict, Iterable, List, Optional
4
4
  from copy import deepcopy
5
5
 
6
- from luminarycloud.types.adfloat import _to_ad_proto, _from_ad_proto
6
+ from luminarycloud.types.adfloat import _to_ad_proto
7
7
  from ._proto.geometry import geometry_pb2 as gpb
8
8
  from .types import Vector3Like
9
- from .types.vector3 import _to_vector3
9
+ from .types.vector3 import _to_vector3_ad_proto
10
10
  from .params.geometry import Shape, Sphere, Cube, Cylinder, Torus, Cone, HalfSphere, Volume
11
11
  from google.protobuf.internal.containers import RepeatedScalarFieldContainer
12
12
 
@@ -265,6 +265,7 @@ def modify_delete(
265
265
  def modify_union(
266
266
  feature: gpb.Feature,
267
267
  volumes: Optional[List[Volume | int]] = None,
268
+ keep: Optional[bool] = None,
268
269
  ) -> gpb.Modification:
269
270
  """
270
271
  Modify a boolean union feature with optional new body IDs.
@@ -273,6 +274,7 @@ def modify_union(
273
274
  Args:
274
275
  feature: A gpb.Feature object
275
276
  volumes: List of volumes or volume IDs to union
277
+ keep: Whether to keep the original bodies
276
278
 
277
279
  Returns:
278
280
  A gpb.Modification object
@@ -287,6 +289,9 @@ def modify_union(
287
289
  vol_ids = _volumes_to_int_list(volumes)
288
290
  _update_repeated_field(boolean_op.reg_union.bodies, vol_ids)
289
291
 
292
+ if keep is not None:
293
+ boolean_op.reg_union.keep_source_bodies = keep
294
+
290
295
  return gpb.Modification(
291
296
  mod_type=gpb.Modification.ModificationType.MODIFICATION_TYPE_UPDATE_FEATURE,
292
297
  feature=feature_copy,
@@ -297,6 +302,8 @@ def modify_subtraction(
297
302
  feature: gpb.Feature,
298
303
  volumes: Optional[List[Volume | int]] = None,
299
304
  tool_volumes: Optional[List[Volume | int]] = None,
305
+ keep_source_bodies: Optional[bool] = None,
306
+ keep_tool_bodies: Optional[bool] = None,
300
307
  propagate_tool_tags: Optional[bool] = None,
301
308
  ) -> gpb.Modification:
302
309
  """
@@ -307,6 +314,8 @@ def modify_subtraction(
307
314
  feature: A gpb.Feature object
308
315
  volumes: List of volumes or volume IDs to subtract from
309
316
  tool_volumes: List of volumes or volume IDs to use for subtraction
317
+ keep_source_bodies: Whether to keep the original bodies
318
+ keep_tool_bodies: Whether to keep the tool bodies
310
319
  propagate_tool_tags: Whether to propagate tool tags
311
320
 
312
321
  Returns:
@@ -325,6 +334,12 @@ def modify_subtraction(
325
334
  tool_ids = _volumes_to_int_list(tool_volumes)
326
335
  _update_repeated_field(boolean_op.reg_subtraction.tools, tool_ids)
327
336
 
337
+ if keep_source_bodies is not None:
338
+ boolean_op.reg_subtraction.keep_source_bodies = keep_source_bodies
339
+
340
+ if keep_tool_bodies is not None:
341
+ boolean_op.reg_subtraction.keep_tool_bodies = keep_tool_bodies
342
+
328
343
  if propagate_tool_tags is not None:
329
344
  boolean_op.reg_subtraction.propagate_tool_tags = propagate_tool_tags
330
345
 
@@ -335,7 +350,9 @@ def modify_subtraction(
335
350
 
336
351
 
337
352
  def modify_intersection(
338
- feature: gpb.Feature, volumes: Optional[List[Volume | int]] = None
353
+ feature: gpb.Feature,
354
+ volumes: Optional[List[Volume | int]] = None,
355
+ keep: Optional[bool] = None,
339
356
  ) -> gpb.Modification:
340
357
  """
341
358
  Modify a boolean intersection feature with optional new volumes.
@@ -344,6 +361,7 @@ def modify_intersection(
344
361
  Args:
345
362
  feature: A gpb.Feature object
346
363
  volumes: List of volumes or volume IDs to intersect
364
+ keep: Whether to keep the original bodies
347
365
 
348
366
  Returns:
349
367
  A gpb.Modification object
@@ -358,6 +376,9 @@ def modify_intersection(
358
376
  vol_ids = _volumes_to_int_list(volumes)
359
377
  _update_repeated_field(boolean_op.reg_intersection.bodies, vol_ids)
360
378
 
379
+ if keep is not None:
380
+ boolean_op.reg_intersection.keep_source_bodies = keep
381
+
361
382
  return gpb.Modification(
362
383
  mod_type=gpb.Modification.ModificationType.MODIFICATION_TYPE_UPDATE_FEATURE,
363
384
  feature=feature_copy,
@@ -368,6 +389,8 @@ def modify_chop(
368
389
  feature: gpb.Feature,
369
390
  volumes: Optional[List[Volume | int]] = None,
370
391
  tool_volumes: Optional[List[Volume | int]] = None,
392
+ keep_source_bodies: Optional[bool] = None,
393
+ keep_tool_bodies: Optional[bool] = None,
371
394
  propagate_tool_tags: Optional[bool] = None,
372
395
  ) -> gpb.Modification:
373
396
  """
@@ -378,6 +401,8 @@ def modify_chop(
378
401
  feature: A gpb.Feature object
379
402
  volumes: List of volumes or volume IDs to chop
380
403
  tool_volumes: List of volumes or volume IDs to use for chopping
404
+ keep_source_bodies: Whether to keep the original bodies
405
+ keep_tool_bodies: Whether to keep the tool bodies
381
406
  propagate_tool_tags: Whether to propagate tool tags
382
407
 
383
408
  Returns:
@@ -397,6 +422,12 @@ def modify_chop(
397
422
  tool_ids = _volumes_to_int_list(tool_volumes)
398
423
  _update_repeated_field(boolean_op.reg_chop.tools, tool_ids)
399
424
 
425
+ if keep_source_bodies is not None:
426
+ boolean_op.reg_chop.keep_source_bodies = keep_source_bodies
427
+
428
+ if keep_tool_bodies is not None:
429
+ boolean_op.reg_chop.keep_tool_bodies = keep_tool_bodies
430
+
400
431
  if propagate_tool_tags is not None:
401
432
  boolean_op.reg_chop.propagate_tool_tags = propagate_tool_tags
402
433
 
@@ -483,10 +514,7 @@ def modify_translate(
483
514
  _update_repeated_field(transform_op.body, vol_ids)
484
515
 
485
516
  if displacement is not None:
486
- vec = _to_vector3(displacement)
487
- transform_op.translation.vector.x = vec.x
488
- transform_op.translation.vector.y = vec.y
489
- transform_op.translation.vector.z = vec.z
517
+ transform_op.translation.vector.CopyFrom(_to_vector3_ad_proto(displacement))
490
518
 
491
519
  if keep is not None:
492
520
  transform_op.keep = keep
@@ -532,19 +560,13 @@ def modify_rotate(
532
560
 
533
561
  # Update existing rotation
534
562
  if angle is not None:
535
- transform_op.rotation.angle = angle
563
+ transform_op.rotation.angle.CopyFrom(_to_ad_proto(angle))
536
564
 
537
565
  if axis is not None:
538
- axis_vec = _to_vector3(axis)
539
- transform_op.rotation.arbitrary.direction.x = axis_vec.x
540
- transform_op.rotation.arbitrary.direction.y = axis_vec.y
541
- transform_op.rotation.arbitrary.direction.z = axis_vec.z
566
+ transform_op.rotation.arbitrary.direction.CopyFrom(_to_vector3_ad_proto(axis))
542
567
 
543
568
  if origin is not None:
544
- origin_vec = _to_vector3(origin)
545
- transform_op.rotation.arbitrary.origin.x = origin_vec.x
546
- transform_op.rotation.arbitrary.origin.y = origin_vec.y
547
- transform_op.rotation.arbitrary.origin.z = origin_vec.z
569
+ transform_op.rotation.arbitrary.origin.CopyFrom(_to_vector3_ad_proto(origin))
548
570
 
549
571
  if keep is not None:
550
572
  transform_op.keep = keep
@@ -587,13 +609,10 @@ def modify_scale(
587
609
  _update_repeated_field(transform_op.body, vol_ids)
588
610
 
589
611
  if scale_factor is not None:
590
- transform_op.scaling.isotropic = scale_factor
612
+ transform_op.scaling.isotropic.CopyFrom(_to_ad_proto(scale_factor))
591
613
 
592
614
  if origin is not None:
593
- origin_vec = _to_vector3(origin)
594
- transform_op.scaling.arbitrary.x = origin_vec.x
595
- transform_op.scaling.arbitrary.y = origin_vec.y
596
- transform_op.scaling.arbitrary.z = origin_vec.z
615
+ transform_op.scaling.arbitrary.CopyFrom(_to_vector3_ad_proto(origin))
597
616
 
598
617
  if keep is not None:
599
618
  transform_op.keep = keep
@@ -769,10 +788,7 @@ def modify_linear_pattern(
769
788
 
770
789
  if direction is not None:
771
790
  # Update existing linear pattern direction
772
- dir_vec = _to_vector3(direction)
773
- pattern_op.direction.linear_spacing.vector.x = dir_vec.x
774
- pattern_op.direction.linear_spacing.vector.y = dir_vec.y
775
- pattern_op.direction.linear_spacing.vector.z = dir_vec.z
791
+ pattern_op.direction.linear_spacing.vector.CopyFrom(_to_vector3_ad_proto(direction))
776
792
 
777
793
  if quantity is not None:
778
794
  pattern_op.direction.quantity = quantity
@@ -832,19 +848,13 @@ def modify_circular_pattern(
832
848
  circular = pattern_op.direction.circular_distribution
833
849
 
834
850
  if angle is not None:
835
- circular.rotation.angle = angle
851
+ circular.rotation.angle.CopyFrom(_to_ad_proto(angle))
836
852
 
837
853
  if axis is not None:
838
- axis_vec = _to_vector3(axis)
839
- circular.rotation.arbitrary.direction.x = axis_vec.x
840
- circular.rotation.arbitrary.direction.y = axis_vec.y
841
- circular.rotation.arbitrary.direction.z = axis_vec.z
854
+ circular.rotation.arbitrary.direction.CopyFrom(_to_vector3_ad_proto(axis))
842
855
 
843
856
  if origin is not None:
844
- origin_vec = _to_vector3(origin)
845
- circular.rotation.arbitrary.origin.x = origin_vec.x
846
- circular.rotation.arbitrary.origin.y = origin_vec.y
847
- circular.rotation.arbitrary.origin.z = origin_vec.z
857
+ circular.rotation.arbitrary.origin.CopyFrom(_to_vector3_ad_proto(origin))
848
858
 
849
859
  if full_rotation is not None:
850
860
  circular.full = full_rotation
luminarycloud/geometry.py CHANGED
@@ -29,7 +29,7 @@ from .named_variable_set import NamedVariableSet, get_named_variable_set
29
29
 
30
30
  if TYPE_CHECKING:
31
31
  from .project import Project
32
- from .geometry_version import GeometryVersion
32
+ from .geometry_version import GeometryVersion, GeometryVersionIterator
33
33
 
34
34
 
35
35
  @ProtoWrapper(geometrypb.Geometry)
@@ -42,6 +42,8 @@ class Geometry(ProtoWrapperBase):
42
42
  "Geometry name."
43
43
  status: GeometryStatus
44
44
  "The status of the geometry."
45
+ project_id: ProjectID
46
+ "The ID of the project this geometry belongs to."
45
47
 
46
48
  _proto: geometrypb.Geometry
47
49
 
@@ -63,7 +65,7 @@ class Geometry(ProtoWrapperBase):
63
65
  """
64
66
  Get the project this geometry belongs to.
65
67
  """
66
- return lc.get_project(ProjectID(self._proto.project_id))
68
+ return lc.get_project(self.project_id)
67
69
 
68
70
  def update(self, *, name: str | None = None) -> None:
69
71
  """
@@ -82,6 +84,18 @@ class Geometry(ProtoWrapperBase):
82
84
  res = get_default_client().UpdateGeometry(req)
83
85
  self._proto = res.geometry
84
86
 
87
+ def delete(self) -> None:
88
+ """
89
+ Delete the geometry.
90
+
91
+ This operation cannot be reverted and all the geometry data will be deleted as part of
92
+ this request.
93
+ """
94
+ req = geometrypb.DeleteGeometryRequest(
95
+ geometry_id=self.id,
96
+ )
97
+ get_default_client().DeleteGeometry(req)
98
+
85
99
  def copy(self, name: str = "") -> "Geometry":
86
100
  """
87
101
  Copy the geometry.
@@ -290,6 +304,46 @@ class Geometry(ProtoWrapperBase):
290
304
  )
291
305
  )
292
306
 
307
+ def versions(self, unfiltered: bool = False, page_size: int = 50) -> "GeometryVersionIterator":
308
+ """
309
+ List the geometry versions for this geometry in chronological order, oldest first.
310
+
311
+ By default, this only returns versions that are named OR have an associated Mesh OR are the
312
+ latest version of the geometry. If `unfiltered` is true, this returns all versions.
313
+
314
+ The geometry versions are fetched lazily in batches using pagination to optimize memory
315
+ usage and API calls.
316
+
317
+ Parameters
318
+ ----------
319
+ unfiltered : bool, optional
320
+ If True, returns all versions. If False, returns only versions that are named OR have an
321
+ associated Mesh OR are the latest version of the geometry. Defaults to False.
322
+ page_size : int, optional
323
+ Number of geometry versions to fetch per page. Defaults to 50, max is 500.
324
+
325
+ Returns
326
+ -------
327
+ GeometryVersionIterator
328
+ An iterator that yields GeometryVersion objects one at a time.
329
+
330
+ Examples
331
+ --------
332
+ Fetch the versions of this geometry with default filtering applied.
333
+ >>> for version in geometry.versions():
334
+ ... print(version.id, version.name)
335
+
336
+ Fetch all versions of the geometry.
337
+ >>> for version in geometry.versions(unfiltered=True):
338
+ ... print(version.id, version.name)
339
+
340
+ Build a list of versions of this geometry that have the name "So Important".
341
+ >>> important_versions = [ver for ver in geometry.versions() if ver.name == "So Important"]
342
+ """
343
+ from .geometry_version import GeometryVersionIterator
344
+
345
+ return GeometryVersionIterator(self.id, unfiltered, page_size)
346
+
293
347
  def latest_version(self) -> GeometryVersion:
294
348
  """
295
349
  Get the latest version of the geometry.
@@ -344,7 +398,7 @@ class Geometry(ProtoWrapperBase):
344
398
  jitter = random.uniform(0.5, 1.5)
345
399
  time.sleep(2 + jitter)
346
400
 
347
- def create_tag(self, name: str, entities: Sequence[Volume | Surface]) -> None:
401
+ def create_tag(self, name: str, entities: Sequence[Volume | Surface]) -> Tag:
348
402
  """
349
403
  Create a tag in the geometry.
350
404
 
@@ -354,6 +408,11 @@ class Geometry(ProtoWrapperBase):
354
408
  The name of the tag to create.
355
409
  entities : list of Volumes or Surfaces
356
410
  The Volumes and Surfaces to tag.
411
+
412
+ Returns
413
+ -------
414
+ Tag
415
+ The tag that was created.
357
416
  """
358
417
  volume_ids = []
359
418
  surface_ids = []
@@ -365,8 +424,7 @@ class Geometry(ProtoWrapperBase):
365
424
  else:
366
425
  raise TypeError("entities must be of type Volume or Surface")
367
426
 
368
- req = geometrypb.ModifyGeometryRequest(
369
- geometry_id=self.id,
427
+ self._modify(
370
428
  modification=gpb.Modification(
371
429
  mod_type=gpb.Modification.MODIFICATION_TYPE_CREATE_TAG,
372
430
  create_or_update_tag=gpb.CreateOrUpdateTag(
@@ -376,9 +434,9 @@ class Geometry(ProtoWrapperBase):
376
434
  ),
377
435
  ),
378
436
  )
379
- get_default_client().ModifyGeometry(req)
437
+ return self._get_tag_by_name(name)
380
438
 
381
- def rename_tag(self, old_name: str, new_name: str) -> None:
439
+ def rename_tag(self, old_name: str, new_name: str) -> Tag:
382
440
  """
383
441
  Rename a tag in the geometry.
384
442
 
@@ -388,6 +446,11 @@ class Geometry(ProtoWrapperBase):
388
446
  The name of the tag to rename.
389
447
  new_name : str
390
448
  The new name for the tag.
449
+
450
+ Returns
451
+ -------
452
+ Tag
453
+ The updated tag.
391
454
  """
392
455
  self._modify(
393
456
  modification=gpb.Modification(
@@ -398,8 +461,9 @@ class Geometry(ProtoWrapperBase):
398
461
  ),
399
462
  ),
400
463
  )
464
+ return self._get_tag_by_name(new_name)
401
465
 
402
- def untag_entities(self, name: str, entities: Sequence[Volume | Surface]) -> None:
466
+ def untag_entities(self, name: str, entities: Sequence[Volume | Surface]) -> Tag | None:
403
467
  """
404
468
  Untag entities from a tag in the geometry.
405
469
 
@@ -410,6 +474,11 @@ class Geometry(ProtoWrapperBase):
410
474
  entities : list of Volumes or Surfaces
411
475
  The Volumes and Surfaces to untag. If empty, all entities with the
412
476
  tag will be untagged.
477
+
478
+ Returns
479
+ -------
480
+ Tag
481
+ The updated tag.
413
482
  """
414
483
  volume_ids = []
415
484
  surface_ids = []
@@ -431,8 +500,12 @@ class Geometry(ProtoWrapperBase):
431
500
  ),
432
501
  ),
433
502
  )
503
+ try:
504
+ return self._get_tag_by_name(name)
505
+ except ValueError:
506
+ return None
434
507
 
435
- def update_tag(self, name: str, entities: Sequence[Volume | Surface]) -> None:
508
+ def update_tag(self, name: str, entities: Sequence[Volume | Surface]) -> Tag:
436
509
  """
437
510
  Adds entities to a tag in the geometry.
438
511
 
@@ -441,6 +514,11 @@ class Geometry(ProtoWrapperBase):
441
514
  name : str
442
515
  The name of the tag to update.
443
516
  entities : list of Volumes or Surfaces
517
+
518
+ Returns
519
+ -------
520
+ Tag
521
+ The updated tag.
444
522
  """
445
523
  volume_ids = []
446
524
  surface_ids = []
@@ -462,6 +540,7 @@ class Geometry(ProtoWrapperBase):
462
540
  ),
463
541
  ),
464
542
  )
543
+ return self._get_tag_by_name(name)
465
544
 
466
545
  def list_tags(self) -> list[Tag]:
467
546
  """
@@ -477,6 +556,25 @@ class Geometry(ProtoWrapperBase):
477
556
  res: geometrypb.ListTagsResponse = get_default_client().ListTags(req)
478
557
  return [Tag(t) for t in res.tags]
479
558
 
559
+ def _get_tag_by_name(self, name: str) -> Tag:
560
+ """
561
+ Get a specific tag from the geometry.
562
+
563
+ Parameters
564
+ ----------
565
+ name : str
566
+ The name of the tag.
567
+
568
+ Returns
569
+ -------
570
+ Tag
571
+ The tag with the specified name.
572
+ """
573
+ try:
574
+ return next(filter(lambda t: t.name == name, self.list_tags()))
575
+ except StopIteration:
576
+ raise ValueError(f"Tag '{name}' not found in geometry {self.id}")
577
+
480
578
  def list_entities(self) -> tuple[list[Surface], list[Volume]]:
481
579
  """
482
580
  List all the entities in the geometry.