luminarycloud 0.22.1__py3-none-any.whl → 0.22.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 (64) hide show
  1. luminarycloud/_client/client.py +5 -3
  2. luminarycloud/_helpers/__init__.py +9 -0
  3. luminarycloud/_helpers/_inference_jobs.py +227 -0
  4. luminarycloud/_helpers/_parse_iso_datetime.py +54 -0
  5. luminarycloud/_helpers/proto_decorator.py +38 -7
  6. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +45 -25
  7. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +30 -0
  8. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +34 -0
  9. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +12 -0
  10. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +118 -45
  11. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +246 -2
  12. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.py +34 -0
  13. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.pyi +12 -0
  14. luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2.py +93 -33
  15. luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2.pyi +105 -0
  16. luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2_grpc.py +70 -0
  17. luminarycloud/_proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference_pb2_grpc.pyi +29 -0
  18. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.py +29 -7
  19. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.pyi +39 -0
  20. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.py +36 -0
  21. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.pyi +18 -0
  22. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.py +70 -70
  23. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.pyi +5 -5
  24. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +163 -153
  25. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +37 -3
  26. luminarycloud/_proto/client/simulation_pb2.py +356 -337
  27. luminarycloud/_proto/client/simulation_pb2.pyi +89 -3
  28. luminarycloud/_proto/physicsaiinferenceservice/physicsaiinferenceservice_pb2.py +9 -4
  29. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2.py +6 -3
  30. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.py +34 -0
  31. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.pyi +12 -0
  32. luminarycloud/_wrapper.py +53 -7
  33. luminarycloud/feature_modification.py +25 -32
  34. luminarycloud/geometry.py +6 -6
  35. luminarycloud/outputs/__init__.py +2 -0
  36. luminarycloud/outputs/output_definitions.py +3 -3
  37. luminarycloud/outputs/stopping_conditions.py +94 -0
  38. luminarycloud/params/enum/_enum_wrappers.py +16 -0
  39. luminarycloud/params/geometry/shapes.py +33 -33
  40. luminarycloud/params/simulation/adaptive_mesh_refinement/__init__.py +1 -0
  41. luminarycloud/params/simulation/adaptive_mesh_refinement/active_region_.py +83 -0
  42. luminarycloud/params/simulation/adaptive_mesh_refinement/boundary_layer_profile_.py +1 -1
  43. luminarycloud/params/simulation/adaptive_mesh_refinement_.py +8 -1
  44. luminarycloud/physics_ai/__init__.py +7 -0
  45. luminarycloud/physics_ai/inference.py +166 -199
  46. luminarycloud/physics_ai/models.py +22 -0
  47. luminarycloud/pipelines/api.py +45 -9
  48. luminarycloud/project.py +56 -2
  49. luminarycloud/simulation.py +25 -0
  50. luminarycloud/types/__init__.py +2 -0
  51. luminarycloud/types/ids.py +2 -0
  52. luminarycloud/vis/__init__.py +1 -0
  53. luminarycloud/vis/filters.py +97 -0
  54. luminarycloud/vis/visualization.py +3 -0
  55. luminarycloud/volume_selection.py +6 -6
  56. luminarycloud/workflow_utils.py +149 -0
  57. {luminarycloud-0.22.1.dist-info → luminarycloud-0.22.2.dist-info}/METADATA +1 -1
  58. {luminarycloud-0.22.1.dist-info → luminarycloud-0.22.2.dist-info}/RECORD +59 -60
  59. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +0 -61
  60. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +0 -85
  61. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2_grpc.py +0 -67
  62. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2_grpc.pyi +0 -26
  63. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +0 -69
  64. {luminarycloud-0.22.1.dist-info → luminarycloud-0.22.2.dist-info}/WHEEL +0 -0
@@ -9,6 +9,7 @@ import google.protobuf.internal.containers
9
9
  import google.protobuf.internal.enum_type_wrapper
10
10
  import google.protobuf.message
11
11
  import luminarycloud._proto.base.base_pb2
12
+ import luminarycloud._proto.cad.shape_pb2
12
13
  import luminarycloud._proto.client.entity_pb2
13
14
  import luminarycloud._proto.entitygroup.entitygroup_pb2
14
15
  import luminarycloud._proto.output.output_pb2
@@ -740,6 +741,14 @@ class _PtTableFluidEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._E
740
741
  """R410A."""
741
742
  REAL_GAS_R507A: _PtTableFluid.ValueType # 56561
742
743
  """R507A."""
744
+ REAL_GAS_R513A: _PtTableFluid.ValueType # 13621
745
+ """R513A."""
746
+ REAL_GAS_R513B: _PtTableFluid.ValueType # 5656
747
+ """R513B."""
748
+ REAL_GAS_R515A: _PtTableFluid.ValueType # 55001
749
+ """R515A."""
750
+ REAL_GAS_R515B: _PtTableFluid.ValueType # 5076
751
+ """R515B."""
743
752
  REAL_GAS_RC318: _PtTableFluid.ValueType # 66529
744
753
  """RC318."""
745
754
  REAL_GAS_SES36: _PtTableFluid.ValueType # 14408
@@ -990,6 +999,14 @@ REAL_GAS_R410A: PtTableFluid.ValueType # 59981
990
999
  """R410A."""
991
1000
  REAL_GAS_R507A: PtTableFluid.ValueType # 56561
992
1001
  """R507A."""
