luminarycloud 0.15.4__py3-none-any.whl → 0.16.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 (58) hide show
  1. luminarycloud/_helpers/__init__.py +1 -0
  2. luminarycloud/_helpers/_code_representation.py +18 -3
  3. luminarycloud/_helpers/_create_geometry.py +36 -17
  4. luminarycloud/_helpers/download.py +67 -1
  5. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +1 -2
  6. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +9 -9
  7. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +7 -4
  8. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +45 -21
  9. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +65 -0
  10. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.py +34 -0
  11. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.pyi +12 -0
  12. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.py +191 -82
  13. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.pyi +327 -74
  14. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.py +140 -65
  15. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.pyi +74 -38
  16. luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2.py +6 -2
  17. luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.py +71 -0
  18. luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.pyi +32 -0
  19. luminarycloud/_proto/assistant/assistant_pb2.py +76 -75
  20. luminarycloud/_proto/assistant/assistant_pb2.pyi +21 -18
  21. luminarycloud/_proto/assistant/assistant_pb2_grpc.py +14 -14
  22. luminarycloud/_proto/assistant/assistant_pb2_grpc.pyi +8 -8
  23. luminarycloud/_proto/base/base_pb2.py +7 -6
  24. luminarycloud/_proto/base/base_pb2.pyi +4 -0
  25. luminarycloud/_proto/client/simulation_pb2.py +188 -186
  26. luminarycloud/_proto/client/simulation_pb2.pyi +10 -2
  27. luminarycloud/_proto/geometry/geometry_pb2.py +63 -64
  28. luminarycloud/_proto/geometry/geometry_pb2.pyi +5 -1
  29. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +11 -11
  30. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +12 -4
  31. luminarycloud/_proto/quantity/quantity_pb2.py +11 -2
  32. luminarycloud/_proto/quantity/quantity_pb2.pyi +6 -0
  33. luminarycloud/_proto/table/table_pb2.pyi +4 -2
  34. luminarycloud/_proto/upload/upload_pb2.py +47 -7
  35. luminarycloud/_proto/upload/upload_pb2.pyi +62 -0
  36. luminarycloud/enum/quantity_type.py +15 -0
  37. luminarycloud/mesh.py +8 -1
  38. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/__init__.py +1 -0
  39. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_auto_.py +30 -0
  40. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_on_.py +1 -1
  41. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation_.py +7 -3
  42. luminarycloud/params/simulation/physics/fluid/solution_controls_fluid_.py +4 -0
  43. luminarycloud/params/simulation/physics/fluid/turbulence_.py +1 -1
  44. luminarycloud/params/simulation/physics/fluid_.py +1 -1
  45. luminarycloud/params/simulation/physics/heat/solution_controls/heat_relaxation_method/heat_implicit_relaxation_.py +1 -1
  46. luminarycloud/params/simulation/physics/heat/solution_controls_heat_.py +1 -1
  47. luminarycloud/physics_ai/__init__.py +4 -0
  48. luminarycloud/physics_ai/inference.py +140 -4
  49. luminarycloud/physics_ai/solution.py +60 -0
  50. luminarycloud/tables.py +6 -8
  51. luminarycloud/vis/data_extraction.py +4 -5
  52. luminarycloud/vis/display.py +26 -11
  53. luminarycloud/vis/filters.py +116 -68
  54. luminarycloud/vis/primitives.py +3 -2
  55. luminarycloud/vis/visualization.py +197 -40
  56. {luminarycloud-0.15.4.dist-info → luminarycloud-0.16.0.dist-info}/METADATA +1 -1
  57. {luminarycloud-0.15.4.dist-info → luminarycloud-0.16.0.dist-info}/RECORD +58 -56
  58. {luminarycloud-0.15.4.dist-info → luminarycloud-0.16.0.dist-info}/WHEEL +0 -0
@@ -15,9 +15,10 @@ from .display import Field, DisplayAttributes
15
15
  from typing import List, Any, cast
16
16
  from .primitives import Box, Plane
17
17
  from .vis_util import generate_id
18
+ from .._helpers._code_representation import CodeRepr
18
19
 
