luminarycloud 0.20.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 (95) hide show
  1. luminarycloud/__init__.py +5 -1
  2. luminarycloud/_client/client.py +5 -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/download.py +3 -1
  8. luminarycloud/_helpers/pagination.py +62 -0
  9. luminarycloud/_helpers/proto_decorator.py +13 -5
  10. luminarycloud/_helpers/upload.py +18 -12
  11. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2.py +55 -0
  12. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2.pyi +52 -0
  13. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2_grpc.py +72 -0
  14. luminarycloud/_proto/api/v0/luminarycloud/feature_flag/feature_flag_pb2_grpc.pyi +35 -0
  15. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +168 -124
  16. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +133 -4
  17. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +66 -0
  18. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +20 -0
  19. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +8 -8
  20. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +5 -5
  21. luminarycloud/_proto/api/v0/luminarycloud/mesh/mesh_pb2.py +74 -73
  22. luminarycloud/_proto/api/v0/luminarycloud/mesh/mesh_pb2.pyi +17 -3
  23. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +33 -20
  24. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +21 -1
  25. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.py +16 -16
  26. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.pyi +7 -3
  27. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.py +97 -61
  28. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.pyi +72 -3
  29. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.py +34 -0
  30. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.pyi +12 -0
  31. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +33 -31
  32. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +23 -2
  33. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +68 -19
  34. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +98 -0
  35. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.py +33 -0
  36. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.pyi +10 -0
  37. luminarycloud/_proto/assistant/assistant_pb2.py +74 -41
  38. luminarycloud/_proto/assistant/assistant_pb2.pyi +64 -2
  39. luminarycloud/_proto/assistant/assistant_pb2_grpc.py +33 -0
  40. luminarycloud/_proto/assistant/assistant_pb2_grpc.pyi +10 -0
  41. luminarycloud/_proto/base/base_pb2.py +20 -7
  42. luminarycloud/_proto/base/base_pb2.pyi +38 -0
  43. luminarycloud/_proto/cad/shape_pb2.py +39 -19
  44. luminarycloud/_proto/cad/shape_pb2.pyi +86 -34
  45. luminarycloud/_proto/cad/transformation_pb2.py +60 -16
  46. luminarycloud/_proto/cad/transformation_pb2.pyi +138 -32
  47. luminarycloud/_proto/client/simulation_pb2.py +490 -348
  48. luminarycloud/_proto/client/simulation_pb2.pyi +570 -8
  49. luminarycloud/_proto/geometry/geometry_pb2.py +77 -63
  50. luminarycloud/_proto/geometry/geometry_pb2.pyi +42 -3
  51. luminarycloud/_proto/hexmesh/hexmesh_pb2.py +24 -18
  52. luminarycloud/_proto/hexmesh/hexmesh_pb2.pyi +23 -2
  53. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +10 -10
  54. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +5 -5
  55. luminarycloud/_proto/quantity/quantity_options_pb2.py +6 -6
  56. luminarycloud/_proto/quantity/quantity_options_pb2.pyi +10 -1
  57. luminarycloud/_proto/quantity/quantity_pb2.py +176 -167
  58. luminarycloud/_proto/quantity/quantity_pb2.pyi +11 -5
  59. luminarycloud/enum/__init__.py +1 -0
  60. luminarycloud/enum/gpu_type.py +2 -0
  61. luminarycloud/enum/quantity_type.py +9 -0
  62. luminarycloud/enum/vis_enums.py +23 -3
  63. luminarycloud/feature_modification.py +45 -35
  64. luminarycloud/geometry.py +104 -8
  65. luminarycloud/geometry_version.py +57 -3
  66. luminarycloud/meshing/mesh_generation_params.py +8 -8
  67. luminarycloud/params/enum/_enum_wrappers.py +537 -30
  68. luminarycloud/params/simulation/adaptive_mesh_refinement_.py +4 -0
  69. luminarycloud/params/simulation/physics/__init__.py +0 -1
  70. luminarycloud/params/simulation/physics/periodic_pair_.py +12 -31
  71. luminarycloud/physics_ai/architectures.py +5 -5
  72. luminarycloud/physics_ai/inference.py +13 -13
  73. luminarycloud/physics_ai/solution.py +3 -1
  74. luminarycloud/pipelines/__init__.py +11 -3
  75. luminarycloud/pipelines/api.py +240 -4
  76. luminarycloud/pipelines/arguments.py +15 -0
  77. luminarycloud/pipelines/core.py +113 -96
  78. luminarycloud/pipelines/{operators.py → stages.py} +96 -39
  79. luminarycloud/project.py +15 -47
  80. luminarycloud/simulation.py +66 -3
  81. luminarycloud/simulation_param.py +0 -9
  82. luminarycloud/types/matrix3.py +12 -0
  83. luminarycloud/vis/__init__.py +2 -0
  84. luminarycloud/vis/interactive_report.py +79 -93
  85. luminarycloud/vis/report.py +219 -65
  86. luminarycloud/vis/visualization.py +60 -0
  87. luminarycloud/volume_selection.py +132 -69
  88. {luminarycloud-0.20.0.dist-info → luminarycloud-0.22.0.dist-info}/METADATA +1 -1
  89. {luminarycloud-0.20.0.dist-info → luminarycloud-0.22.0.dist-info}/RECORD +90 -89
  90. luminarycloud/params/simulation/physics/periodic_pair/__init__.py +0 -2
  91. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/__init__.py +0 -2
  92. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/rotational_periodicity_.py +0 -31
  93. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/translational_periodicity_.py +0 -29
  94. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type_.py +0 -25
  95. {luminarycloud-0.20.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
@@ -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)
@@ -84,6 +84,18 @@ class Geometry(ProtoWrapperBase):
84
84
  res = get_default_client().UpdateGeometry(req)