1002
+ REAL_GAS_R513A: PtTableFluid.ValueType # 13621
1003
+ """R513A."""
1004
+ REAL_GAS_R513B: PtTableFluid.ValueType # 5656
1005
+ """R513B."""
1006
+ REAL_GAS_R515A: PtTableFluid.ValueType # 55001
1007
+ """R515A."""
1008
+ REAL_GAS_R515B: PtTableFluid.ValueType # 5076
1009
+ """R515B."""
993
1010
  REAL_GAS_RC318: PtTableFluid.ValueType # 66529
994
1011
  """RC318."""
995
1012
  REAL_GAS_SES36: PtTableFluid.ValueType # 14408
@@ -4469,7 +4486,7 @@ class ReferenceValues(google.protobuf.message.Message):
4469
4486
  global___ReferenceValues = ReferenceValues
4470
4487
 
4471
4488
  class BoundaryLayerProfile(google.protobuf.message.Message):
4472
- """TODO: no help"""
4489
+ """Boundary layer meshing parameters to apply to adapted meshes."""
4473
4490
 
4474
4491
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
4475
4492
 
@@ -4502,6 +4519,67 @@ class BoundaryLayerProfile(google.protobuf.message.Message):
4502
4519
 
4503
4520
  global___BoundaryLayerProfile = BoundaryLayerProfile
4504
4521
 
4522
+ class ActiveRegion(google.protobuf.message.Message):
4523
+ """Region(s) within which the mesh is adapted at full resolution. Outside
4524
+ of these regions the mesh is coarsened with increasing distance from the
4525
+ region.
4526
+ """
4527
+
4528
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
4529
+
4530
+ ERROR_WEIGHT_FIELD_NUMBER: builtins.int
4531
+ MAX_DISTANCE_FIELD_NUMBER: builtins.int
4532
+ CUBE_FIELD_NUMBER: builtins.int
4533
+ ORIENTED_CUBE_FIELD_NUMBER: builtins.int
4534
+ CYLINDER_FIELD_NUMBER: builtins.int
4535
+ ANNULAR_CYLINDER_FIELD_NUMBER: builtins.int
4536
+ SPHERE_FIELD_NUMBER: builtins.int
4537
+ SPHERE_SHELL_FIELD_NUMBER: builtins.int
4538
+ @property
4539
+ def error_weight(self) -> luminarycloud._proto.base.base_pb2.AdFloatType:
4540
+ """Error weighting from the active region at Max Distance. Local error
4541
+ weighting is 1.0 inside the region and decreases to Error Weight at the Max
4542
+ Distance.
4543
+ """
4544
+ @property
4545
+ def max_distance(self) -> luminarycloud._proto.base.base_pb2.AdFloatType:
4546
+ """Distance from the active regions at which to apply the full error factor."""
4547
+ @property
4548
+ def cube(self) -> luminarycloud._proto.cad.shape_pb2.Cube:
4549
+ """Coordinate-aligned box."""
4550
+ @property
4551
+ def oriented_cube(self) -> luminarycloud._proto.cad.shape_pb2.OrientedCube:
4552
+ """Box oriented along arbitrary axes."""
4553
+ @property
4554
+ def cylinder(self) -> luminarycloud._proto.cad.shape_pb2.Cylinder:
4555
+ """Cylinder shape."""
4556
+ @property
4557
+ def annular_cylinder(self) -> luminarycloud._proto.cad.shape_pb2.AnnularCylinder:
4558
+ """Cylinder with a hole along the axis."""
4559
+ @property
4560
+ def sphere(self) -> luminarycloud._proto.cad.shape_pb2.Sphere:
4561
+ """Sphere shape."""
4562
+ @property
4563
+ def sphere_shell(self) -> luminarycloud._proto.cad.shape_pb2.SphereShell:
4564
+ """Spherical shell shape."""
4565
+ def __init__(
4566
+ self,
4567
+ *,
4568
+ error_weight: luminarycloud._proto.base.base_pb2.AdFloatType | None = ...,
4569
+ max_distance: luminarycloud._proto.base.base_pb2.AdFloatType | None = ...,
4570
+ cube: luminarycloud._proto.cad.shape_pb2.Cube | None = ...,
4571
+ oriented_cube: luminarycloud._proto.cad.shape_pb2.OrientedCube | None = ...,
4572
+ cylinder: luminarycloud._proto.cad.shape_pb2.Cylinder | None = ...,
4573
+ annular_cylinder: luminarycloud._proto.cad.shape_pb2.AnnularCylinder | None = ...,
4574
+ sphere: luminarycloud._proto.cad.shape_pb2.Sphere | None = ...,
4575
+ sphere_shell: luminarycloud._proto.cad.shape_pb2.SphereShell | None = ...,
4576
+ ) -> None: ...
4577
+ def HasField(self, field_name: typing_extensions.Literal["annular_cylinder", b"annular_cylinder", "cube", b"cube", "cylinder", b"cylinder", "error_weight", b"error_weight", "max_distance", b"max_distance", "oriented_cube", b"oriented_cube", "shape", b"shape", "sphere", b"sphere", "sphere_shell", b"sphere_shell"]) -> builtins.bool: ...
4578
+ def ClearField(self, field_name: typing_extensions.Literal["annular_cylinder", b"annular_cylinder", "cube", b"cube", "cylinder", b"cylinder", "error_weight", b"error_weight", "max_distance", b"max_distance", "oriented_cube", b"oriented_cube", "shape", b"shape", "sphere", b"sphere", "sphere_shell", b"sphere_shell"]) -> None: ...
4579
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["shape", b"shape"]) -> typing_extensions.Literal["cube", "oriented_cube", "cylinder", "annular_cylinder", "sphere", "sphere_shell"] | None: ...
4580
+
4581
+ global___ActiveRegion = ActiveRegion
4582
+
4505
4583
  class AdaptiveMeshRefinement(google.protobuf.message.Message):