19
20
 
20
- class Filter(ABC):
21
+ class Filter(ABC, CodeRepr):
21
22
  """
22
23
  This is the base class for all filters. Each derived filter class
23
24
  is responsible for providing a _to_proto method to convert to a filter
@@ -95,7 +96,7 @@ class Slice(Filter):
95
96
  Specifies this filter's appearance.
96
97
  """
97
98
 
98
- def __init__(self, name: str) -> None:
99
+ def __init__(self, name: str = "") -> None:
99
100
  super().__init__(generate_id("slice-"))
100
101
  self._plane = Plane()
101
102
  self._project_vectors: bool = False
@@ -166,7 +167,7 @@ class Isosurface(Filter):
166
167
  Specifies this filter's appearance.
167
168
  """
168
169
 
169
- def __init__(self, name: str) -> None:
170
+ def __init__(self, name: str = "") -> None:
170
171
  super().__init__(generate_id("contour-"))
171
172
  self.isovalues: List[float] = []
172
173
  self.field: Field = Field()
@@ -188,7 +189,10 @@ class Isosurface(Filter):
188
189
  vis_filter.id = self.id
189
190
  vis_filter.name = self.name
190
191
  vis_filter.contour.field.quantity_typ = self.field.quantity.value
191
- vis_filter.contour.field.component = self.field.component.value
192
+ if not quantity_type._is_vector(self.field.quantity):
193
+ vis_filter.contour.field.component = vis_pb2.Field.COMPONENT_UNSPECIFIED
194
+ else:
195
+ vis_filter.contour.field.component = self.field.component.value
192
196
  for val in self.isovalues:
193
197
  vis_filter.contour.iso_values.append(val)
194
198
  return vis_filter
@@ -200,7 +204,11 @@ class Isosurface(Filter):
200
204
  self.id = filter.id
201
205
  self.name = filter.name
202
206
  self.field.quantity = VisQuantity(filter.contour.field.quantity_typ)
203
- self.field.component = FieldComponent(filter.contour.field.component)
207
+ if not quantity_type._is_vector(self.field.quantity):
208
+ # This is a scalar so just set the component to magnitude which is the default.
209
+ self.field.component = FieldComponent.MAGNITUDE
210
+ else:
211
+ self.field.component = FieldComponent(filter.contour.field.component)
204
212
  self.isovalues.clear()
205
213
  for val in filter.contour.iso_values:
206
214
  self.isovalues.append(val)
@@ -226,11 +234,9 @@ class PlaneClip(Filter):
226
234
  are removed. Default: False
227
235
  """
228
236
 
229
- def __init__(self, name: str) -> None:
237
+ def __init__(self, name: str = "") -> None:
230
238
  super().__init__(generate_id("planeClip-"))
231
239
  self._plane: Plane = Plane()
232
- # TODO(matt): We could make this a prop to that is unsettable. Or we could
233
- # not use ids and force that the filter names are unique.
234
240
  self.name = name
235
241
  self.inverted: bool = False
236
242
 
@@ -289,7 +295,7 @@ class BoxClip(Filter):
289
295
  Default : False
290
296
  """
291
297
 
292
- def __init__(self, name: str) -> None:
298
+ def __init__(self, name: str = "") -> None:
293
299
  super().__init__(generate_id("boxClip-"))
294
300
  self._box: Box = Box()
295
301
  self.name = name
@@ -351,17 +357,11 @@ class VectorGlyphs(Filter):
351
357
 
352
358
  """
353
359
 
354
- def __init__(self, name: str) -> None:
360
+ def __init__(self, name: str = "") -> None:
355
361
  super().__init__(generate_id("vector-"))
356
362
  self.name: str = name
357
- # TODO(matt): we should be able to help set some reasonable defaults bases
358
- # on the mesh size (i.e., number of points) and bounds (the default glyph size).
359
- # The scene has accesss to this theoretically. Perhaps the scene class can be used
360
- # as a factory.
361
363
  self._sampling_rate: int = 500
362
- self.field: Field = Field()
363
- # TODO(matt): we should only allow vectors somehow
364
- self.field.quantity = VisQuantity.VELOCITY
364
+ self.quantity: VisQuantity = VisQuantity.VELOCITY
365
365
 