85
85
  self._proto = res.geometry
86
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
+
87
99
  def copy(self, name: str = "") -> "Geometry":
88
100
  """
89
101
  Copy the geometry.
@@ -292,6 +304,46 @@ class Geometry(ProtoWrapperBase):
292
304
  )
293
305
  )
294
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
+
295
347
  def latest_version(self) -> GeometryVersion:
296
348
  """
297
349
  Get the latest version of the geometry.
@@ -346,7 +398,7 @@ class Geometry(ProtoWrapperBase):
346
398
  jitter = random.uniform(0.5, 1.5)
347
399
  time.sleep(2 + jitter)
348
400
 
349
- def create_tag(self, name: str, entities: Sequence[Volume | Surface]) -> None:
401
+ def create_tag(self, name: str, entities: Sequence[Volume | Surface]) -> Tag:
350
402
  """
351
403
  Create a tag in the geometry.
352
404
 
@@ -356,6 +408,11 @@ class Geometry(ProtoWrapperBase):
356
408
  The name of the tag to create.
357
409
  entities : list of Volumes or Surfaces
358
410
  The Volumes and Surfaces to tag.
411
+
412
+ Returns
413
+ -------
414
+ Tag
415
+ The tag that was created.
359
416
  """
360
417
  volume_ids = []
361
418
  surface_ids = []
@@ -367,8 +424,7 @@ class Geometry(ProtoWrapperBase):
367
424
  else:
368
425
  raise TypeError("entities must be of type Volume or Surface")
369
426
 