4506
4584
  """Adaptive Mesh Refinement"""
4507
4585
 
@@ -4517,6 +4595,7 @@ class AdaptiveMeshRefinement(google.protobuf.message.Message):
4517
4595
  ALL_TET_FIELD_NUMBER: builtins.int
4518
4596
  USER_SCALING_FIELD_NUMBER: builtins.int
4519
4597
  BOUNDARY_LAYER_PROFILE_FIELD_NUMBER: builtins.int
4598
+ ACTIVE_REGION_FIELD_NUMBER: builtins.int
4520
4599
  @property
4521
4600
  def refinement_iterations(self) -> luminarycloud._proto.base.base_pb2.Int:
4522
4601
  """Number of refinement iterations to perform."""
@@ -4546,7 +4625,13 @@ class AdaptiveMeshRefinement(google.protobuf.message.Message):
4546
4625
  """Scale factor between the geometry and the mesh."""
4547
4626
  @property
4548
4627
  def boundary_layer_profile(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BoundaryLayerProfile]:
4549
- """TODO: no help"""
4628
+ """Boundary layer meshing parameters to apply to adapted meshes."""
4629
+ @property
4630
+ def active_region(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ActiveRegion]:
4631
+ """Region(s) within which the mesh is adapted at full resolution. Outside
4632
+ of these regions the mesh is coarsened with increasing distance from the
4633
+ region.
4634
+ """
4550
4635
  def __init__(
4551
4636
  self,
4552
4637
  *,
@@ -4560,9 +4645,10 @@ class AdaptiveMeshRefinement(google.protobuf.message.Message):
4560
4645
  all_tet: global___AllTet.ValueType = ...,
4561
4646
  user_scaling: luminarycloud._proto.base.base_pb2.AdFloatType | None = ...,
4562
4647
  boundary_layer_profile: collections.abc.Iterable[global___BoundaryLayerProfile] | None = ...,
4648
+ active_region: collections.abc.Iterable[global___ActiveRegion] | None = ...,
4563
4649
  ) -> None: ...
4564
4650
  def HasField(self, field_name: typing_extensions.Literal["final_target_complexity", b"final_target_complexity", "initial_target_complexity", b"initial_target_complexity", "max_refinement_interval", b"max_refinement_interval", "refinement_dispatch_interval", b"refinement_dispatch_interval", "refinement_iterations", b"refinement_iterations", "target_cv_millions", b"target_cv_millions", "user_scaling", b"user_scaling"]) -> builtins.bool: ...
4565
- def ClearField(self, field_name: typing_extensions.Literal["all_tet", b"all_tet", "boundary_layer_profile", b"boundary_layer_profile", "final_target_complexity", b"final_target_complexity", "initial_target_complexity", b"initial_target_complexity", "max_refinement_interval", b"max_refinement_interval", "meshing_method", b"meshing_method", "refinement_dispatch_interval", b"refinement_dispatch_interval", "refinement_iterations", b"refinement_iterations", "target_cv_millions", b"target_cv_millions", "user_scaling", b"user_scaling"]) -> None: ...
4651
+ def ClearField(self, field_name: typing_extensions.Literal["active_region", b"active_region", "all_tet", b"all_tet", "boundary_layer_profile", b"boundary_layer_profile", "final_target_complexity", b"final_target_complexity", "initial_target_complexity", b"initial_target_complexity", "max_refinement_interval", b"max_refinement_interval", "meshing_method", b"meshing_method", "refinement_dispatch_interval", b"refinement_dispatch_interval", "refinement_iterations", b"refinement_iterations", "target_cv_millions", b"target_cv_millions", "user_scaling", b"user_scaling"]) -> None: ...
4566
4652
 
4567
4653
  global___AdaptiveMeshRefinement = AdaptiveMeshRefinement
4568
4654
 
@@ -12,11 +12,12 @@ from google.protobuf import symbol_database as _symbol_database
12
12
  _sym_db = _symbol_database.Default()
13
13
 
14
14
 
15
- from luminarycloud._proto.base import base_pb2 as proto_dot_base_dot_base__pb2
15
+ from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
16
16
  from luminarycloud._proto.api.v0.luminarycloud.physicsaiinference import physicsaiinference_pb2 as proto_dot_api_dot_v0_dot_luminarycloud_dot_physicsaiinference_dot_physicsaiinference__pb2
17
+ from luminarycloud._proto.ratelimit import ratelimit_pb2 as proto_dot_ratelimit_dot_ratelimit__pb2
17
18
 
18
19
 
