luminarycloud 0.16.0__py3-none-any.whl → 0.16.1__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 (40) hide show
  1. luminarycloud/_client/client.py +5 -0
  2. luminarycloud/_helpers/_code_representation.py +5 -3
  3. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.py +7 -7
  4. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.pyi +4 -4
  5. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +128 -107
  6. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +48 -3
  7. luminarycloud/_proto/client/simulation_pb2.py +348 -350
  8. luminarycloud/_proto/client/simulation_pb2.pyi +95 -95
  9. luminarycloud/_proto/geometry/geometry_pb2.py +68 -68
  10. luminarycloud/_proto/geometry/geometry_pb2.pyi +10 -6
  11. luminarycloud/_proto/hexmesh/hexmesh_pb2.py +40 -15
  12. luminarycloud/_proto/hexmesh/hexmesh_pb2.pyi +58 -1
  13. luminarycloud/_proto/lcstatus/codes_pb2.py +3 -2
  14. luminarycloud/_proto/lcstatus/codes_pb2.pyi +4 -0
  15. luminarycloud/enum/quantity_type.py +4 -0
  16. luminarycloud/enum/tables.py +1 -0
  17. luminarycloud/enum/vis_enums.py +20 -0
  18. luminarycloud/feature_modification.py +6 -7
  19. luminarycloud/geometry.py +24 -0
  20. luminarycloud/geometry_version.py +23 -0
  21. luminarycloud/params/simulation/adjoint_.py +4 -4
  22. luminarycloud/params/simulation/material/material_fluid_.py +1 -1
  23. luminarycloud/params/simulation/material/material_solid_.py +1 -1
  24. luminarycloud/params/simulation/output_.py +1 -1
  25. luminarycloud/params/simulation/physics/fluid/initialization/fluid_existing_solution_.py +28 -0
  26. luminarycloud/params/simulation/simulation_param_.py +6 -0
  27. luminarycloud/project.py +9 -7
  28. luminarycloud/simulation_param.py +29 -15
  29. luminarycloud/simulation_template.py +14 -10
  30. luminarycloud/tables.py +5 -4
  31. luminarycloud/thirdparty/__init__.py +12 -0
  32. luminarycloud/thirdparty/onshape.py +170 -0
  33. luminarycloud/vis/__init__.py +2 -0
  34. luminarycloud/vis/data_extraction.py +40 -1
  35. luminarycloud/vis/filters.py +128 -2
  36. luminarycloud/vis/visualization.py +1 -1
  37. luminarycloud/volume_selection.py +2 -2
  38. {luminarycloud-0.16.0.dist-info → luminarycloud-0.16.1.dist-info}/METADATA +6 -6
  39. {luminarycloud-0.16.0.dist-info → luminarycloud-0.16.1.dist-info}/RECORD +40 -38
  40. {luminarycloud-0.16.0.dist-info → luminarycloud-0.16.1.dist-info}/WHEEL +0 -0
@@ -6,7 +6,6 @@ from os import PathLike
6
6
  from pprint import pformat
7
7
  from typing import Optional, TypeVar, cast
8
8
 