366
366
  @property
367
367
  def sampling_rate(self) -> int:
@@ -396,11 +396,11 @@ class FixedSizeVectorGlyphs(VectorGlyphs):
396
396
  The size in world units (meters) of the glyphs.
397
397
  display_attrs (DisplayAttributes)
398
398
  Specifies this filters appearance.
399
- field: Field
399
+ quantity: VisQuantity
400
400
  The vector field to use for glyph generation. Default: Velocity
401
401
  """
402
402
 
403
- def __init__(self, name: str) -> None:
403
+ def __init__(self, name: str = "") -> None:
404
404
  super().__init__(name)
405
405
  self.size: float = 1.0
406
406
 
@@ -411,10 +411,10 @@ class FixedSizeVectorGlyphs(VectorGlyphs):
411
411
  vis_filter.glyph.fixed_size_glyphs = self.size
412
412
  vis_filter.glyph.n_glyphs = self.sampling_rate
413
413
  vis_filter.glyph.sampling_mode = vis_pb2.GLYPH_SAMPLING_MODE_EVERY_NTH
414
- if not quantity_type._is_vector(self.field.quantity):
414
+ if not quantity_type._is_vector(self.quantity):
415
415
  raise ValueError("FixedSizeVectorGlyphs: field must be a vector type")
416
- vis_filter.glyph.field.quantity_typ = self.field.quantity.value
417
- vis_filter.glyph.field.component = self.field.component.value
416
+ vis_filter.glyph.field.quantity_typ = self.quantity.value
417
+ vis_filter.glyph.field.component = vis_pb2.Field.COMPONENT_UNSPECIFIED
418
418
  return vis_filter
419
419
 
420
420
  def _from_proto(self, filter: vis_pb2.Filter) -> None:
@@ -429,8 +429,7 @@ class FixedSizeVectorGlyphs(VectorGlyphs):
429
429
  self.n_glyphs = filter.glyph.n_glyphs
430
430
  self.size = filter.glyph.fixed_size_glyphs
431
431
  self.sampling_rate = filter.glyph.n_glyphs
432
- self.field.quantity = VisQuantity(filter.glyph.field.quantity_typ)
433
- self.field.component = FieldComponent(filter.glyph.field.component)
432
+ self.quantity = VisQuantity(filter.glyph.field.quantity_typ)
434
433
 
435
434
 
436
435
  class ScaledVectorGlyphs(VectorGlyphs):
@@ -457,11 +456,11 @@ class ScaledVectorGlyphs(VectorGlyphs):
457
456
  0.5 and the scale is 2 then the resulting world space size is 1 meter. Default: 1.
458
457
  display_attrs (DisplayAttributes)
459
458
  Specifies this filters appearance.
460
- field: Field
459
+ quantity: VisQuantity
461
460
  The vector field to use for glyph generation. Default: Velocity
462
461
  """
463
462
 
464
- def __init__(self, name: str) -> None:
463
+ def __init__(self, name: str = "") -> None:
465
464
  super().__init__(name)
466
465
  self.scale: float = 1.0
467
466
 
@@ -472,10 +471,10 @@ class ScaledVectorGlyphs(VectorGlyphs):
472
471
  vis_filter.glyph.glyph_scale_size = self.scale
473
472
  vis_filter.glyph.n_glyphs = self.sampling_rate
474
473
  vis_filter.glyph.sampling_mode = vis_pb2.GLYPH_SAMPLING_MODE_EVERY_NTH
475
- if not quantity_type._is_vector(self.field.quantity):
474
+ if not quantity_type._is_vector(self.quantity):
476
475
  raise ValueError("ScaledVectorGyph: field must be a vector type")
477
- vis_filter.glyph.field.quantity_typ = self.field.quantity.value
478
- vis_filter.glyph.field.component = self.field.component.value
476
+ vis_filter.glyph.field.quantity_typ = self.quantity.value
477
+ vis_filter.glyph.field.component = vis_pb2.Field.COMPONENT_UNSPECIFIED
479
478
  return vis_filter