19
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n?proto/physicsaiinferenceservice/physicsaiinferenceservice.proto\x12(luminary.proto.physicsaiinferenceservice\x1a\x15proto/base/base.proto\x1a\x46proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference.proto2\xda\x06\n\x19PhysicsAiInferenceService\x12\xcd\x01\n\x19\x43reateInferenceServiceJob\x12X.luminary.proto.api.v0.luminarycloud.physicsaiinference.CreateInferenceServiceJobRequest\x1aV.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobResponse\x12\xd2\x01\n\x1e\x43reateInferenceServiceJobAsync\x12X.luminary.proto.api.v0.luminarycloud.physicsaiinference.CreateInferenceServiceJobRequest\x1aV.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobResponse\x12\xc7\x01\n\x16GetInferenceServiceJob\x12U.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobRequest\x1aV.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobResponse\x12\xcd\x01\n\x18ListInferenceServiceJobs\x12W.luminary.proto.api.v0.luminarycloud.physicsaiinference.ListInferenceServiceJobsRequest\x1aX.luminary.proto.api.v0.luminarycloud.physicsaiinference.ListInferenceServiceJobsResponseB8Z6luminarycloud.com/core/proto/physicsaiinferenceserviceb\x06proto3')
20
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n?proto/physicsaiinferenceservice/physicsaiinferenceservice.proto\x12(luminary.proto.physicsaiinferenceservice\x1a\x1bgoogle/protobuf/empty.proto\x1a\x46proto/api/v0/luminarycloud/physicsaiinference/physicsaiinference.proto\x1a\x1fproto/ratelimit/ratelimit.proto2\xb1\t\n\x19PhysicsAiInferenceService\x12\xd9\x01\n\x19\x43reateInferenceServiceJob\x12X.luminary.proto.api.v0.luminarycloud.physicsaiinference.CreateInferenceServiceJobRequest\x1aV.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobResponse\"\n\x8a\xb5\x18\x06\x08\n\x12\x02\x08\x01\x12\xde\x01\n\x1e\x43reateInferenceServiceJobAsync\x12X.luminary.proto.api.v0.luminarycloud.physicsaiinference.CreateInferenceServiceJobRequest\x1aV.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobResponse\"\n\x8a\xb5\x18\x06\x08\n\x12\x02\x08\x01\x12\xbb\x01\n\x12GetInferenceFields\x12Q.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceFieldsRequest\x1aR.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceFieldsResponse\x12\xc7\x01\n\x16GetInferenceServiceJob\x12U.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobRequest\x1aV.luminary.proto.api.v0.luminarycloud.physicsaiinference.GetInferenceServiceJobResponse\x12\xcd\x01\n\x18ListInferenceServiceJobs\x12W.luminary.proto.api.v0.luminarycloud.physicsaiinference.ListInferenceServiceJobsRequest\x1aX.luminary.proto.api.v0.luminarycloud.physicsaiinference.ListInferenceServiceJobsResponse\x12\x7f\n\x12\x44\x65leteInferenceJob\x12Q.luminary.proto.api.v0.luminarycloud.physicsaiinference.DeleteInferenceJobRequest\x1a\x16.google.protobuf.EmptyB8Z6luminarycloud.com/core/proto/physicsaiinferenceserviceb\x06proto3')
20
21
 
21
22
 
22
23
 
@@ -25,6 +26,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
25
26
 
26
27
  DESCRIPTOR._options = None
27
28
  DESCRIPTOR._serialized_options = b'Z6luminarycloud.com/core/proto/physicsaiinferenceservice'
28
- _PHYSICSAIINFERENCESERVICE._serialized_start=205
29
- _PHYSICSAIINFERENCESERVICE._serialized_end=1063
29
+ _PHYSICSAIINFERENCESERVICE.methods_by_name['CreateInferenceServiceJob']._options = None
30
+ _PHYSICSAIINFERENCESERVICE.methods_by_name['CreateInferenceServiceJob']._serialized_options = b'\212\265\030\006\010\n\022\002\010\001'
31
+ _PHYSICSAIINFERENCESERVICE.methods_by_name['CreateInferenceServiceJobAsync']._options = None
32
+ _PHYSICSAIINFERENCESERVICE.methods_by_name['CreateInferenceServiceJobAsync']._serialized_options = b'\212\265\030\006\010\n\022\002\010\001'
33
+ _PHYSICSAIINFERENCESERVICE._serialized_start=244
34
+ _PHYSICSAIINFERENCESERVICE._serialized_end=1445
30
35
  # @@protoc_insertion_point(module_scope)
@@ -13,9 +13,10 @@ _sym_db = _symbol_database.Default()
13
13
 
14
14
 
15
15
  from luminarycloud._proto.api.v0.luminarycloud.physics_ai import physics_ai_pb2 as proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2
16
+ from luminarycloud._proto.ratelimit import ratelimit_pb2 as proto_dot_ratelimit_dot_ratelimit__pb2
16
17
 
17
18
 
18
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n=proto/physicsaitrainingservice/physicsaitrainingservice.proto\x12\'luminary.proto.physicsaitrainingservice\x1a\x36proto/api/v0/luminarycloud/physics_ai/physics_ai.proto2\xf4\x02\n\x18PhysicsAiTrainingService\x12\xaa\x01\n\x11SubmitTrainingJob\x12H.luminary.proto.api.v0.luminarycloud.physics_ai.SubmitTrainingJobRequest\x1aI.luminary.proto.api.v0.luminarycloud.physics_ai.SubmitTrainingJobResponse\"\x00\x12\xaa\x01\n\x11\x43\x61ncelTrainingJob\x12H.luminary.proto.api.v0.luminarycloud.physics_ai.CancelTrainingJobRequest\x1aI.luminary.proto.api.v0.luminarycloud.physics_ai.CancelTrainingJobResponse\"\x00\x42\x37Z5luminarycloud.com/core/proto/physicsaitrainingserviceb\x06proto3')
19
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n=proto/physicsaitrainingservice/physicsaitrainingservice.proto\x12\'luminary.proto.physicsaitrainingservice\x1a\x36proto/api/v0/luminarycloud/physics_ai/physics_ai.proto\x1a\x1fproto/ratelimit/ratelimit.proto2\x9f\x04\n\x18PhysicsAiTrainingService\x12\x9e\x01\n\rCreateDataset\x12\x44.luminary.proto.api.v0.luminarycloud.physics_ai.CreateDatasetRequest\x1a\x45.luminary.proto.api.v0.luminarycloud.physics_ai.CreateDatasetResponse\"\x00\x12\xb4\x01\n\x11SubmitTrainingJob\x12H.luminary.proto.api.v0.luminarycloud.physics_ai.SubmitTrainingJobRequest\x1aI.luminary.proto.api.v0.luminarycloud.physics_ai.SubmitTrainingJobResponse\"\n\x8a\xb5\x18\x06\x08\x05\x12\x02\x08\x01\x12\xaa\x01\n\x11\x43\x61ncelTrainingJob\x12H.luminary.proto.api.v0.luminarycloud.physics_ai.CancelTrainingJobRequest\x1aI.luminary.proto.api.v0.luminarycloud.physics_ai.CancelTrainingJobResponse\"\x00\x42\x37Z5luminarycloud.com/core/proto/physicsaitrainingserviceb\x06proto3')
19
20
 