9
- import luminarycloud.params.enum._enum_wrappers as enum
10
9
  from luminarycloud._helpers._simulation_params_from_json import (
11
10
  simulation_params_from_json_path,
12
11
  simulation_params_from_json_dict,
@@ -38,7 +37,7 @@ from luminarycloud.params.simulation.volume_entity_ import (
38
37
  from luminarycloud.params.simulation import (
39
38
  SimulationParam as _SimulationParam,
40
39
  )
41
- from luminarycloud.reference_values import ReferenceValues, ReferenceValuesType
40
+ from luminarycloud.reference_values import ReferenceValues
42
41
  from luminarycloud.types import Vector3Like
43
42
  from luminarycloud.types.vector3 import _to_vector3_ad_proto
44
43
 
@@ -152,7 +151,10 @@ class SimulationParam(_SimulationParam):
152
151
  _list=volume_material_pairs,
153
152
  _accessor=lambda v: get_id(v.volume_identifier),
154
153
  _to_remove=volume_identifier.id,
155
- _warning_message=lambda v: f"Volume {_stringify_identifier(volume_identifier)} has already been assigned material {_stringify_identifier(v.material_identifier)}. Overwriting...",
154
+ _warning_message=lambda v: (
155
+ f"Volume {_stringify_identifier(volume_identifier)} has already been assigned "
156
+ f"material {_stringify_identifier(v.material_identifier)}. Overwriting..."
157
+ ),
156
158
  )
157
159
 
158
160
  if volume_identifier.id not in (get_id(v.volume_identifier) for v in self.volume_entity):
@@ -226,13 +228,19 @@ class SimulationParam(_SimulationParam):
226
228
  _list=volume_physics_pairs,
227
229
  _accessor=lambda v: get_id(v.volume_identifier),
228
230
  _to_remove=volume_identifier.id,
229
- _warning_message=lambda v: f"Volume {_stringify_identifier(volume_identifier)} has already been assigned physics {_stringify_identifier(v.physics_identifier)}. Overwriting...",
231
+ _warning_message=lambda v: (
232
+ f"Volume {_stringify_identifier(volume_identifier)} has already been assigned "
233
+ f"physics {_stringify_identifier(v.physics_identifier)}. Overwriting..."
234
+ ),
230
235
  )
231
236
  _remove_from_list_with_warning(
232
237
  _list=volume_physics_pairs,
233
238
  _accessor=lambda v: get_id(v.physics_identifier),
234
239
  _to_remove=get_id(physics.physics_identifier),
235
- _warning_message=lambda v: f"Physics {_stringify_identifier(physics.physics_identifier)} has already been assigned to volume {_stringify_identifier(v.volume_identifier)}. Overwriting...",
240
+ _warning_message=lambda v: (
241
+ f"Physics {_stringify_identifier(physics.physics_identifier)} has already been "
242
+ f"assigned to volume {_stringify_identifier(v.volume_identifier)}. Overwriting..."
243
+ ),
236
244
  )
237
245
 
238
246
  if volume_identifier.id not in (get_id(v.volume_identifier) for v in self.volume_entity):
@@ -268,25 +276,25 @@ class SimulationParam(_SimulationParam):
268
276
  .. warning:: This feature is experimental and may change or be removed without notice.
269
277
  """
270
278
  self.adjoint = self.adjoint or Adjoint()
271
- self.adjoint.output = outputpb.Output()
272
- self.adjoint.output.quantity = quantity_type.value
273
- self.adjoint.output.in_surfaces.extend(surface_ids)
274
- self.adjoint.output.frame_id = frame_id
279
+ self.adjoint._output = outputpb.Output()
280
+ self.adjoint._output.quantity = quantity_type.value
281
+ self.adjoint._output.in_surfaces.extend(surface_ids)
282
+ self.adjoint._output.frame_id = frame_id
275
283
  if QuantityType._is_average(quantity_type):
276
284
  if averaging_type == AveragingType.UNSPECIFIED:
277
- self.adjoint.output.surface_average_properties.averaging_type = (
285
+ self.adjoint._output.surface_average_properties.averaging_type = (
278
286
  SpaceAveragingType.NO_AVERAGING.value
279
287
  )
280
288
  elif averaging_type == AveragingType.AREA:
281
- self.adjoint.output.surface_average_properties.averaging_type = (
289
+ self.adjoint._output.surface_average_properties.averaging_type = (
282
290
  SpaceAveragingType.AREA.value
283
291
  )
284
292
  elif averaging_type == AveragingType.MASS_FLOW:
285
- self.adjoint.output.surface_average_properties.averaging_type = (
293
+ self.adjoint._output.surface_average_properties.averaging_type = (
286
294
  SpaceAveragingType.MASS_FLOW.value
287
295
  )
288
296
  elif QuantityType._is_force(quantity_type):
289
- self.adjoint.output.force_properties.CopyFrom(
297
+ self.adjoint._output.force_properties.CopyFrom(
290
298
  outputpb.ForceProperties(
291
299
  force_dir_type=(
292
300
  outputpb.FORCE_DIRECTION_BODY_ORIENTATION_AND_FLOW_DIR
@@ -332,9 +340,15 @@ from luminarycloud import outputs, SimulationParam, EntityIdentifier
332
340
 
333
341
 
334
342
  """