480
479
 
481
480
  def _from_proto(self, filter: vis_pb2.Filter) -> None:
@@ -490,8 +489,7 @@ class ScaledVectorGlyphs(VectorGlyphs):
490
489
  self.n_glyphs = filter.glyph.n_glyphs
491
490
  self.scale = filter.glyph.glyph_scale_size
492
491
  self.sampling_rate = filter.glyph.n_glyphs
493
- self.field.quantity = VisQuantity(filter.glyph.field.quantity_typ)
494
- self.field.component = FieldComponent(filter.glyph.field.component)
492
+ self.quantity = VisQuantity(filter.glyph.field.quantity_typ)
495
493
 
496
494
 
497
495
  class Threshold(Filter):
@@ -526,7 +524,7 @@ class Threshold(Filter):
526
524
  Specifies this filter's appearance.
527
525
  """
528
526
 
529
- def __init__(self, name: str) -> None:
527
+ def __init__(self, name: str = "") -> None:
530
528
  super().__init__(generate_id("threshold-"))
531
529
  self.field: Field = Field()
532
530
  self.min_value: float = 0.0
@@ -559,6 +557,10 @@ class Threshold(Filter):
559
557
  vis_filter.threshold.range.min = self.min_value
560
558
  vis_filter.threshold.range.max = self.max_value
561
559
  vis_filter.threshold.field.quantity_typ = self.field.quantity.value
560
+ if not quantity_type._is_vector(self.field.quantity):
561
+ vis_filter.threshold.field.component = vis_pb2.Field.COMPONENT_UNSPECIFIED
562
+ else:
563
+ vis_filter.threshold.field.component = self.field.component.value
562
564
  vis_filter.threshold.field.component = self.field.component.value
563
565
  vis_filter.threshold.smooth = self.smooth
564
566
  vis_filter.threshold.invert = self.invert
@@ -577,7 +579,11 @@ class Threshold(Filter):
577
579
  self.invert = filter.threshold.invert
578
580
  self.strict = filter.threshold.strict
579
581
  self.field.quantity = VisQuantity(filter.threshold.field.quantity_typ)
580
- self.field.component = FieldComponent(filter.threshold.field.component)
582
+ if not quantity_type._is_vector(self.field.quantity):
583
+ # This is a scalar so just set the component to magnitude which is the default.
584
+ self.field.component = FieldComponent.MAGNITUDE
585
+ else:
586
+ self.field.component = FieldComponent(filter.threshold.field.component)
581
587
 
582
588
 
583
589
  class Streamlines(Filter):
@@ -595,14 +601,13 @@ class Streamlines(Filter):
595
601
  Specifies this filter's appearance.
596
602
  """
597
603
 
598
- def __init__(self, name: str) -> None:
604
+ def __init__(self, name: str = "") -> None:
599
605
  super().__init__(generate_id("streamlines-"))
600
606
  self.name: str = name
601
607
  self.n_streamlines: int = 100
602
608
  self.max_length: float = 10
603
609
  self.direction: StreamlineDirection = StreamlineDirection.FORWARD
604
- self.field: Field = Field()
605
- self.field.quantity = VisQuantity.VELOCITY
610
+ self.quantity: VisQuantity = VisQuantity.VELOCITY
606
611
 
607
612
 
608
613
  class RakeStreamlines(Streamlines):
@@ -625,7 +630,7 @@ class RakeStreamlines(Streamlines):
625
630
  The number of seed particles to place on the rake. Default: 100
626
631
  max_length: float
627
632
  The maximum path length of the particle in meters. Default: 10
628
- field: Field
633
+ quantity: VisQuantity
629
634
  The vector field to used for the particle advection. Default: Velocity
630
635
  start: Vector3Like
631
636
  The start point of the rake. Default: [0,0,0].
@@ -637,10 +642,10 @@ class RakeStreamlines(Streamlines):
637
642
  Specifies this filter's appearance.