20
21
 
21
22
 
@@ -24,6 +25,8 @@ if _descriptor._USE_C_DESCRIPTORS == False:
24
25
 
25
26
  DESCRIPTOR._options = None
26
27
  DESCRIPTOR._serialized_options = b'Z5luminarycloud.com/core/proto/physicsaitrainingservice'
27
- _PHYSICSAITRAININGSERVICE._serialized_start=163
28
- _PHYSICSAITRAININGSERVICE._serialized_end=535
28
+ _PHYSICSAITRAININGSERVICE.methods_by_name['SubmitTrainingJob']._options = None
29
+ _PHYSICSAITRAININGSERVICE.methods_by_name['SubmitTrainingJob']._serialized_options = b'\212\265\030\006\010\005\022\002\010\001'
30
+ _PHYSICSAITRAININGSERVICE._serialized_start=196
31
+ _PHYSICSAITRAININGSERVICE._serialized_end=739
29
32
  # @@protoc_insertion_point(module_scope)
@@ -15,6 +15,11 @@ class PhysicsAiTrainingServiceStub(object):
15
15
  Args:
16
16
  channel: A grpc.Channel.
17
17
  """
18
+ self.CreateDataset = channel.unary_unary(
19
+ '/luminary.proto.physicsaitrainingservice.PhysicsAiTrainingService/CreateDataset',
20
+ request_serializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.CreateDatasetRequest.SerializeToString,
21
+ response_deserializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.CreateDatasetResponse.FromString,
22
+ )
18
23
  self.SubmitTrainingJob = channel.unary_unary(
19
24
  '/luminary.proto.physicsaitrainingservice.PhysicsAiTrainingService/SubmitTrainingJob',
20
25
  request_serializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.SubmitTrainingJobRequest.SerializeToString,
@@ -31,6 +36,13 @@ class PhysicsAiTrainingServiceServicer(object):
31
36
  """PhysicsAiTrainingService provides training functionality for Physics AI
32
37
  """
33
38
 
39
+ def CreateDataset(self, request, context):
40
+ """Create a Physics AI dataset
41
+ """
42
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
43
+ context.set_details('Method not implemented!')
44
+ raise NotImplementedError('Method not implemented!')
45
+
34
46
  def SubmitTrainingJob(self, request, context):
35
47
  """Submit a physics AI training job
36
48
  """
@@ -48,6 +60,11 @@ class PhysicsAiTrainingServiceServicer(object):
48
60
 
49
61
  def add_PhysicsAiTrainingServiceServicer_to_server(servicer, server):
50
62
  rpc_method_handlers = {
63
+ 'CreateDataset': grpc.unary_unary_rpc_method_handler(
64
+ servicer.CreateDataset,
65
+ request_deserializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.CreateDatasetRequest.FromString,
66
+ response_serializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.CreateDatasetResponse.SerializeToString,
67
+ ),
51
68
  'SubmitTrainingJob': grpc.unary_unary_rpc_method_handler(
52
69
  servicer.SubmitTrainingJob,
53
70
  request_deserializer=proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.SubmitTrainingJobRequest.FromString,
@@ -69,6 +86,23 @@ class PhysicsAiTrainingService(object):
69
86
  """PhysicsAiTrainingService provides training functionality for Physics AI