335
- return code + self._to_code_helper("obj", hide_defaults, use_tmp_objs).replace(
336
- "luminarycloud.simulation_param.SimulationParam()", "SimulationParam()", 1
343
+ code += self._to_code_helper("obj", hide_defaults, use_tmp_objs).replace(
344
+ "luminarycloud.simulation_param.SimulationParam()", "luminarycloud.SimulationParam()", 1
337
345
  )
346
+ if self.adjoint.primal_simulation_id != "":
347
+ code += """
348
+ # TODO(USER): Select appropriate parameters to configure the adjoint output.
349
+ obj.configure_adjoint_output("...")
350
+ """
351
+ return code
338
352
 
339
353
  def find_parameter(self, parameter: str) -> None:
340
354
  """
@@ -119,14 +119,6 @@ class SimulationTemplate(ProtoWrapperBase):
119
119
  param_proto.CopyFrom(parameters)
120
120
  else:
121
121
  param_proto = simulation_params_from_json_path(parameters)
122
-
123
- if isinstance(parameters, (SimulationParam, clientpb.SimulationParam)):
124
- # Table references are manipulated via the simulation template, hence we need to persist
125
- # them when we update the parameters.
126
- param_proto.table_references.clear()
127
- for k, v in self._proto.parameters.table_references.items():
128
- param_proto.table_references[k].CopyFrom(v)
129
-
130
122
  req.parameters.CopyFrom(param_proto)
131
123
 
132
124
  if copy_from is not None:
@@ -482,7 +474,8 @@ class SimulationTemplate(ProtoWrapperBase):
482
474
  By default parameters with default values are omitted, this change be changed with
483
475
  hide_defaults=False.
484
476
  """
485
- param_code = self.get_parameters().to_code(hide_defaults)
477
+ parameters = self.get_parameters()
478
+ param_code = parameters.to_code(hide_defaults)
486
479
  code, param_code = param_code.split("\n\n\n")
487
480
 
488
481
  # Modify the header note included by SimulationParam.to_code.
@@ -490,10 +483,21 @@ class SimulationTemplate(ProtoWrapperBase):
490
483
  code += "\n\n\n"
491
484
  code += '# Create a new simulation template or modify the one that is synced with the UI "Setup" tab.\n'
492
485
  code += "# TODO(USER): Replace ... with project ID, or create a project.\n"
493
- code += 'project = get_project("...")\n'
486
+ code += 'project = luminarycloud.get_project("...")\n'
494
487
  code += f"# template = project.create_simulation_template(name={self.name})\n"
495
488
  code += "template = project.list_simulation_templates()[0] # Setup template\n\n"
496
489
 
490
+ if parameters._table_references:
491
+ code += "# Upload tabular data.\n"
492
+ code += (
493
+ "# TODO(USER): Provide the local file paths (they are not stored by Luminary).\n"
494
+ )
495
+ for name, table in sorted(parameters._table_references.items()):
496
+ table_type = TableType(table.table_type).__repr__().split(": ")[0][1:]
497
+ code += f'project.create_table({table_type}, "path/to/{table.uploaded_filename}", template)\n'
498
+ code += "# This shows the available tables:\n"
499
+ code += "# print(template.list_tables())\n\n"
500
+
497
501
  code += "# Define the simulation parameters.\n"
498
502
  code += param_code
499
503
  code += "\n# Update the simulation template with the parameters.\n"
luminarycloud/tables.py CHANGED
@@ -8,6 +8,7 @@ import csv
8
8
  from typing import Union
9
9
 
10
10
  from .enum import TableType, QuantityType
11
+ from ._helpers import CodeRepr
11
12
  from ._proto.table import table_pb2 as tablepb
12
13
  from ._proto.quantity import quantity_pb2 as quantitypb
13
14
 
@@ -155,12 +156,12 @@ def create_rectilinear_table(
155
156
 
156
157
 
157
158
  @dataclass(kw_only=True)
158
- class RectilinearTable:
159
+ class RectilinearTable(CodeRepr):
159
160
  """Represents an uploaded table."""
160
161
 
161
- id: str
162
- name: str
163
- table_type: TableType
162
+ id: str = ""
163
+ name: str = ""
164
+ table_type: TableType = TableType.INVALID
164
165
 
165
166
 
166
167
  def _param_name_to_table_type(name: str) -> TableType:
@@ -0,0 +1,12 @@
1
+ # Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
2
+ """
3
+ Third-party integration functionality for the Luminary SDK.
4
+
5
+ This package provides modules for interacting with various third-party services.
6
+ Each service has its own submodule with specific functionality.
7
+
8
+ Available integrations:
9
+ - onshape: Onshape CAD platform integration
10
+ """
11
+
12
+ from . import onshape as onshape
@@ -0,0 +1,170 @@
1
+ # Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
2
+ """
3
+ Onshape integration functionality for the Luminary SDK.
4
+
5
+ This module provides functions for interacting with Onshape CAD platform,
6
+ allowing users to check authentication status and fetch variables using
7
+ native Python types. This is in addition to our more integrated uses of
8
+ Onshape within the SDK, such as allowing Onshape URLs in create_geometry.
9
+ """
10
+
11
+ from typing import NamedTuple
12
+ from dataclasses import dataclass
13
+ import re
14
+
15
+ from .._client import get_default_client
16
+ from .._proto.api.v0.luminarycloud.thirdpartyintegration.onshape import onshape_pb2 as onshapepb
17
+
18
+
19
+ @dataclass
20
+ class OnshapeVariable:
21
+ """
22
+ Represents a variable from Onshape according to its variable studio definition.
23
+
24
+ Attributes
25
+ ----------
26
+ type : str
27
+ The type of the variable (e.g., "LENGTH", "ANGLE", "NUMBER", "ANY")
28
+ name : str
29
+ The name of the variable
30
+ description : str
31
+ A description of the variable
32
+ value : str
33
+ The current value of the variable as a string
34
+ expression : str
35
+ The expression defining the variable
36
+ """
37
+
38
+ type: str
39
+ name: str
40
+ description: str
41
+ value: str
42
+ expression: str
43
+
44
+
45
+ class AuthenticationStatus(NamedTuple):
46
+ """
47
+ Authentication status for Onshape.
48
+
49
+ Attributes
50
+ ----------
51
+ is_authenticated : bool
52
+ Whether the use has active authentication with Onshape.
53
+ """
54
+
55
+ is_authenticated: bool
56
+
57
+
58
+ def _parse_onshape_url(url: str) -> onshapepb.OnshapePath:
59
+ """
60
+ Parse an Onshape URL to extract the necessary components and return an OnshapePath.
61
+
62
+ Parameters
63
+ ----------
64
+ url : str
65
+ The Onshape URL to parse
66
+
67
+ Returns
68
+ -------
69
+ OnshapePath
70
+ A protobuf OnshapePath object with all fields populated
71
+
72
+ Raises
73
+ ------
74
+ ValueError
75
+ If the URL format is invalid or cannot be parsed
76
+ """
77
+
78
+ # Expected format: https://{company_prefix}.onshape.com/documents/{document_id}/{w_or_v}/{wv_id}/e/{element_id}
79
+ url_pattern = (
80
+ r"https://([^.\s]+)\.onshape\.com/documents/([^/\s]+)/([wv])/([^/\s]+)/e/([^/\s?#]+)$"
81
+ )
82
+ match = re.match(url_pattern, url)
83
+
84
+ if not match:
85
+ raise ValueError(f"Invalid Onshape URL format: {url}")
86
+
87
+ company_prefix, document_id, w_or_v, wv_id, element_id = match.groups()
88
+
89
+ return onshapepb.OnshapePath(
90
+ company_prefix=company_prefix,
91
+ document_id=document_id,
92
+ w_or_v=w_or_v,
93
+ wv_id=wv_id,
94
+ element_id=element_id,
95
+ )
96
+
97
+
98
+ def get_authentication_status() -> AuthenticationStatus:
99
+ """
100
+ Check whether the current user has valid authentication with Onshape.
101
+
102
+ Returns
103
+ -------
104
+ AuthenticationStatus
105
+ The authentication status containing whether the user is authenticated
106
+
107
+ Examples
108
+ --------
109
+ >>> import luminarycloud.thirdparty.onshape as onshape
110
+ >>> status = onshape.get_authentication_status()
111
+ >>> if status.is_authenticated:
112
+ ... print("User is authenticated with Onshape")
113
+ ... else:
114
+ ... print("User needs to authenticate with Onshape")
115
+ """
116
+ req = onshapepb.GetAuthenticationStatusRequest()
117
+ res = get_default_client().GetAuthenticationStatus(req)
118
+ return AuthenticationStatus(is_authenticated=res.auth_active)
119
+
120
+
121
+ def fetch_variables(onshape_url: str) -> list[OnshapeVariable]:
122
+ """
123
+ Fetch variables from an Onshape document using an Onshape URL.
124
+
125
+ Parameters
126
+ ----------
127
+ onshape_url : str
128
+ The Onshape URL pointing to the document element.
129
+ Expected format: https://{company}.onshape.com/documents/{doc_id}/{w_or_v}/{workspace_or_version_id}/e/{element_id}
130
+
131
+ Returns
132
+ -------
133
+ list[OnshapeVariable]
134
+ A list of variables available in the specified Onshape element
135
+
136
+ Raises
137
+ ------
138
+ ValueError
139
+ If the URL format is invalid or cannot be parsed
140
+
141
+ Examples
142
+ --------
143
+ >>> import luminarycloud.thirdparty.onshape as onshape
144
+ >>> # Fetch variables from a workspace URL
145
+ >>> fake_url = "https://cad.onshape.com/documents/abc123/w/def456/e/ghi789"
146
+ >>> variables = onshape.fetch_variables(fake_url)
147
+ >>> for var in variables:
148
+ ... print(f"{var.name}: {var.value} ({var.type})")
149
+
150
+ >>> # Fetch variables from a version URL
151
+ >>> fake_url = "https://cad.onshape.com/documents/abc123/v/xyz999/e/ghi789"
152
+ >>> variables = onshape.fetch_variables(fake_url)
153
+ """
154
+
155
+ path = _parse_onshape_url(onshape_url)
156
+ req = onshapepb.FetchVariablesRequest(path=path)
157
+ res = get_default_client().FetchVariables(req)
158
+ variables = []
159
+ for proto_var in res.variables:
160
+ variables.append(
161
+ OnshapeVariable(
162
+ type=proto_var.type,
163
+ name=proto_var.name,
164
+ value=proto_var.value,
165
+ expression=proto_var.expression,
166
+ description=proto_var.description,
167
+ )
168
+ )
169
+
170
+ return variables
@@ -20,6 +20,7 @@ from .filters import (
20
20
  FixedSizeVectorGlyphs as FixedSizeVectorGlyphs,
21
21
  ScaledVectorGlyphs as ScaledVectorGlyphs,
22
22
  RakeStreamlines as RakeStreamlines,
23
+ GridStreamlines as GridStreamlines,
23
24
  SurfaceStreamlines as SurfaceStreamlines,
24
25
  SurfaceLIC as SurfaceLIC,
25
26
  Threshold as Threshold,
@@ -30,6 +31,7 @@ from .data_extraction import (
30
31
  IntersectionCurve as IntersectionCurve,
31
32
  DataExtractor as DataExtractor,
32
33
  ExtractOutput as ExtractOutput,
34
+ LineSample as LineSample,
33
35
  list_data_extracts as list_data_extracts,
34
36
  )
35
37
 
@@ -8,7 +8,7 @@ from typing import List, Tuple, cast, Union
8
8
  from abc import ABC, abstractmethod
9
9
  from .._proto.api.v0.luminarycloud.vis import vis_pb2
10
10
  from .primitives import Plane
11
- from ..types.vector3 import _to_vector3
11
+ from ..types.vector3 import _to_vector3, Vector3Like, Vector3
12
12
  from .._client import get_default_client
13
13
  import logging
14
14
  from ..solution import Solution
@@ -122,6 +122,45 @@ class IntersectionCurve(DataExtract):
122
122
  return vis_filter
123
123
 
124
124
 
125
+ class LineSample(DataExtract):
126
+ """
127
+
128
+ Generate line data by computing intersections between volumetric data and a line.
129
+
130
+ Extracts a 1D curve where the line intersects cell faces with solution field
131
+ values at intersection points.
132
+
133
+ .. warning:: This feature is experimental and may change or be removed in the future.
134
+
135
+ Attributes:
136
+ -----------
137
+ start: Vector3Like
138
+ The start of the line segment. Defaults to (0, 0, 0).
139
+ end: Vector3Like
140
+ The end of the line segment. Defaults to (1, 0, 0).
141
+ label: str
142
+ A user provided label for the line sample.
143
+ name : str
144
+ A user provided name for the filter.
145
+ """
146
+
147
+ def __init__(self, name: str) -> None:
148
+ super().__init__(generate_id("line-sample"))
149
+ self.name = name
150
+ self.start: Vector3Like = Vector3(x=0, y=0, z=0)
151
+ self.end: Vector3Like = Vector3(x=1, y=0, z=0)
152
+ self.label: str = ""
153
+
154
+ def _to_proto(self) -> vis_pb2.Filter:
155
+ vis_filter = vis_pb2.Filter()
156
+ vis_filter.id = self.id
157
+ vis_filter.name = self.name
158
+ vis_filter.line_sample.label = self.label
159
+ vis_filter.line_sample.start.CopyFrom(_to_vector3(self.start)._to_proto())
160
+ vis_filter.line_sample.end.CopyFrom(_to_vector3(self.end)._to_proto())
161
+ return vis_filter
162
+
163
+
125
164
  class ExtractOutput:
126
165
  """
127
166
  The extract output represents the request to extract data from a solution,
@@ -631,7 +631,7 @@ class RakeStreamlines(Streamlines):
631
631
  max_length: float
632
632
  The maximum path length of the particle in meters. Default: 10
633
633
  quantity: VisQuantity
634
- The vector field to used for the particle advection. Default: Velocity
634
+ The vector field to use for the particle advection. Default: Velocity
635
635
  start: Vector3Like
636
636
  The start point of the rake. Default: [0,0,0].
637
637
  end: Vector3Like
@@ -687,6 +687,130 @@ class RakeStreamlines(Streamlines):
687
687
  self.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
688
688
 
689
689
 
690
+ class GridStreamlines(Streamlines):
691
+ """
692
+ Streamlines is a vector field visualization technique that integrates
693
+ massless particles through a vector field forming curves. Streamlines are
694
+ used to visualize and analyze fluid flow patterns (e.g., the velocity
695
+ field), helping to understand how the fluid moves. Streamlines
696
+ can be use used to visualize any vector field contained in the solution.
697
+
698
+ GridStreamlines generates seed particles arranged in a 2D grid pattern
699
+ inside the volume. GridStreamlines only work with volume data.
700
+
701
+ The grid is defined by a center point and two vectors that define the u
702
+ (rake_direction) and v (seed_direction) directions of the grid. It's
703
+ recommended that the rake_direction and seed_direction vectors are
704
+ orthogonal to each other, but it's not required. Rakes (sets of seed
705
+ particles) are generated in the rake_direction. Seed particles are
706
+ distributed along the seed_direction. The rake spacing controls the distance
707
+ between the rakes, and the seed spacing controls the distance between the
708
+ seed particles along the rake.
709
+
710
+ For example, if the rake vector is [1,0,0] and the seed vector is [0,1,0],
711
+ then the rakes will be generated in the x direction and the seed particles
712
+ will be generated in the y direction. Lets say we want to create a grid of
713
+ of 4x4 particles that is 8 meters wide and 2 meters tall. The rake spacing
714
+ would be 2 meters / 4 = 0.5 meters, and the seed spacing would be 8 meters /
715
+ 4 = 2 meters.
716
+
717
+ .. warning:: This feature is experimental and may change or be removed in the future.
718
+
719
+ Attributes:
720
+ -----------
721
+ max_length: float
722
+ The maximum path length of the particle in meters. Default: 10
723
+ quantity: VisQuantity
724
+ The vector field to use for the particle advection. Default: Velocity
725
+ rake_direction: Vector3Like
726
+ The vector defining the u direction of the grid along which the rakes are placed.
727
+ Default: [1,0,0].
728
+ seed_direction: Vector3Like
729
+ The vector defining the v direction of the grid along which the seed particles are placed.
730
+ Default: [0,1,0].
731
+ center: Vector3Like
732
+ The center point of the grid. Default: [0,0,0].
733
+ rake_res: int
734
+ The number of rake lines to generate in the u direction. Default: 2.
735
+ seed_res: int
736
+ The number of seed particles to generate in the v direction. Default: 10.
737
+ rake_spacing: float
738
+ The spacing between the rake lines in meters. Default: 0.5.
739
+ seed_spacing: float
740
+ The spacing between the seed particles in meters. Default: 0.1.
741
+ name : str
742
+ A user provided name for the filter.
743
+ display_attrs : DisplayAttributes
744
+ Specifies this filter's appearance.
745
+ """
746
+
747
+ def __init__(self, name: str = "") -> None:
748
+ super().__init__(name)
749
+ self.rake_direction: Vector3Like = Vector3(x=1, y=0, z=0)
750
+ self.seed_direction: Vector3Like = Vector3(x=0, y=1, z=0)
751
+ self.center: Vector3Like = Vector3(x=0, y=0, z=0)
752
+ self.rake_res: int = 2
753
+ self.seed_res: int = 10
754
+ self.rake_spacing: float = 0.5
755
+ self.seed_spacing: float = 0.1
756
+
757
+ def _to_proto(self) -> vis_pb2.Filter:
758
+ # Type checking
759
+ if not isinstance(self.max_length, (float, int)):
760
+ raise TypeError(f"Expected 'float or int', got {type(self.max_length).__name__}")
761
+ if not isinstance(self.quantity, VisQuantity):
762
+ raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
763
+ if not isinstance(self.rake_res, int):
764
+ raise TypeError(f"Expected 'int', got {type(self.rake_res).__name__}")
765
+ if not isinstance(self.seed_res, int):
766
+ raise TypeError(f"Expected 'int', got {type(self.seed_res).__name__}")
767
+ if not isinstance(self.rake_spacing, (float, int)):
768
+ raise TypeError(f"Expected 'float or int', got {type(self.rake_spacing).__name__}")
769
+ if not isinstance(self.seed_spacing, (float, int)):
770
+ raise TypeError(f"Expected 'float or int', got {type(self.seed_spacing).__name__}")
771
+
772
+ vis_filter = vis_pb2.Filter()
773
+ vis_filter.id = self.id
774
+ vis_filter.name = self.name
775
+ vis_filter.streamlines.max_length = self.max_length
776
+ vis_filter.streamlines.grid.center.CopyFrom(_to_vector3(self.center)._to_proto())
777
+ vis_filter.streamlines.grid.u_vec.CopyFrom(_to_vector3(self.rake_direction)._to_proto())
778
+ vis_filter.streamlines.grid.v_vec.CopyFrom(_to_vector3(self.seed_direction)._to_proto())
779
+ vis_filter.streamlines.grid.rake_res = self.rake_res
780
+ vis_filter.streamlines.grid.seed_res = self.seed_res
781
+ vis_filter.streamlines.grid.rake_spacing = self.rake_spacing
782
+ vis_filter.streamlines.grid.seed_spacing = self.seed_spacing
783
+
784
+ if not quantity_type._is_vector(self.quantity):
785
+ raise ValueError("GridStreamlines: field must be a vector type")
786
+ vis_filter.streamlines.field.quantity_typ = self.quantity.value
787
+ return vis_filter
788
+
789
+ def _from_proto(self, filter: vis_pb2.Filter) -> None:
790
+ typ = filter.WhichOneof("value")
791
+ if typ != "streamlines":
792
+ raise TypeError(f"Expected 'streamlines', got {typ}")
793
+ s_typ = filter.streamlines.WhichOneof("seed_type")
794
+ if s_typ != "grid":
795
+ raise TypeError(f"Expected 'grid streamlines', got {s_typ}")
796
+ self.id = filter.id
797
+ self.name = filter.name
798
+ self.n_streamlines = filter.streamlines.n_streamlines
799
+ self.max_length = filter.streamlines.max_length
800
+
801
+ self.center = Vector3()
802
+ self.center._from_proto(filter.streamlines.grid.center)
803
+ self.rake_direction = Vector3()
804
+ self.rake_direction._from_proto(filter.streamlines.grid.u_vec)
805
+ self.seed_direction = Vector3()
806
+ self.seed_direction._from_proto(filter.streamlines.grid.v_vec)
807
+ self.rake_res = filter.streamlines.grid.rake_res
808
+ self.seed_res = filter.streamlines.grid.seed_res
809
+ self.rake_spacing = filter.streamlines.grid.rake_spacing
810
+ self.seed_spacing = filter.streamlines.grid.seed_spacing
811
+ self.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
812
+
813
+
690
814
  class SurfaceStreamlines(Streamlines):
691
815
  """
692
816
  Streamlines is a vector field visualization technique that integrates
@@ -732,7 +856,7 @@ class SurfaceStreamlines(Streamlines):
732
856
  max_length: float
733
857
  The maximum path length of the particle in meters. Default: 10
734
858
  quantity: VisQuantity
735
- The vector field to used for the particle advection. Default: Velocity
859
+ The vector field to use for the particle advection. Default: Velocity
736
860
  name : str
737
861
  A user provided name for the filter.
738
862
  display_attrs : DisplayAttributes
@@ -963,6 +1087,8 @@ def _filter_to_obj_name(filter: Filter) -> str:
963
1087
  return "threshold"
964
1088
  elif isinstance(filter, RakeStreamlines):
965
1089
  return "rake_streamlines"
1090
+ elif isinstance(filter, GridStreamlines):
1091
+ return "grid_streamlines"
966
1092
  elif isinstance(filter, SurfaceStreamlines):
967
1093
  return "surface_streamlines"
968
1094
  elif isinstance(filter, SurfaceLIC):
@@ -507,7 +507,7 @@ class Scene(CodeRepr):
507
507
  display attributes using tags, then surface ids.
508
508
  """
509
509
  if not surface_id in self._surface_ids:
510
- raise ValueError(f"Id {id} not a boundary id")
510
+ raise ValueError(f"Id {surface_id} not a boundary id")
511
511
  self._surface_visibilities[surface_id] = visible
512
512
 
513
513
  if self._interactive_scene:
@@ -157,8 +157,8 @@ class VolumeSelection:
157
157
  cad_url = finish_res.url
158
158
 
159
159
  feature = gpb.Feature(feature_name=feature_name)
160
- getattr(feature, "import").CopyFrom(
161
- gpb.Import(
160
+ feature.import_geometry.CopyFrom(
161
+ gpb.ImportGeometry(
162
162
  geometry_url=cad_url,
163
163
  scaling=scaling,
164
164
  force_discrete=force_discrete,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: luminarycloud
3
- Version: 0.16.0
3
+ Version: 0.16.1
4
4
  Summary: Luminary Cloud SDK
5
5
  Project-URL: Homepage, https://www.luminarycloud.com/
6
6
  Project-URL: Documentation, https://app.luminarycloud.com/docs/api/
@@ -13,13 +13,13 @@ Requires-Dist: grpcio-status~=1.51
13
13
  Requires-Dist: grpcio-tools~=1.51
14
14
  Requires-Dist: grpcio~=1.51
15
15
  Requires-Dist: importlib-metadata~=6.0.0
16
- Requires-Dist: opentelemetry-api~=1.25.0
17
- Requires-Dist: opentelemetry-exporter-otlp-proto-common~=1.25.0
18
- Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.25.0
16
+ Requires-Dist: opentelemetry-api~=1.25
17
+ Requires-Dist: opentelemetry-exporter-otlp-proto-common~=1.25
18
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.25
19
19
  Requires-Dist: opentelemetry-instrumentation-grpc~=0.46b0
20
20
  Requires-Dist: opentelemetry-instrumentation~=0.46b0
21
- Requires-Dist: opentelemetry-proto~=1.25.0
22
- Requires-Dist: opentelemetry-sdk~=1.25.0
21
+ Requires-Dist: opentelemetry-proto~=1.25
22
+ Requires-Dist: opentelemetry-sdk~=1.25
23
23
  Requires-Dist: opentelemetry-semantic-conventions~=0.46b0
24
24
  Requires-Dist: protobuf~=4.21
25
25
  Requires-Dist: pyjwt~=2.6