luminarycloud 0.15.5__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.
- luminarycloud/_client/client.py +5 -0
- luminarycloud/_helpers/__init__.py +1 -0
- luminarycloud/_helpers/_code_representation.py +21 -4
- luminarycloud/_helpers/download.py +67 -1
- luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +9 -9
- luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +7 -4
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +45 -21
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +65 -0
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.py +194 -7
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.pyi +407 -5
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.py +171 -0
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.pyi +64 -0
- luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2.py +4 -2
- luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +128 -107
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +48 -3
- luminarycloud/_proto/assistant/assistant_pb2.py +82 -61
- luminarycloud/_proto/assistant/assistant_pb2.pyi +40 -0
- luminarycloud/_proto/assistant/assistant_pb2_grpc.py +34 -0
- luminarycloud/_proto/assistant/assistant_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/base/base_pb2.py +7 -6
- luminarycloud/_proto/base/base_pb2.pyi +4 -0
- luminarycloud/_proto/client/simulation_pb2.py +351 -351
- luminarycloud/_proto/client/simulation_pb2.pyi +105 -97
- luminarycloud/_proto/geometry/geometry_pb2.py +68 -68
- luminarycloud/_proto/geometry/geometry_pb2.pyi +15 -7
- luminarycloud/_proto/hexmesh/hexmesh_pb2.py +40 -15
- luminarycloud/_proto/hexmesh/hexmesh_pb2.pyi +58 -1
- luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +11 -11
- luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +12 -4
- luminarycloud/_proto/lcstatus/codes_pb2.py +3 -2
- luminarycloud/_proto/lcstatus/codes_pb2.pyi +4 -0
- luminarycloud/_proto/quantity/quantity_pb2.py +11 -2
- luminarycloud/_proto/quantity/quantity_pb2.pyi +6 -0
- luminarycloud/_proto/table/table_pb2.pyi +4 -2
- luminarycloud/_proto/upload/upload_pb2.py +27 -7
- luminarycloud/_proto/upload/upload_pb2.pyi +31 -0
- luminarycloud/enum/quantity_type.py +19 -0
- luminarycloud/enum/tables.py +1 -0
- luminarycloud/enum/vis_enums.py +20 -0
- luminarycloud/feature_modification.py +6 -7
- luminarycloud/geometry.py +24 -0
- luminarycloud/geometry_version.py +23 -0
- luminarycloud/mesh.py +8 -1
- luminarycloud/params/simulation/adjoint_.py +4 -4
- luminarycloud/params/simulation/material/material_fluid_.py +1 -1
- luminarycloud/params/simulation/material/material_solid_.py +1 -1
- luminarycloud/params/simulation/output_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/initialization/fluid_existing_solution_.py +28 -0
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/__init__.py +1 -0
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_auto_.py +30 -0
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_on_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation_.py +6 -2
- luminarycloud/params/simulation/physics/fluid/solution_controls_fluid_.py +4 -0
- luminarycloud/params/simulation/simulation_param_.py +6 -0
- luminarycloud/physics_ai/__init__.py +4 -0
- luminarycloud/physics_ai/inference.py +140 -4
- luminarycloud/physics_ai/solution.py +60 -0
- luminarycloud/project.py +9 -7
- luminarycloud/simulation_param.py +29 -15
- luminarycloud/simulation_template.py +14 -10
- luminarycloud/tables.py +11 -12
- luminarycloud/thirdparty/__init__.py +12 -0
- luminarycloud/thirdparty/onshape.py +170 -0
- luminarycloud/vis/__init__.py +2 -0
- luminarycloud/vis/data_extraction.py +44 -6
- luminarycloud/vis/display.py +26 -11
- luminarycloud/vis/filters.py +226 -67
- luminarycloud/vis/primitives.py +3 -2
- luminarycloud/vis/visualization.py +198 -41
- luminarycloud/volume_selection.py +2 -2
- {luminarycloud-0.15.5.dist-info → luminarycloud-0.16.1.dist-info}/METADATA +6 -6
- {luminarycloud-0.15.5.dist-info → luminarycloud-0.16.1.dist-info}/RECORD +77 -73
- {luminarycloud-0.15.5.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
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
272
|
-
self.adjoint.
|
|
273
|
-
self.adjoint.
|
|
274
|
-
self.adjoint.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -121,11 +122,11 @@ def create_rectilinear_table(
|
|
|
121
122
|
table.header.record_label[-1].name = label
|
|
122
123
|
else:
|
|
123
124
|
table.header.record_label[-1].quantity = QuantityType(label).value
|
|
124
|
-
table.record.append(tablepb.Record())
|
|
125
125
|
|
|
126
126
|
types = data_types(table_type, len(header))
|
|
127
127
|
|
|
128
128
|
for row in rows:
|
|
129
|
+
record = tablepb.Record()
|
|
129
130
|
for i, val in enumerate(row):
|
|
130
131
|
# Axis coordinates are always adfloats and cannot be missing.
|
|
131
132
|
if i == 0 and has_axis(table_type):
|
|
@@ -133,36 +134,34 @@ def create_rectilinear_table(
|
|
|
133
134
|
table.axis[0].coordinate[-1].adfloat.value = float(val)
|
|
134
135
|
continue
|
|
135
136
|
|
|
136
|
-
j = i - has_axis(table_type)
|
|
137
137
|
if val == "":
|
|
138
138
|
if allow_missing_entries(table_type):
|
|
139
|
-
|
|
140
|
-
tablepb.Record.Entry(empty=tablepb.Record.Entry.Empty())
|
|
141
|
-
)
|
|
139
|
+
record.entry.append(tablepb.Record.Entry(empty=tablepb.Record.Entry.Empty()))
|
|
142
140
|
pass
|
|
143
141
|
else:
|
|
144
142
|
raise ValueError(f"Entry {i} in row {row} is missing.")
|
|
145
143
|
continue
|
|
146
144
|
|
|
147
|
-
|
|
145
|
+
record.entry.append(tablepb.Record.Entry())
|
|
148
146
|
try:
|
|
149
147
|
if types[i] == float:
|
|
150
|
-
|
|
148
|
+
record.entry[-1].adfloat.value = float(val)
|
|
151
149
|
else:
|
|
152
|
-
|
|
150
|
+
record.entry[-1].string = val
|
|
153
151
|
except ValueError:
|
|
154
152
|
raise ValueError(f"Expected type {types[i]} for entry {i} in row {row}.")
|
|
153
|
+
table.record.append(record)
|
|
155
154
|
|
|
156
155
|
return table
|
|
157
156
|
|
|
158
157
|
|
|
159
158
|
@dataclass(kw_only=True)
|
|
160
|
-
class RectilinearTable:
|
|
159
|
+
class RectilinearTable(CodeRepr):
|
|
161
160
|
"""Represents an uploaded table."""
|
|
162
161
|
|
|
163
|
-
id: str
|
|
164
|
-
name: str
|
|
165
|
-
table_type: TableType
|
|
162
|
+
id: str = ""
|
|
163
|
+
name: str = ""
|
|
164
|
+
table_type: TableType = TableType.INVALID
|
|
166
165
|
|
|
167
166
|
|
|
168
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
|
luminarycloud/vis/__init__.py
CHANGED
|
@@ -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,
|
|
@@ -381,12 +420,11 @@ class DataExtractor:
|
|
|
381
420
|
simulation = get_simulation(self._solution.simulation_id)
|
|
382
421
|
mesh_meta = get_mesh_metadata(simulation.mesh_id)
|
|
383
422
|
mesh = get_mesh(simulation.mesh_id)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
except NotFoundError:
|
|
423
|
+
geo_ver = mesh.geometry_version()
|
|
424
|
+
if geo_ver is None:
|
|
387
425
|
self._has_tags = False
|
|
388
|
-
|
|
389
|
-
|
|
426
|
+
else:
|
|
427
|
+
geom = geo_ver.geometry()
|
|
390
428
|
|
|
391
429
|
self._surface_ids: List[str] = []
|
|
392
430
|
for zone in mesh_meta.zones:
|
luminarycloud/vis/display.py
CHANGED
|
@@ -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.
|
|
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
|
|
217
|
-
|
|
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,
|