70
87
  """
71
88
 
89
+ @staticmethod
90
+ def CreateDataset(request,
91
+ target,
92
+ options=(),
93
+ channel_credentials=None,
94
+ call_credentials=None,
95
+ insecure=False,
96
+ compression=None,
97
+ wait_for_ready=None,
98
+ timeout=None,
99
+ metadata=None):
100
+ return grpc.experimental.unary_unary(request, target, '/luminary.proto.physicsaitrainingservice.PhysicsAiTrainingService/CreateDataset',
101
+ proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.CreateDatasetRequest.SerializeToString,
102
+ proto_dot_api_dot_v0_dot_luminarycloud_dot_physics__ai_dot_physics__ai__pb2.CreateDatasetResponse.FromString,
103
+ options, channel_credentials,
104
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
105
+
72
106
  @staticmethod
73
107
  def SubmitTrainingJob(request,
74
108
  target,
@@ -10,6 +10,11 @@ class PhysicsAiTrainingServiceStub:
10
10
  """PhysicsAiTrainingService provides training functionality for Physics AI"""
11
11
 
12
12
  def __init__(self, channel: grpc.Channel) -> None: ...
13
+ CreateDataset: grpc.UnaryUnaryMultiCallable[
14
+ luminarycloud._proto.api.v0.luminarycloud.physics_ai.physics_ai_pb2.CreateDatasetRequest,
15
+ luminarycloud._proto.api.v0.luminarycloud.physics_ai.physics_ai_pb2.CreateDatasetResponse,
16
+ ]
17
+ """Create a Physics AI dataset"""
13
18
  SubmitTrainingJob: grpc.UnaryUnaryMultiCallable[
14
19
  luminarycloud._proto.api.v0.luminarycloud.physics_ai.physics_ai_pb2.SubmitTrainingJobRequest,
15
20
  luminarycloud._proto.api.v0.luminarycloud.physics_ai.physics_ai_pb2.SubmitTrainingJobResponse,
@@ -24,6 +29,13 @@ class PhysicsAiTrainingServiceStub:
24
29
  class PhysicsAiTrainingServiceServicer(metaclass=abc.ABCMeta):
25
30
  """PhysicsAiTrainingService provides training functionality for Physics AI"""
26
31
 
32
+ @abc.abstractmethod
33
+ def CreateDataset(
34
+ self,
35
+ request: luminarycloud._proto.api.v0.luminarycloud.physics_ai.physics_ai_pb2.CreateDatasetRequest,
36
+ context: grpc.ServicerContext,
37
+ ) -> luminarycloud._proto.api.v0.luminarycloud.physics_ai.physics_ai_pb2.CreateDatasetResponse:
38
+ """Create a Physics AI dataset"""
27
39
  @abc.abstractmethod
28
40
  def SubmitTrainingJob(
29
41
  self,
luminarycloud/_wrapper.py CHANGED
@@ -103,20 +103,62 @@ class ProtoWrapper(Generic[P]):
103
103
  def getter(field_name: str) -> Any:
104
104
  return lambda self: getattr(self._proto, field_name)
105
105
 
106
- def wrapped_getter(field_name: str, wrapper: type[ProtoWrapperBase]) -> Any:
107
- return lambda self: wrapper(getattr(self._proto, field_name))
106
+ def wrapped_getter(field_name: str, wrapper: type) -> Any:
107
+ def _get(self):
108
+ proto_value = getattr(self._proto, field_name)
109
+
110
+ # If it's a ProtoWrapperBase, use the standard pattern
111
+ if issubclass(wrapper, ProtoWrapperBase):
112
+ return wrapper(proto_value)
113
+
114
+ # If it's Vector3, use the conversion pattern
115
+ if wrapper is Vector3:
116
+ return Vector3(
117
+ x=float(proto_value.x), y=float(proto_value.y), z=float(proto_value.z)
118
+ )
119
+
120
+ # For other types, try the constructor directly
121
+ try:
122
+ return wrapper(proto_value)
123
+ except (TypeError, ValueError):
124
+ # Fallback: return the proto value as-is
125
+ return proto_value
126
+
127
+ return _get
108
128
 
109
129
  # This binds the field name to the setter.
110
130
  def setter(field_name: str) -> Callable[[C, Any], None]:
111
131
  return lambda self, value: setattr(self._proto, field_name, value)
112
132
 
113
- def wrapped_setter(
114
- field_name: str, wrapper: type[ProtoWrapperBase]
115
- ) -> Callable[[C, Any], None]:
133
+ def wrapped_setter(field_name: str, wrapper: type) -> Callable[[C, Any], None]:
116
134
  def _set(self: C, value: Any) -> None:
117
135
  if not isinstance(value, wrapper):
118
136
  raise TypeError(f"{field_name} should be a {wrapper.__name__}")
119
- setattr(self._proto, field_name, value._proto)
137
+
138
+ # If it's a ProtoWrapperBase, extract the _proto
139
+ if issubclass(wrapper, ProtoWrapperBase):
140
+ # For protobuf message fields, use CopyFrom instead of direct assignment
141
+ proto_field = getattr(self._proto, field_name)
142
+ proto_field.CopyFrom(value._proto)
143
+
144
+ # If it's Vector3, convert to protobuf Vector3
145
+ elif wrapper is Vector3:
146
+ proto_field = getattr(self._proto, field_name)
147
+ proto_field.x = float(value.x)
148
+ proto_field.y = float(value.y)
149
+ proto_field.z = float(value.z)
150
+
151
+ # For other types, try direct assignment or conversion
152
+ else:
153
+ try:
154
+ # Try using _to_proto method if it exists
155
+ if hasattr(value, "_to_proto"):
156
+ setattr(self._proto, field_name, value._to_proto())
157
+ else:
158
+ setattr(self._proto, field_name, value)
159
+ except (TypeError, ValueError, AttributeError):
160
+ # Fallback: direct assignment
161
+ setattr(self._proto, field_name, value)
120
162
 
121
163
  return _set
122
164
 
@@ -148,7 +190,11 @@ class ProtoWrapper(Generic[P]):
148
190
  pass
149
191
  else:
150
192
  try:
151
- if issubclass(_type, Enum) or issubclass(_type, ProtoWrapperBase):
193
+ if (
194
+ issubclass(_type, Enum)
195
+ or issubclass(_type, ProtoWrapperBase)
196
+ or _type is Vector3
197
+ ):
152
198
  fget = wrapped_getter(field.name, _type)
153
199
  fset = wrapped_setter(field.name, _type)
154
200
  except TypeError:
@@ -148,6 +148,29 @@ def _update_repeated_field(
148
148
  target_field.extend(new_values)
149
149
 
150
150
 
151
+ def _update_create_op_from_shape(create_op: gpb.Create, shape: Shape) -> None:
152
+ """Helper function to replace the shape in a gpb.Create operation with a shape object.
153
+
154
+ Args:
155
+ create: The gpb.Create to modify
156
+ shape: The shape to add
157
+ """
158
+ if isinstance(shape, Sphere):
159
+ create_op.sphere.CopyFrom(shape._to_proto())
160
+ elif isinstance(shape, Cube):
161
+ create_op.box.CopyFrom(shape._to_proto())
162
+ elif isinstance(shape, Cylinder):
163
+ create_op.cylinder.CopyFrom(shape._to_proto())
164
+ elif isinstance(shape, Torus):
165
+ create_op.torus.CopyFrom(shape._to_proto())
166
+ elif isinstance(shape, Cone):
167
+ create_op.cone.CopyFrom(shape._to_proto())
168
+ elif isinstance(shape, HalfSphere):
169
+ create_op.half_sphere.CopyFrom(shape._to_proto())
170
+ else:
171
+ raise TypeError(f"Unsupported shape type: {type(shape)}")
172
+
173
+
151
174
  def modify_import(
152
175
  feature: gpb.Feature,
153
176
  geometry_url: Optional[str] = None,
@@ -205,22 +228,7 @@ def modify_create(
205
228
  create_op = feature_copy.create
206
229
 
207
230
  if shape is not None:
208
- create_op.ClearField("shape")
209
-
210
- if isinstance(shape, Sphere):
211
- create_op.sphere.CopyFrom(shape._to_proto()) # type: ignore
212
- elif isinstance(shape, Cube):
213
- create_op.box.CopyFrom(shape._to_proto()) # type: ignore
214
- elif isinstance(shape, Cylinder):
215
- create_op.cylinder.CopyFrom(shape._to_proto()) # type: ignore
216
- elif isinstance(shape, Torus):
217
- create_op.torus.CopyFrom(shape._to_proto()) # type: ignore
218
- elif isinstance(shape, Cone):
219
- create_op.cone.CopyFrom(shape._to_proto()) # type: ignore
220
- elif isinstance(shape, HalfSphere):
221
- create_op.half_sphere.CopyFrom(shape._to_proto()) # type: ignore
222
- else:
223
- raise TypeError(f"Unsupported shape type: {type(shape)}")
231
+ _update_create_op_from_shape(create_op, shape)
224
232
 
225
233
  return gpb.Modification(
226
234
  mod_type=gpb.Modification.ModificationType.MODIFICATION_TYPE_UPDATE_FEATURE,
@@ -719,22 +727,7 @@ def modify_farfield(
719
727
  farfield_op = feature_copy.farfield
720
728
 
721
729
  if shape is not None:
722
- create_op = gpb.Create()
723
- if isinstance(shape, Sphere):
724
- create_op.sphere.CopyFrom(shape._to_proto()) # type: ignore
725
- elif isinstance(shape, Cube):
726
- create_op.box.CopyFrom(shape._to_proto()) # type: ignore
727
- elif isinstance(shape, Cylinder):
728
- create_op.cylinder.CopyFrom(shape._to_proto()) # type: ignore
729
- elif isinstance(shape, Torus):
730
- create_op.torus.CopyFrom(shape._to_proto()) # type: ignore
731
- elif isinstance(shape, Cone):
732
- create_op.cone.CopyFrom(shape._to_proto()) # type: ignore
733
- elif isinstance(shape, HalfSphere):
734
- create_op.half_sphere.CopyFrom(shape._to_proto()) # type: ignore
735
- else:
736
- raise TypeError(f"Unsupported shape type: {type(shape)}")
737
- farfield_op.create.CopyFrom(create_op)
730
+ _update_create_op_from_shape(farfield_op.create, shape)
738
731
 
739
732
  if volumes is not None:
740
733
  vol_ids = _volumes_to_int_list(volumes)
luminarycloud/geometry.py CHANGED
@@ -178,17 +178,17 @@ class Geometry(ProtoWrapperBase):
178
178
  """