638
643
  """
639
644
 
640
- def __init__(self, name: str) -> None:
645
+ def __init__(self, name: str = "") -> None:
641
646
  super().__init__(name)
642
- self.start: Vector3Like = dc.field(default_factory=lambda: Vector3(x=0, y=0, z=0))
643
- self.end: Vector3Like = dc.field(default_factory=lambda: Vector3(x=1, y=0, z=0))
647
+ self.start: Vector3Like = Vector3(x=0, y=0, z=0)
648
+ self.end: Vector3Like = Vector3(x=1, y=0, z=0)
644
649
 
645
650
  def _to_proto(self) -> vis_pb2.Filter:
646
651
  # Type checking
@@ -648,8 +653,8 @@ class RakeStreamlines(Streamlines):
648
653
  raise TypeError(f"Expected 'int', got {type(self.n_streamlines).__name__}")
649
654
  if not isinstance(self.max_length, (float, int)):
650
655
  raise TypeError(f"Expected 'float or int', got {type(self.max_length).__name__}")
651
- if not isinstance(self.field, Field):
652
- raise TypeError(f"Expected 'Field', got {type(self.field).__name__}")
656
+ if not isinstance(self.quantity, VisQuantity):
657
+ raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
653
658
 
654
659
  vis_filter = vis_pb2.Filter()
655
660
  vis_filter.id = self.id
@@ -658,9 +663,9 @@ class RakeStreamlines(Streamlines):
658
663
  vis_filter.streamlines.max_length = self.max_length
659
664
  vis_filter.streamlines.rake.start.CopyFrom(_to_vector3(self.start)._to_proto())
660
665
  vis_filter.streamlines.rake.end.CopyFrom(_to_vector3(self.end)._to_proto())
661
- if not quantity_type._is_vector(self.field.quantity):
666
+ if not quantity_type._is_vector(self.quantity):
662
667
  raise ValueError("RakeStreamlines: field must be a vector type")
663
- vis_filter.streamlines.field.quantity_typ = self.field.quantity.value
668
+ vis_filter.streamlines.field.quantity_typ = self.quantity.value
664
669
  return vis_filter
665
670
 
666
671
  def _from_proto(self, filter: vis_pb2.Filter) -> None:
@@ -679,7 +684,7 @@ class RakeStreamlines(Streamlines):
679
684
  self.start._from_proto(filter.streamlines.rake.start)
680
685
  self.end = Vector3()
681
686
  self.end._from_proto(filter.streamlines.rake.end)
682
- self.field.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
687
+ self.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
683
688
 
684
689
 
685
690
  class SurfaceStreamlines(Streamlines):
@@ -726,7 +731,7 @@ class SurfaceStreamlines(Streamlines):
726
731
  particles further into the volume based on the surface normal. Default: 0.0
727
732
  max_length: float
728
733
  The maximum path length of the particle in meters. Default: 10
729
- field: Field
734
+ quantity: VisQuantity
730
735
  The vector field to used for the particle advection. Default: Velocity
731
736
  name : str
732
737
  A user provided name for the filter.
@@ -734,7 +739,7 @@ class SurfaceStreamlines(Streamlines):
734
739
  Specifies this filter's appearance.
735
740
  """
736
741
 
737
- def __init__(self, name: str) -> None:
742
+ def __init__(self, name: str = "") -> None:
738
743
  super().__init__(name)
739
744
  self.offset: float = 0.0
740
745
  self._surface_names: List[str] = []
@@ -768,8 +773,8 @@ class SurfaceStreamlines(Streamlines):
768
773
  raise TypeError(f"Expected 'float or int', got {type(self.max_length).__name__}")
769
774
  if not isinstance(self.offset, (float, int)):
770
775
  raise TypeError(f"Expected 'float or int', got {type(self.offset).__name__}")
771
- if not isinstance(self.field, Field):
772
- raise TypeError(f"Expected 'Field', got {type(self.field).__name__}")
776
+ if not isinstance(self.quantity, VisQuantity):
777
+ raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
773
778
  if not isinstance(self.mode, SurfaceStreamlineMode):
774
779
  raise TypeError(f"Expected 'SurfaceStreamlinesMode', got {type(self.mode).__name__}")
775
780
 
@@ -781,12 +786,12 @@ class SurfaceStreamlines(Streamlines):
781
786
  project = False
