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
luminarycloud/mesh.py CHANGED
@@ -44,10 +44,17 @@ class Mesh(ProtoWrapperBase):
44
44
  """
45
45
  return lc.get_project(ProjectID(self._proto.project_id))
46
46
 
47
- def geometry_version(self) -> GeometryVersion:
47
+ def geometry_version(self) -> GeometryVersion | None:
48
48
  """
49
49
  Get the geometry version associated with this mesh.
50
+
51
+ Returns
52
+ -------
53
+ GeometryVersion | None
54
+ The geometry version associated with this mesh, or None if the mesh has no geometry version.
50
55
  """
56
+ if self._proto.geometry_version_id == "":
57
+ return None
51
58
  return get_geometry_version(self._proto.geometry_version_id)
52
59
 
53
60
  def update(
@@ -1,2 +1,3 @@
1
+ from .robust_startup_auto_ import RobustStartupAuto
1
2
  from .robust_startup_off_ import RobustStartupOff
2
3
  from .robust_startup_on_ import RobustStartupOn
@@ -0,0 +1,30 @@
1
+ # Generated by generate_sdk_wrappers.py. DO NOT EDIT
2
+
3
+ from abc import ABC, ABCMeta
4
+ from dataclasses import dataclass, field
5
+ from typing import Any
6
+ from uuid import uuid4
7
+
8
+ from google.protobuf.message import Message as _Message
9
+ from luminarycloud.tables import RectilinearTable, _param_name_to_table_type
10
+ from luminarycloud.types import Vector3, LcFloat
11
+ from luminarycloud.types.adfloat import _to_ad_proto, _from_ad_proto
12
+ from luminarycloud._helpers._entity_identifier import _create_entity_identifier
13
+ from luminarycloud._proto.client import simulation_pb2 as clientpb
14
+ from luminarycloud._proto.client.entity_pb2 import EntityIdentifier
15
+ from luminarycloud._helpers import CodeRepr
16
+ import luminarycloud.params.enum._enum_wrappers as enum
17
+
18
+ from luminarycloud.params.simulation._lib import ParamGroupWrapper, create_unique_id
19
+
20
+ from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relaxation_method.fluid_implicit_relaxation.robust_startup_ import (
21
+ RobustStartup,
22
+ )
23
+ from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relaxation_method.fluid_implicit_relaxation.robust_startup_ import *
24
+
25
+
26
+ @dataclass(kw_only=True)
27
+ class RobustStartupAuto(RobustStartup):
28
+ """Enable robust startup mode and automatically determine the settings."""
29
+
30
+ pass
@@ -25,7 +25,7 @@ from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relax
25
25
 
26
26
  @dataclass(kw_only=True)
27
27
  class RobustStartupOn(RobustStartup):
28
- """Enable robust startup mode."""
28
+ """Enable robust startup mode and manually specify the settings."""
29
29
 
30
30
  robust_startup_initial_cfl: LcFloat = 1.0
31
31
  "Initial CFL number for robust startup mode. The CFL is ramped toward the target value during startup."
@@ -37,6 +37,10 @@ from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relax
37
37
  RobustStartup,
38
38
  )
39
39
  from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relaxation_method.fluid_implicit_relaxation.robust_startup_ import *
40
+ from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relaxation_method.fluid_implicit_relaxation.robust_startup.robust_startup_auto_ import (
41
+ RobustStartupAuto,
42
+ )
43
+ from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relaxation_method.fluid_implicit_relaxation.robust_startup.robust_startup_auto_ import *
40
44
  from luminarycloud.params.simulation.physics.fluid.solution_controls.fluid_relaxation_method.fluid_implicit_relaxation.robust_startup.robust_startup_off_ import (
41
45
  RobustStartupOff,
42
46
  )
@@ -54,7 +58,7 @@ class FluidImplicitRelaxation(FluidRelaxationMethod):
54
58
  implicit_method: enum.ImplicitMethod = enum.ImplicitMethod.BACKWARD_EULER
55
59
  "Scheme for implicit relaxation of the governing equations."
56
60
  linear_solver_type: LinearSolverType = field(default_factory=GaussSeidel)
57
- "Type of linear solver used for implicit relaxation. Possible types: ``GaussSeidel``, ``GsAmgx``, ``KrylovAmg``, ``Amg`` from the ``linear_solver_type`` module."
61
+ "Type of linear solver used for implicit relaxation. Possible types: ``GaussSeidel``, ``KrylovAmg`` from the ``linear_solver_type`` module."
58
62
  jacobian_update_method: enum.JacobianUpdateMethod = (
59
63
  enum.JacobianUpdateMethod.EXPLICIT_INTERVAL_AND_WARMUP
60
64
  )
@@ -63,8 +67,8 @@ class FluidImplicitRelaxation(FluidRelaxationMethod):
63
67
  "How many iterations in between updating the Jacobian values for implicit solving."
64
68
  jacobian_warmup_threshold: int = 300
65
69
  "How many iterations to update Jacobians every iteration before switching to the specified 'Jacobian Update Interval'."
66
- robust_startup: RobustStartup = field(default_factory=RobustStartupOn)
67
- "Applies a robust startup process during the initial transients of a simulation. Applicable to steady problems only. Possible types: ``RobustStartupOn``, ``RobustStartupOff`` from the ``robust_startup`` module."
70
+ robust_startup: RobustStartup = field(default_factory=RobustStartupAuto)
71
+ "Applies a robust startup process during the initial transients of a simulation. Applicable to steady problems only. Possible types: ``RobustStartupOn``, ``RobustStartupAuto``, ``RobustStartupOff`` from the ``robust_startup`` module."
68
72
  relaxation_flow: LcFloat = 1.0
69
73
  "Under-relaxation factor in [0,1] applied to the mean flow solution update with each implicit nonlinear iteration. Default of 1.0."
70
74
  relaxation_turb: LcFloat = 0.5
@@ -122,6 +122,8 @@ class SolutionControlsFluid(CodeRepr, ParamGroupWrapper[clientpb.SolutionControl
122
122
  _proto.robust_startup_iterations.value = (
123
123
  self.fluid_relaxation_method.robust_startup.robust_startup_iterations
124
124
  )
125
+ if isinstance(self.fluid_relaxation_method.robust_startup, RobustStartupAuto):
126
+ _proto.robust_startup = clientpb.ROBUST_STARTUP_AUTO
125
127
  if isinstance(self.fluid_relaxation_method.robust_startup, RobustStartupOff):
126
128
  _proto.robust_startup = clientpb.ROBUST_STARTUP_OFF
127
129
  _proto.relax_flow.CopyFrom(_to_ad_proto(self.fluid_relaxation_method.relaxation_flow))
@@ -211,6 +213,8 @@ class SolutionControlsFluid(CodeRepr, ParamGroupWrapper[clientpb.SolutionControl
211
213
  self.fluid_relaxation_method.robust_startup.robust_startup_iterations = (
212
214
  proto.robust_startup_iterations.value
213
215
  )
216
+ elif proto.robust_startup == clientpb.ROBUST_STARTUP_AUTO:
217
+ self.fluid_relaxation_method.robust_startup = RobustStartupAuto()
214
218
  elif proto.robust_startup == clientpb.ROBUST_STARTUP_OFF:
215
219
  self.fluid_relaxation_method.robust_startup = RobustStartupOff()
216
220
  self.fluid_relaxation_method.relaxation_flow = _from_ad_proto(proto.relax_flow)
@@ -54,7 +54,7 @@ class Turbulence(CodeRepr, ParamGroupWrapper[clientpb.Turbulence]):
54
54
  """Turbulence settings for a fluid flow physics solver."""
55
55
 
56
56
  des_formulation: DesFormulation = field(default_factory=DdesVtm)
57
- "Select a Detached Eddy Simulation (DES) formulation. Possible types: ``DdesVtm``, ``DdesVtmSigma``, ``Iddes``, ``Ddes``, ``Des97``, ``Zdes`` from the ``des_formulation`` module."
57
+ "Select a Detached Eddy Simulation (DES) formulation. Possible types: ``DdesVtm``, ``Iddes``, ``Ddes`` from the ``des_formulation`` module."
58
58
  sub_grid_scale_model: SubGridScaleModel | None = field(default_factory=Vreman)
59
59
  "Sub-grid scale models available for Large Eddy Simulation (LES). Possible types: ``Smagorinsky``, ``Vreman``, ``Wale``, ``Sigma``, ``Amd`` from the ``sub_grid_scale_model`` module."
60
60
  turbulent_prandtl_number: LcFloat = 0.85
@@ -124,7 +124,7 @@ class Fluid(CodeRepr, ParamGroupWrapper[clientpb.Fluid]):
124
124
  periodic_pair: list[PeriodicPair] = field(default_factory=list)
125
125
  "Defines the input options needed for periodic boundaries. We assume each periodic BC can have translational OR rotational periodicity. To transform a point on boundary A to its periodically matching point on boundary B, we first subtract the center of rotation from the point coordinates to get the distance vector from the center to the point of interest, then we apply rotation around the periodicity axis and add back the center of rotation to get the coordinates of the transformed point."
126
126
  initialization: InitializationFluid = field(default_factory=FluidPrescribedValues)
127
- "Type of initial condition for the field variables. Possible types: ``FluidPrescribedValues``, ``FluidFarfieldValues``, ``FluidPotentialFlow``, ``FluidVerificationSolution``, ``FluidExistingSolution`` from the ``initialization`` module."
127
+ "Type of initial condition for the field variables. Possible types: ``FluidPrescribedValues``, ``FluidFarfieldValues``, ``FluidExistingSolution`` from the ``initialization`` module."
128
128
  physical_behavior: list[PhysicalBehavior] = field(default_factory=list)
129
129
  "Physical behavior settings for a fluid flow physics solver."
130
130
  porous_behavior: list[PorousBehavior] = field(default_factory=list)
@@ -42,4 +42,4 @@ class HeatImplicitRelaxation(HeatRelaxationMethod):
42
42
  implicit_method: enum.ImplicitMethod = enum.ImplicitMethod.BACKWARD_EULER
43
43
  "Scheme for implicit relaxation of the governing equations."
44
44
  linear_solver_type: LinearSolverType = field(default_factory=GaussSeidel)
45
- "Type of linear solver used for implicit relaxation. Possible types: ``GaussSeidel``, ``GsAmgx``, ``KrylovAmg``, ``Amg`` from the ``linear_solver_type`` module."
45
+ "Type of linear solver used for implicit relaxation. Possible types: ``GaussSeidel``, ``KrylovAmg`` from the ``linear_solver_type`` module."
@@ -32,7 +32,7 @@ class SolutionControlsHeat(CodeRepr, ParamGroupWrapper[clientpb.SolutionControls
32
32
  """Solution controls for a heat transfer physics solver."""
33
33
 
34
34
  heat_relaxation_method: HeatRelaxationMethod = field(default_factory=HeatImplicitRelaxation)
35
- "Relaxation scheme for steady-state simulations or time implicit transient simulations. Possible types: ``HeatImplicitRelaxation``, ``HeatExplicitRelaxation`` from the ``heat_relaxation_method`` module."
35
+ "Relaxation scheme for steady-state simulations or time implicit transient simulations. Possible types: ``HeatImplicitRelaxation`` from the ``heat_relaxation_method`` module."
36
36
  preset: enum.SolutionControlsHeatPreset = (
37
37
  enum.SolutionControlsHeatPreset.DEFAULT_SOLUTION_CONTROLS_HEAT
38
38
  )
@@ -8,3 +8,7 @@ from .models import (
8
8
  PhysicsAiModel as PhysicsAiModel,
9
9
  PhysicsAiModelVersion as PhysicsAiModelVersion,
10
10
  )
11
+
12
+ from .solution import (
13
+ _download_processed_solution_physics_ai as _download_processed_solution_physics_ai,
14
+ )
@@ -1,30 +1,165 @@
1
1
  # File: python/sdk/luminarycloud/inference/inference.py
2
2
  # Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
3
3
  from datetime import datetime
4
- from typing import Any
4
+ from typing import Any, Callable
5
5
  from json import loads as json_loads
6
+ from dataclasses import dataclass
7
+ import base64
8
+ import os
9
+ import urllib.request
6
10
 
7
11
  from .._client import get_default_client
8
12
  from .._helpers._timestamp_to_datetime import timestamp_to_datetime
9
13
  from .._proto.api.v0.luminarycloud.inference import inference_pb2 as inferencepb
10
14
  from .._proto.inferenceservice import inferenceservice_pb2 as inferenceservicepb
11
15
  from .._wrapper import ProtoWrapper, ProtoWrapperBase
16
+ from ..project import Project
12
17
  from .._helpers.warnings import experimental
18
+ from ..project import Project
19
+ from .._helpers import upload_file
20
+ from .._proto.upload import upload_pb2 as uploadpb
21
+
22
+
23
+ @dataclass
24
+ class ExtAeroInferenceResult:
25
+ """Result of an external aerodynamic inference job.
26
+
27
+ Attributes
28
+ ----------
29
+ drag_force: float
30
+ The drag force returned from the inference.
31
+ lift_force: float
32
+ The lift force returned from the inference.
33
+ wall_shear_stress:
34
+ A dict containing wall shear stress data, or None if not available.
35
+ pressure_surface:
36
+ A dict containing pressure surface stress data, or None if not available.
37
+ """
38
+
39
+ drag_force: float
40
+ lift_force: float
41
+ wall_shear_stress: dict[str, Any] | None
42
+ pressure_surface: dict[str, Any] | None
43
+
44
+ def __init__(self, inference_result: dict[str, Any]) -> None:
45
+ self.drag_force = inference_result["drag_force"]
46
+ self.lift_force = inference_result["lift_force"]
47
+ self.wall_shear_stress = inference_result.get("wall-shear-stress", None)
48
+ self.pressure_surface = inference_result.get("pressure_surface", None)
49
+
50
+
51
+ @experimental
52
+ def external_aero_inference(
53
+ project: Project, stl_file: str, checkpoint_file: str, config_name: str, stencil_size: int
54
+ ) -> ExtAeroInferenceResult:
55
+ """Performs an inference job returning external aerodynamic results.
56
+ Parameters
57
+ ----------
58
+ project : Project
59
+ The project to which the inference files will be added.
60
+ stl_file : str
61
+ Fullpath the STL file to be used for inference.
62
+ checkpoint_file : str
63
+ Fullpath of the model to be used for inference.
64
+ config_name :str
65
+ Name of the configuration to be used for inference.
66
+ stencil_size :int
67
+ Size of the stencil to be used for inference.
68
+
69
+ Returns
70
+ ExtAeroInferenceResult
71
+ Result of the external aerodynamic inference job.
72
+
73
+ warning:: This feature is experimental and may change or be removed without notice.
74
+ """
75
+
76
+ result = perform_inference(project, stl_file, checkpoint_file, config_name, stencil_size)
77
+ return ExtAeroInferenceResult(result)
78
+
79
+
80
+ @experimental
81
+ def perform_inference(
82
+ project: Project, stl_file: str, checkpoint_file: str, config_name: str, stencil_size: int
83
+ ) -> dict[str, Any]:
84
+ """Creates an inference service job.
85
+ Parameters
86
+ ----------
87
+ project : Project
88
+ The project to which the inference files will be added.
89
+ stl_file : str
90
+ Fullpath the STL file to be used for inference.
91
+ checkpoint_file : str
92
+ Fullpath of the model to be used for inference.
93
+ config_name :str
94
+ Name of the configuration to be used for inference.
95
+ stencil_size :int
96
+ Size of the stencil to be used for inference.
97
+
98
+
99
+ Returns
100
+ dict[str, Any]
101
+ Response from the server as key-value pairs.
102
+
103
+ warning:: This feature is experimental and may change or be removed without notice.
104
+ """
105
+
106
+ client = get_default_client()
107
+
108
+ def upload_if_file(fname: str) -> str:
109
+ if os.path.exists(fname) and os.path.isfile(fname):
110
+ params = uploadpb.ResourceParams()
111
+ result = upload_file(client, project.id, params, fname)
112
+ return result[1].url
113
+ if fname.startswith("gs://"):
114
+ return fname
115
+ raise RuntimeError("Unsupported file for inference")
116
+
117
+ def future_file(url: str) -> Callable[[], dict[str, Any]]:
118
+ def download_file() -> dict[str, Any]:
119
+ with urllib.request.urlopen(url) as f:
120
+ serialized = f.read()
121
+ jsondata = json_loads(serialized)
122
+ data = base64.b64decode(jsondata["data"])
123
+ jsondata["data"] = data
124
+ return jsondata
125
+
126
+ return download_file
127
+
128
+ stl_url = upload_if_file(stl_file)
129
+ check_url = upload_if_file(checkpoint_file)
130
+
131
+ raw = start_inference_job(project, stl_url, check_url, config_name, stencil_size)
132
+ currated: dict[str, Any] = {}
133
+ for k, v in raw.items():
134
+ if isinstance(v, str) and v.startswith("https://"):
135
+ tmp = future_file(v)
136
+ if k.endswith("_url"):
137
+ currated[k[:-4]] = tmp
138
+ currated[k] = v
139
+ else:
140
+ currated[k] = tmp
141
+ currated[k + "_url"] = v
142
+ else:
143
+ currated[k] = v
144
+ return currated
13
145
 
14
146
 
15
147
  @experimental
16
148
  def start_inference_job(
149
+ project: Project,
17
150
  stl_url: str,
18
- model_url: str,
151
+ checkpoint_url: str,
19
152
  config_name: str,
20
153
  stencil_size: int,
21
154
  ) -> dict[str, Any]:
22
155
  """Creates an inference service job.