179
179
  create_proto = gpb.Create()
180
180
  if isinstance(shape, shapes.Sphere):
181
- create_proto.sphere.CopyFrom(shape._to_proto()) # type: ignore
181
+ create_proto.sphere.CopyFrom(shape._to_proto())
182
182
  elif isinstance(shape, shapes.Cube):
183
- create_proto.box.CopyFrom(shape._to_proto()) # type: ignore
183
+ create_proto.box.CopyFrom(shape._to_proto())
184
184
  elif isinstance(shape, shapes.Cylinder):
185
- create_proto.cylinder.CopyFrom(shape._to_proto()) # type: ignore
185
+ create_proto.cylinder.CopyFrom(shape._to_proto())
186
186
  elif isinstance(shape, shapes.Torus):
187
- create_proto.torus.CopyFrom(shape._to_proto()) # type: ignore
187
+ create_proto.torus.CopyFrom(shape._to_proto())
188
188
  elif isinstance(shape, shapes.Cone):
189
- create_proto.cone.CopyFrom(shape._to_proto()) # type: ignore
189
+ create_proto.cone.CopyFrom(shape._to_proto())
190
190
  elif isinstance(shape, shapes.HalfSphere):
191
- create_proto.half_sphere.CopyFrom(shape._to_proto()) # type: ignore
191
+ create_proto.half_sphere.CopyFrom(shape._to_proto())
192
192
  else:
193
193
  raise TypeError(f"Unsupported shape for farfield: {type(shape)}")