782
787
  # Prevent common mistakes that cause confusion.
783
788
  if self.mode == SurfaceStreamlineMode.ADVECT_ON_SURFACE:
784
- if self.field.quantity == VisQuantity.VELOCITY:
789
+ if self.quantity == VisQuantity.VELOCITY:
785
790
  raise ValueError(
786
791
  "SurfacesStreamines: velocity is 0 on surfaces and will produce no data"
787
792
  )
788
793
  project = True
789
- elif self.field.quantity == VisQuantity.WALL_SHEAR_STRESS:
794
+ elif self.quantity == VisQuantity.WALL_SHEAR_STRESS:
790
795
  raise ValueError(
791
796
  "SurfacesStreamines: wall shear stress is 0 in the volume and will produce no data "
792
797
  )
@@ -796,10 +801,9 @@ class SurfaceStreamlines(Streamlines):
796
801
  raise ValueError("SurfaceStreamlines: need at least one surfaces specified.")
797
802
  for id in self._surface_names:
798
803
  vis_filter.streamlines.surface.surface_names.append(id)
799
- if not quantity_type._is_vector(self.field.quantity):
800
- raise ValueError("SurfaceStreamlines: field must be a vector type")
801
- vis_filter.streamlines.field.component = self.field.component.value
802
- vis_filter.streamlines.field.quantity_typ = self.field.quantity.value
804
+ if not quantity_type._is_vector(self.quantity):
805
+ raise ValueError("SurfaceStreamlines: quantity must be a vector type")
806
+ vis_filter.streamlines.field.quantity_typ = self.quantity.value
803
807
  return vis_filter
804
808
 
805
809
  def _from_proto(self, filter: vis_pb2.Filter) -> None:
@@ -816,7 +820,15 @@ class SurfaceStreamlines(Streamlines):
816
820
  self._surface_names.clear()
817
821
  for s in filter.streamlines.surface.surface_names:
818
822
  self._surface_names.append(s)
819
- self.field.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
823
+ self.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
824
+
825
+ def _to_code(self, hide_defaults: bool = True, use_tmp_objs: bool = True) -> str:
826
+ code = super()._to_code(hide_defaults=hide_defaults)
827
+ # We need to explicity write the code for the surfaces since its
828
+ # technically a private variable.
829
+ for s in self._surface_names:
830
+ code += f".add_surface('{s}')\n"
831
+ return code
820
832
 
821
833
 
822
834
  class SurfaceLIC(Filter):
@@ -840,7 +852,7 @@ class SurfaceLIC(Filter):
840
852
 
841
853
  Attributes:
842
854
  -----------
843
- field: Field
855
+ quantity: VisQuantity
844
856
  Specifies the field used to advect particles for the surface LIC.
845
857
  Default: WALL_SHEER_STRESS
846
858
  contrast: float
@@ -849,13 +861,12 @@ class SurfaceLIC(Filter):
849
861
  higher values mean more contrast. Default: 1
850
862
  """
851
863
 
852
- def __init__(self, name: str) -> None:
864
+ def __init__(self, name: str = "") -> None:
853
865
  super().__init__(generate_id("surface-lic-"))
854
866
  self.name = name
855
867
  self.contrast: float = 1.0
856
868
  self._surface_names: List[str] = []
857
- self.field: Field = Field()
858
- self.field.quantity = VisQuantity.WALL_SHEAR_STRESS
869
+ self.quantity: VisQuantity = VisQuantity.WALL_SHEAR_STRESS
859
870
 
860
871
  def add_surface(self, id: str) -> None:
861
872
  """
@@ -883,9 +894,9 @@ class SurfaceLIC(Filter):
883
894
  vis_filter.name = self.name
884
895
  # Prevent common mistakes that cause confusion. The only current option
885
896
  # is to be on a surface, so no velocity.
886
- if not isinstance(self.field, Field):
887
- raise TypeError(f"Expected 'Field', got {type(self.field).__name__}")
888
- if self.field.quantity == VisQuantity.VELOCITY:
897
+ if not isinstance(self.quantity, VisQuantity):
898
+ raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
899
+ if self.quantity == VisQuantity.VELOCITY:
889
900
  raise ValueError("SurfaceLIC: velocity is 0 on surfaces and will produce no data")