370
- req = geometrypb.ModifyGeometryRequest(
371
- geometry_id=self.id,
427
+ self._modify(
372
428
  modification=gpb.Modification(
373
429
  mod_type=gpb.Modification.MODIFICATION_TYPE_CREATE_TAG,
374
430
  create_or_update_tag=gpb.CreateOrUpdateTag(
@@ -378,9 +434,9 @@ class Geometry(ProtoWrapperBase):
378
434
  ),
379
435
  ),
380
436
  )
381
- get_default_client().ModifyGeometry(req)
437
+ return self._get_tag_by_name(name)
382
438
 
383
- def rename_tag(self, old_name: str, new_name: str) -> None:
439
+ def rename_tag(self, old_name: str, new_name: str) -> Tag:
384
440
  """
385
441
  Rename a tag in the geometry.
386
442
 
@@ -390,6 +446,11 @@ class Geometry(ProtoWrapperBase):
390
446
  The name of the tag to rename.
391
447
  new_name : str
392
448
  The new name for the tag.
449
+
450
+ Returns
451
+ -------
452
+ Tag
453
+ The updated tag.
393
454
  """
394
455
  self._modify(
395
456
  modification=gpb.Modification(
@@ -400,8 +461,9 @@ class Geometry(ProtoWrapperBase):
400
461
  ),
401
462
  ),
402
463
  )
464
+ return self._get_tag_by_name(new_name)
403
465
 
404
- def untag_entities(self, name: str, entities: Sequence[Volume | Surface]) -> None:
466
+ def untag_entities(self, name: str, entities: Sequence[Volume | Surface]) -> Tag | None:
405
467
  """
406
468
  Untag entities from a tag in the geometry.
407
469
 
@@ -412,6 +474,11 @@ class Geometry(ProtoWrapperBase):
412
474
  entities : list of Volumes or Surfaces
413
475
  The Volumes and Surfaces to untag. If empty, all entities with the
414
476
  tag will be untagged.
477
+
478
+ Returns
479
+ -------
480
+ Tag
481
+ The updated tag.
415
482
  """
416
483
  volume_ids = []
417
484
  surface_ids = []
@@ -433,8 +500,12 @@ class Geometry(ProtoWrapperBase):
433
500
  ),
434
501
  ),
435
502
  )
503
+ try:
504
+ return self._get_tag_by_name(name)
505
+ except ValueError:
506
+ return None
436
507
 
437
- def update_tag(self, name: str, entities: Sequence[Volume | Surface]) -> None:
508
+ def update_tag(self, name: str, entities: Sequence[Volume | Surface]) -> Tag:
438
509
  """
439
510
  Adds entities to a tag in the geometry.
440
511
 
@@ -443,6 +514,11 @@ class Geometry(ProtoWrapperBase):
443
514
  name : str
444
515
  The name of the tag to update.
445
516
  entities : list of Volumes or Surfaces
517
+
518
+ Returns
519
+ -------
520
+ Tag
521
+ The updated tag.
446
522
  """
447
523
  volume_ids = []
448
524
  surface_ids = []
@@ -464,6 +540,7 @@ class Geometry(ProtoWrapperBase):
464
540
  ),
465
541
  ),
466
542
  )
543
+ return self._get_tag_by_name(name)
467
544
 
468
545
  def list_tags(self) -> list[Tag]:
469
546
  """
@@ -479,6 +556,25 @@ class Geometry(ProtoWrapperBase):
479
556
  res: geometrypb.ListTagsResponse = get_default_client().ListTags(req)
480
557
  return [Tag(t) for t in res.tags]
481
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
+
482
578
  def list_entities(self) -> tuple[list[Surface], list[Volume]]:
483
579
  """
484
580
  List all the entities in the geometry.
@@ -3,6 +3,7 @@ from datetime import datetime
3
3
 
4
4
  from ._client import get_default_client
5
5
  from ._helpers._timestamp_to_datetime import timestamp_to_datetime
6
+ from ._helpers.pagination import PaginationIterator
6
7
  from ._proto.api.v0.luminarycloud.geometry import geometry_pb2 as geometrypb
7
8
  from ._proto.geometry import geometry_pb2 as gpb
8
9
  from ._wrapper import ProtoWrapper, ProtoWrapperBase
@@ -87,7 +88,7 @@ class GeometryVersion(ProtoWrapperBase):
87
88
  ]
88
89
  return surfaces, volumes
89
90
 