194
194
  self._modify(
@@ -19,6 +19,8 @@ from .output_definitions import (
19
19
 
20
20
  from .stopping_conditions import (
21
21
  StoppingCondition as StoppingCondition,
22
+ StoppingConditionResult as StoppingConditionResult,
23
+ StoppingConditionStatusResult as StoppingConditionStatusResult,
22
24
  GeneralStoppingConditions as GeneralStoppingConditions,
23
25
  create_or_update_stopping_condition as create_or_update_stopping_condition,
24
26
  get_stopping_condition as get_stopping_condition,
@@ -125,7 +125,7 @@ class SurfaceAverageOutputDefinition(CodeRepr):
125
125
  space_averaging_type: SpaceAveragingType = SpaceAveragingType.AREA
126
126
  "Type of spatial averaging to use."
127
127
  vector_component: Vector3Component = Vector3Component.UNSPECIFIED
128
- "For vector quantities, the component to measure."
128
+ "For vector quantities, the component to measure must be specified (X, Y, or Z)."
129
129
 
130
130
  def _to_proto(self) -> feoutputpb.OutputNode:
131
131
  proto = feoutputpb.OutputNode(
@@ -309,7 +309,7 @@ class PointProbeOutputDefinition(CodeRepr):
309
309
  include: OutputDefinitionInclusions = field(default_factory=OutputDefinitionInclusions)
310
310
  "Configuration for what values to include."
311
311
  vector_component: Vector3Component = Vector3Component.UNSPECIFIED
312
- "For vector quantities, the component to measure."
312
+ "For vector quantities, the component to measure must be specified (X, Y, or Z)."
313
313
 
314
314
  def _to_proto(self) -> feoutputpb.OutputNode:
315
315
  proto = feoutputpb.OutputNode(
@@ -356,7 +356,7 @@ class VolumeReductionOutputDefinition(CodeRepr):
356
356
  volume_reduction_type: VolumeReductionType = VolumeReductionType.AVERAGE
357
357
  "Type of reduction to perform for each volume."
358
358
  vector_component: Vector3Component = Vector3Component.UNSPECIFIED
359
- "For vector quantities, the component to measure."
359
+ "For vector quantities, the component to measure must be specified (X, Y, or Z)."
360
360
 
361
361
  def _to_proto(self) -> feoutputpb.OutputNode:
362
362
  proto = feoutputpb.OutputNode(
@@ -2,6 +2,7 @@
2
2
 
3
3
  from .._client import get_default_client
4
4
  from .._proto.api.v0.luminarycloud.stopping_condition import stopping_condition_pb2 as stopcondpb
5
+ from .._proto.api.v0.luminarycloud.simulation import simulation_pb2 as simulationpb
5
6
  from .._proto.output import output_pb2 as outputpb
6
7
  from dataclasses import dataclass
7
8
 
@@ -213,3 +214,96 @@ def update_general_stopping_conditions(
213
214
  )
214
215
  res = get_default_client().UpdateBasicStoppingConditions(req)
215
216
  return GeneralStoppingConditions._from_proto(res.basic_stopping_conditions)
217
+
218
+
219
+ @dataclass
220
+ class StoppingConditionResult:
221
+ """
222
+ Result of evaluating a single stopping condition.
223
+
224
+ Attributes
225
+ ----------
226
+ output_name : str
227
+ Name of the output being monitored.
228
+ threshold : float
229
+ The threshold value for this condition.
230
+ value : float
231
+ The actual value of the monitored output.
232
+ satisfied : bool
233
+ Whether this condition was satisfied.
234
+ is_force_condition : bool
235
+ Whether this is a force-stop condition (e.g., max iterations, max time).
236
+ Force conditions cause immediate termination regardless of other conditions.
237
+ """
238
+
239
+ output_name: str
240
+ threshold: float
241
+ value: float
242
+ satisfied: bool
243
+ is_force_condition: bool
244
+
245
+
246
+ @dataclass
247
+ class StoppingConditionStatusResult:
248
+ """
249
+ Status of all stopping conditions for a simulation.
250
+
251
+ This represents the evaluation of stopping conditions against the final
252
+ simulation results.
253
+
254
+ Attributes
255
+ ----------
256
+ overall_success : bool
257
+ Whether the overall stopping criteria were met (excluding force-stop conditions).
258
+ force_stopped : bool
259
+ Whether a force-stop condition was triggered.
260
+ condition_results : list[StoppingConditionResult]
261
+ Results for each individual stopping condition.
262
+ """
263
+
264
+ overall_success: bool
265
+ force_stopped: bool
266
+ condition_results: list[StoppingConditionResult]
267
+
268
+ @classmethod
269
+ def _from_proto(
270
+ cls, proto: simulationpb.GetStoppingConditionStatusResponse
271
+ ) -> "StoppingConditionStatusResult":
272
+ condition_results = []
273
+ for i, condition in enumerate(proto.conditions):
274
+ # Get the value from the status with bounds checking
275
+ value = (
276
+ proto.status.value[i].values[0].value
277
+ if i < len(proto.status.value) and proto.status.value[i].values
278
+ else float("nan")
279
+ )
280
+
281
+ # Get the threshold, checking for field presence (not truthiness)
282
+ threshold = condition.threshold.value if condition.HasField("threshold") else 0.0
283
+
284
+ # Check if this is a force condition
285
+ is_force = condition.op == outputpb.STOP_COND_OP_FORCE
286
+
287
+ # Get output name with field presence check
288
+ output_name = condition.output.name if condition.HasField("output") else ""
289
+
290
+ # Get satisfied status with bounds checking
291
+ satisfied = (
292
+ proto.status.cond_success[i] if i < len(proto.status.cond_success) else False
293
+ )
294
+
295
+ condition_results.append(
296
+ StoppingConditionResult(
297
+ output_name=output_name,
298
+ threshold=threshold,
299
+ value=value,
300
+ satisfied=satisfied,
301
+ is_force_condition=is_force,
302
+ )
303
+ )
304
+
305
+ return cls(
306
+ overall_success=proto.status.success,
307
+ force_stopped=proto.status.force_stop,
308
+ condition_results=condition_results,
309
+ )