890
901
  if not isinstance(self.contrast, (int, float)):
891
902
  raise TypeError(f"Expected 'int or float', got {type(self.contrast).__name__}")
@@ -899,7 +910,7 @@ class SurfaceLIC(Filter):
899
910
  geometry = vis_pb2.SurfaceLICGeomtery()
900
911
  vis_filter.surface_lic.geometry.CopyFrom(geometry)
901
912
 
902
- vis_filter.surface_lic.field.quantity_typ = self.field.quantity.value
913
+ vis_filter.surface_lic.field.quantity_typ = self.quantity.value
903
914
  vis_filter.surface_lic.contrast = self.contrast
904
915
  return vis_filter
905
916
 
@@ -917,7 +928,44 @@ class SurfaceLIC(Filter):
917
928
  self._surface_names = []
918
929
  for s in filter.surface_lic.geometry.surface_names:
919
930
  self._surface_names.append(s)
920
- if not quantity_type._is_vector(self.field.quantity):
921
- raise ValueError("SurfaceLIC: field must be a vector type")
922
- self.field.quantity = VisQuantity(filter.surface_lic.field.quantity_typ)
923
- self.field.component = FieldComponent(filter.surface_lic.field.component)
931
+ if not quantity_type._is_vector(self.quantity):
932
+ raise ValueError("SurfaceLIC: quantity must be a vector type")
933
+ self.quantity = VisQuantity(filter.surface_lic.field.quantity_typ)
934
+
935
+ def _to_code(self, hide_defaults: bool = True, use_tmp_objs: bool = True) -> str:
936
+ code = super()._to_code(hide_defaults=hide_defaults)
937
+ # We need to explicity write the code for the surfaces since its
938
+ # technically a private variable.
939
+ for s in self._surface_names:
940
+ code += f".add_surface('{s}')\n"
941
+ return code
942
+
943
+
944
+ def _filter_to_obj_name(filter: Filter) -> str:
945
+ """
946
+ Helper function to convert a filter to a code object name used in code gen.
947
+ """
948
+ if not isinstance(filter, Filter):
949
+ raise TypeError(f"Expected 'Filter', got {type(filter).__name__}")
950
+ if isinstance(filter, Slice):
951
+ return "slice"
952
+ elif isinstance(filter, Isosurface):
953
+ return "isosurface"
954
+ elif isinstance(filter, PlaneClip):
955
+ return "plane_clip"
956
+ elif isinstance(filter, BoxClip):
957
+ return "box_clip"
958
+ elif isinstance(filter, FixedSizeVectorGlyphs):
959
+ return "fixed_size_vector_glyphs"
960
+ elif isinstance(filter, ScaledVectorGlyphs):
961
+ return "scaled_vector_glyphs"
962
+ elif isinstance(filter, Threshold):
963
+ return "threshold"
964
+ elif isinstance(filter, RakeStreamlines):
965
+ return "rake_streamlines"
966
+ elif isinstance(filter, SurfaceStreamlines):
967
+ return "surface_streamlines"
968
+ elif isinstance(filter, SurfaceLIC):
969
+ return "surface_lic"
970
+ else:
971
+ raise TypeError(f"Unknown filter type: {type(filter).__name__}")
@@ -1,10 +1,11 @@
1
1
  # Copyright 2023-2024 Luminary Cloud, Inc. All Rights Reserved.
2
2
  import dataclasses as dc
3
3
  from luminarycloud.types import Vector3, Vector3Like
4
+ from .._helpers._code_representation import CodeRepr
4
5
 
5
6
 
6
7
  @dc.dataclass
7
- class Plane:
8
+ class Plane(CodeRepr):
8
9
  """
9
10
  This class defines a plane.
10
11
 
@@ -19,7 +20,7 @@ class Plane:
19
20
 
20
21
 
21
22
  @dc.dataclass
22
- class Box:
23
+ class Box(CodeRepr):
23
24
  """
24
25
  This class defines a box used for filter such as box clip.
25
26