90
- def copy_to_new_geometry(self, name: str = "") -> Geometry:
91
+ def copy_to_new_geometry(self, name: str = "", request_id: str = "") -> Geometry:
91
92
  """
92
93
  Copy this GeometryVersion and create a new Geometry containing only that newly copied version.
93
94
 
@@ -95,6 +96,11 @@ class GeometryVersion(ProtoWrapperBase):
95
96
  ----------
96
97
  name : str, optional
97
98
  The name of the new Geometry. If not provided, a default name will be used.
99
+ request_id : str, optional
100
+ A deduplication key, useful for idempotency. The first time copy_to_new_geometry() is
101
+ called with a given request_id, a new geometry will be created. Subsequent calls with
102
+ the same request_id will return the same geometry. If not provided, no deduplication is
103
+ done. Max length: 256 characters.
98
104
 
99
105
  Returns
100
106
  -------
@@ -104,6 +110,7 @@ class GeometryVersion(ProtoWrapperBase):
104
110
  req = geometrypb.CopyGeometryFromVersionRequest(
105
111
  geometry_version_id=self.id,
106
112
  name=name,
113
+ request_id=request_id,
107
114
  )
108
115
  res: geometrypb.CopyGeometryFromVersionResponse = (
109
116
  get_default_client().CopyGeometryFromVersion(req)
@@ -173,14 +180,39 @@ class GeometryVersion(ProtoWrapperBase):
173
180
  return res.sdk_code
174
181
 
175
182
 
183
+ class GeometryVersionIterator(PaginationIterator[GeometryVersion]):
184
+ """Iterator class for geometry versions that provides length hint."""
185
+
186
+ def __init__(self, geometry_id: str, unfiltered: bool, page_size: int):
187
+ super().__init__(page_size)
188
+ self._geometry_id: str = geometry_id
189
+ self._unfiltered: bool = unfiltered
190
+
191
+ def _fetch_page(
192
+ self, page_size: int, page_token: str
193
+ ) -> tuple[list[GeometryVersion], str, int]:
194
+ req = geometrypb.ListGeometryVersionsRequest(
195
+ page_size=page_size,
196
+ page_token=page_token,
197
+ geometry_id=self._geometry_id,
198
+ unfiltered=self._unfiltered,
199
+ )
200
+ res = self._client.ListGeometryVersions(req)
201
+ return (
202
+ [GeometryVersion(gv) for gv in res.geometry_versions],
203
+ res.next_page_token,
204
+ res.total_count,
205
+ )
206
+
207
+
176
208
  def get_geometry_version(id: str) -> GeometryVersion:
177
209
  """
178
- Get a specific geometry version with the given ID.
210
+ Get a geometry version by its ID.
179
211
 
180
212
  Parameters
181
213
  ----------
182
214
  id : str
183
- Geometry version ID.
215
+ ID of the geometry version to get.
184
216
 
185
217
  Returns
186
218
  -------
@@ -191,3 +223,25 @@ def get_geometry_version(id: str) -> GeometryVersion:
191
223
  req = geometrypb.GetGeometryVersionRequest(geometry_version_id=id)
192
224
  res: geometrypb.GetGeometryVersionResponse = get_default_client().GetGeometryVersion(req)
193
225
  return GeometryVersion(res.geometry_version)
226
+
227
+
228
+ def update_geometry_version(id: str, name: str) -> GeometryVersion:
229
+ """
230
+ Update a geometry version.
231
+
232
+ Parameters
233
+ ----------
234
+ id : str
235
+ ID of the geometry version to update.
236
+ name : str
237
+ The new name for the geometry version. Pass an empty string to clear the name.
238
+
239
+ Returns
240
+ -------
241
+ GeometryVersion
242
+ The updated GeometryVersion.
243
+ """
244
+
245
+ req = geometrypb.UpdateGeometryVersionRequest(geometry_version_id=id, name=name)
246
+ res: geometrypb.UpdateGeometryVersionResponse = get_default_client().UpdateGeometryVersion(req)
247
+ return GeometryVersion(res.geometry_version)