23
156
  Parameters
24
157
  ----------
158
+ project : Project
159
+ Reference to a project.
25
160
  stl_url : str
26
161
  URL of the STL file to be used for inference.
27
- model_url : str
162
+ checkpoint_url : str
28
163
  URL of the model to be used for inference.
29
164
  config_name :str
30
165
  Name of the configuration to be used for inference.
@@ -41,9 +176,10 @@ def start_inference_job(
41
176
 
42
177
  req = inferencepb.CreateInferenceServiceJobRequest(
43
178
  stl_url=stl_url,
44
- model_url=model_url,
179
+ checkpoint_url=checkpoint_url,
45
180
  config_name=config_name,
46
181
  stencil_size=stencil_size,
182
+ project_id=project.id,
47
183
  )
48
184
 
49
185
  res: inferencepb.CreateInferenceServiceJobResponse = (
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ import tarfile
4
+ from typing import List, Optional, BinaryIO, cast
5
+
6
+ from .._client import get_default_client
7
+ from .._helpers.warnings import experimental
8
+ from .._helpers.download import download_solution_physics_ai as _download_solution_physics_ai
9
+ from ..enum.quantity_type import QuantityType
10
+
11
+
12
+ @experimental
13
+ def _download_processed_solution_physics_ai( # noqa: F841
14
+ solution_id: str,
15
+ exclude_surfaces: Optional[List[str]] = None,
16
+ fill_holes: float = -1.0,
17
+ surface_fields_to_keep: Optional[List[QuantityType]] = None,
18
+ volume_fields_to_keep: Optional[List[QuantityType]] = None,
19
+ process_volume: bool = False,
20
+ single_precision: bool = True,
21
+ ) -> tarfile.TarFile:
22
+ """
23
+ Download solution data with physics AI processing applied.
24
+
25
+ Returns a compressed archive containing processed solution files including
26
+ merged surfaces (VTP/STL) and optionally volume data (VTU).
27
+
28
+ Args:
29
+ solution_id: ID of the solution to download
30
+ exclude_surfaces: List of surface names to exclude from processing
31
+ fill_holes: Sets the maximum size of the hole to be filled for the STL file, measured as the radius of the bounding circumsphere.
32
+ If fill_holes is negative or zero, no holes will be filled.
33
+ surface_fields_to_keep: List of QuantityType enum values for surface fields to keep in output.
34
+ If None, all available surface fields are included.
35
+ volume_fields_to_keep: List of QuantityType enum values for volume fields to keep in output.
36
+ If None, all available volume fields are included.
37
+ process_volume: Whether to process volume data
38
+ single_precision: Whether to use single precision for floating point fields
39
+
40
+ Raises:
41
+ ValueError: If invalid field names are provided
42
+ """
43
+
44
+ stream = _download_solution_physics_ai(
45
+ get_default_client(),
46
+ solution_id,
47
+ exclude_surfaces=exclude_surfaces,
48
+ fill_holes=fill_holes,
49
+ surface_fields_to_keep=surface_fields_to_keep,
50
+ volume_fields_to_keep=volume_fields_to_keep,
51
+ process_volume=process_volume,
52
+ single_precision=single_precision,
53
+ )
54
+
55
+ assert stream is not None, "Failed to download solution data"
56
+ return tarfile.open(
57
+ name=stream.filename,
58
+ fileobj=cast(BinaryIO, stream),
59
+ mode="r|gz",
60
+ )
luminarycloud/tables.py CHANGED
@@ -121,11 +121,11 @@ def create_rectilinear_table(
121
121
  table.header.record_label[-1].name = label
122
122
  else:
123
123
  table.header.record_label[-1].quantity = QuantityType(label).value
124
- table.record.append(tablepb.Record())
125
124
 
126
125
  types = data_types(table_type, len(header))
127
126
 
128
127
  for row in rows:
128
+ record = tablepb.Record()
129
129
  for i, val in enumerate(row):
130
130
  # Axis coordinates are always adfloats and cannot be missing.
131
131
  if i == 0 and has_axis(table_type):
@@ -133,25 +133,23 @@ def create_rectilinear_table(
133
133
  table.axis[0].coordinate[-1].adfloat.value = float(val)
134
134
  continue
135
135
 
136
- j = i - has_axis(table_type)
137
136
  if val == "":
138
137
  if allow_missing_entries(table_type):
139
- table.record[j].entry.append(
140
- tablepb.Record.Entry(empty=tablepb.Record.Entry.Empty())
141
- )
138
+ record.entry.append(tablepb.Record.Entry(empty=tablepb.Record.Entry.Empty()))
142
139
  pass
143
140
  else:
144
141
  raise ValueError(f"Entry {i} in row {row} is missing.")
145
142
  continue
146
143
 
147
- table.record[j].entry.append(tablepb.Record.Entry())
144
+ record.entry.append(tablepb.Record.Entry())
148
145
  try:
149
146
  if types[i] == float:
150
- table.record[j].entry[-1].adfloat.value = float(val)
147
+ record.entry[-1].adfloat.value = float(val)
151
148
  else:
152
- table.record[j].entry[-1].string = val
149
+ record.entry[-1].string = val
153
150
  except ValueError:
154
151
  raise ValueError(f"Expected type {types[i]} for entry {i} in row {row}.")
152
+ table.record.append(record)
155
153
 
156
154
  return table
157
155
 
@@ -381,12 +381,11 @@ class DataExtractor:
381
381
  simulation = get_simulation(self._solution.simulation_id)
382
382
  mesh_meta = get_mesh_metadata(simulation.mesh_id)
383
383
  mesh = get_mesh(simulation.mesh_id)
384
- try:
385
- geom = mesh.geometry_version().geometry()
386
- except NotFoundError:
384
+ geo_ver = mesh.geometry_version()
385
+ if geo_ver is None:
387
386
  self._has_tags = False
388
- except Exception as e:
389
- raise ValueError("An unknow error occurred retrieving tags")
387
+ else:
388
+ geom = geo_ver.geometry()
390
389
 
391
390
  self._surface_ids: List[str] = []
392
391
  for zone in mesh_meta.zones:
@@ -4,10 +4,11 @@ from luminarycloud.enum import Representation, ColorMapPreset, FieldComponent, V
4
4
  from .._proto.api.v0.luminarycloud.vis import vis_pb2
5
5
  from typing import Optional
6
6
  import luminarycloud.enum.quantity_type as quantity_type
7
+ from .._helpers._code_representation import CodeRepr
7
8
 
8
9
 
9
10
  @dc.dataclass
10
- class Field:
11
+ class Field(CodeRepr):
11
12
  """
12
13
  The field controls the field displayed on the object. If the field doesn't
13
14
  exist, we show a solid color.
@@ -16,8 +17,8 @@ class Field:
16
17
 
17
18
  """
18
19
 
19
- quantity: VisQuantity = VisQuantity.ABSOLUTE_PRESSURE
20
- """The quantity to color by."""
20
+ quantity: VisQuantity = VisQuantity.NONE
21
+ """The quantity to color by. Default: NONE."""
21
22
  component: FieldComponent = FieldComponent.MAGNITUDE
22
23
  """
23
24
  The component of the field to use, applicable to vector fields. If the field is a
@@ -45,9 +46,25 @@ class Field:
45
46
  self.component = FieldComponent(field.component)
46
47
  # If its a scalar, just ignore the component.
47
48
 
49
+ def _to_code(self, hide_defaults: bool = True, use_tmp_objs: bool = True) -> str:
50
+ # We have to handle this case specially because we need to omit the field
51
+ # component if the quantity is a scalar. Otherwise, it could confuse the user.
52
+ # Also we omit the instantiation line because all classes that use a Field already
53
+ # instantiate a default one in their own constructor. (Hopefully that invariant continues to
54
+ # hold)
55
+ def enum_to_string(val: VisQuantity | FieldComponent) -> str:
56
+ str_val = val.__repr__()
57
+ return str_val.split(": ")[0][1:]
58
+
59
+ code = ""
60
+ code += f".quantity = {enum_to_string(self.quantity)}\n"
61
+ if quantity_type._is_vector(self.quantity):
62
+ code += f".component = {enum_to_string(self.component)}\n"
63
+ return code
64
+
48
65
 
49
66
  @dc.dataclass
50
- class DisplayAttributes:
67
+ class DisplayAttributes(CodeRepr):
51
68
  """
52
69
  Display attributes specify how objects such as meshes, geometries, and
53
70
  filters appear in the scene.
@@ -86,7 +103,7 @@ class DisplayAttributes:
86
103
 
87
104
 
88
105
  @dc.dataclass
89
- class DataRange:
106
+ class DataRange(CodeRepr):
90
107
  """
91
108
  The data range represents a range of values. Ranges are only valid if the
92
109
  max value is greater than the or equal to the min_value. The default is
@@ -106,7 +123,7 @@ class DataRange:
106
123
 
107
124
 
108
125
  @dc.dataclass
109
- class ColorMapAppearance:
126
+ class ColorMapAppearance(CodeRepr):
110
127
  """
111
128
  ColorMapAppearance controls how the color maps appear in the image, including
112
129
  visibility, position and size.
@@ -141,7 +158,7 @@ class ColorMapAppearance:
141
158
 
142
159
 
143
160
  @dc.dataclass
144
- class ColorMap:
161
+ class ColorMap(CodeRepr):
145
162
  """
146
163
  The color map allows user control over how field values are mapped to
147
164
  colors. Color maps are assigned to fields (e.g., the quantity and component)
@@ -213,10 +230,8 @@ class ColorMap:
213
230
  return res
214
231
 
215
232
  def _from_proto(self, color_map: vis_pb2.ColorMap) -> None:
216
- self.field = Field(
217
- component=FieldComponent(color_map.field.component),
218
- quantity=VisQuantity(color_map.field.quantity_typ),
219
- )
233
+ self.field._from_proto(color_map.field)
234
+
220
235
  if color_map.HasField("range"):
221
236
  self.data_range = DataRange(
222
237
  min_value=color_map.range.min,