luminarycloud 0.15.3__py3-none-any.whl → 0.15.5__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 (242) hide show
  1. luminarycloud/__init__.py +1 -0
  2. luminarycloud/_helpers/_create_geometry.py +36 -17
  3. luminarycloud/_helpers/_create_simulation.py +2 -0
  4. luminarycloud/_helpers/_entity_identifier.py +6 -0
  5. luminarycloud/_helpers/named_variables.py +6 -15
  6. luminarycloud/_helpers/warnings/experimental.py +6 -2
  7. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +77 -55
  8. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +34 -2
  9. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +33 -0
  10. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +10 -0
  11. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +45 -15
  12. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +104 -22
  13. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +12 -12
  14. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +10 -2
  15. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.py +27 -105
  16. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.pyi +24 -173
  17. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.py +17 -113
  18. luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.pyi +16 -44
  19. luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2.py +4 -2
  20. luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.py +37 -0
  21. luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.pyi +20 -0
  22. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +221 -143
  23. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +154 -7
  24. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.py +33 -0
  25. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.pyi +10 -0
  26. luminarycloud/_proto/assistant/assistant_pb2.py +62 -61
  27. luminarycloud/_proto/client/simulation_pb2.py +349 -309
  28. luminarycloud/_proto/client/simulation_pb2.pyi +97 -2
  29. luminarycloud/_proto/fvm/solver_results_pb2.py +25 -11
  30. luminarycloud/_proto/fvm/solver_results_pb2.pyi +24 -1
  31. luminarycloud/_proto/geometry/geometry_pb2.py +63 -64
  32. luminarycloud/_proto/namedvariableset/namedvariableset_pb2.py +49 -0
  33. luminarycloud/_proto/namedvariableset/namedvariableset_pb2.pyi +53 -0
  34. luminarycloud/_proto/upload/upload_pb2.py +27 -7
  35. luminarycloud/_proto/upload/upload_pb2.pyi +31 -0
  36. luminarycloud/_wrapper.py +26 -7
  37. luminarycloud/enum/__init__.py +2 -0
  38. luminarycloud/enum/physics_ai_lifecycle_state.py +30 -0
  39. luminarycloud/enum/quantity_type.py +43 -0
  40. luminarycloud/enum/vis_enums.py +6 -2
  41. luminarycloud/geometry.py +46 -2
  42. luminarycloud/named_variable_set.py +10 -5
  43. luminarycloud/params/enum/_enum_wrappers.py +68 -0
  44. luminarycloud/params/simulation/_lib.py +1 -1
  45. luminarycloud/params/simulation/adaptive_mesh_refinement/boundary_layer_profile_.py +5 -6
  46. luminarycloud/params/simulation/adaptive_mesh_refinement_.py +6 -7
  47. luminarycloud/params/simulation/adjoint_.py +3 -4
  48. luminarycloud/params/simulation/basic/gravity/gravity_off_.py +3 -4
  49. luminarycloud/params/simulation/basic/gravity/gravity_on_.py +3 -4
  50. luminarycloud/params/simulation/basic/gravity_.py +3 -4
  51. luminarycloud/params/simulation/body_frame_.py +3 -4
  52. luminarycloud/params/simulation/entity_relationships/volume_material_relationship_.py +9 -6
  53. luminarycloud/params/simulation/entity_relationships/volume_physics_relationship_.py +9 -6
  54. luminarycloud/params/simulation/entity_relationships_.py +3 -4
  55. luminarycloud/params/simulation/general_.py +3 -4
  56. luminarycloud/params/simulation/material/fluid/boussinesq_approximation/boussinesq_off_.py +3 -4
  57. luminarycloud/params/simulation/material/fluid/boussinesq_approximation/boussinesq_on_.py +5 -6
  58. luminarycloud/params/simulation/material/fluid/boussinesq_approximation_.py +3 -4
  59. luminarycloud/params/simulation/material/fluid/material_model/ideal_gas_.py +5 -6
  60. luminarycloud/params/simulation/material/fluid/material_model/incompressible_fluid_.py +4 -5
  61. luminarycloud/params/simulation/material/fluid/material_model/incompressible_fluid_with_energy_.py +5 -6
  62. luminarycloud/params/simulation/material/fluid/material_model_.py +3 -4
  63. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/prescribed_conductivity_.py +4 -5
  64. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/prescribed_prandtl_number_.py +4 -5
  65. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/temperature_dependent_conductivity_.py +3 -4
  66. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model_.py +3 -4
  67. luminarycloud/params/simulation/material/fluid/viscosity_model/prescribed_viscosity_.py +4 -5
  68. luminarycloud/params/simulation/material/fluid/viscosity_model/sutherland_.py +6 -7
  69. luminarycloud/params/simulation/material/fluid/viscosity_model/temperature_dependent_viscosity_.py +3 -4
  70. luminarycloud/params/simulation/material/fluid/viscosity_model_.py +3 -4
  71. luminarycloud/params/simulation/material/material_fluid_.py +4 -5
  72. luminarycloud/params/simulation/material/material_solid_.py +6 -7
  73. luminarycloud/params/simulation/material_entity_.py +6 -5
  74. luminarycloud/params/simulation/monitor_plane_.py +4 -5
  75. luminarycloud/params/simulation/motion_data/frame_transforms/no_transform_.py +3 -4
  76. luminarycloud/params/simulation/motion_data/frame_transforms/rotational_transform_.py +3 -4
  77. luminarycloud/params/simulation/motion_data/frame_transforms/translational_transform_.py +3 -4
  78. luminarycloud/params/simulation/motion_data/frame_transforms_.py +3 -4
  79. luminarycloud/params/simulation/motion_data/motion_type/constant_angular_motion_.py +3 -4
  80. luminarycloud/params/simulation/motion_data/motion_type/constant_translation_motion_.py +3 -4
  81. luminarycloud/params/simulation/motion_data/motion_type_.py +3 -4
  82. luminarycloud/params/simulation/motion_data_.py +3 -4
  83. luminarycloud/params/simulation/multi_physics_coupling_options_.py +3 -4
  84. luminarycloud/params/simulation/output_.py +3 -4
  85. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection/actuator_disk_specify_normal_vector_.py +3 -4
  86. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection/actuator_disk_specify_rotation_angles_.py +3 -4
  87. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection_.py +3 -4
  88. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk_.py +5 -6
  89. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_line_.py +3 -4
  90. luminarycloud/params/simulation/particle_group/particle_group_type/probe_points_.py +3 -4
  91. luminarycloud/params/simulation/particle_group/particle_group_type/source_points_.py +3 -4
  92. luminarycloud/params/simulation/particle_group/particle_group_type_.py +3 -4
  93. luminarycloud/params/simulation/particle_group_.py +3 -4
  94. luminarycloud/params/simulation/physics/fluid/adjoint_controls_fluid_.py +4 -5
  95. luminarycloud/params/simulation/physics/fluid/basic_fluid_.py +3 -4
  96. luminarycloud/params/simulation/physics/fluid/boundary_conditions/farfield_.py +9 -10
  97. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/fan_curve_inlet_.py +5 -6
  98. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mach_inlet_.py +5 -6
  99. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mass_flow_inlet_.py +4 -5
  100. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/total_pressure_inlet_.py +4 -5
  101. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_components_inlet_.py +4 -5
  102. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_magnitude_inlet_.py +5 -6
  103. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet_.py +4 -5
  104. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/fan_curve_outlet_.py +5 -6
  105. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_pressure_.py +4 -5
  106. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_target_corrected_mass_flow_rate_.py +7 -8
  107. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_target_mass_flow_rate_.py +5 -6
  108. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy_.py +3 -4
  109. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet_.py +3 -4
  110. luminarycloud/params/simulation/physics/fluid/boundary_conditions/symmetry_.py +3 -4
  111. luminarycloud/params/simulation/physics/fluid/boundary_conditions/turbulence_boundary_conditions_.py +9 -10
  112. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/energy/prescribed_heat_flux_.py +4 -5
  113. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/energy/prescribed_temperature_.py +4 -5
  114. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/no_slip_.py +4 -5
  115. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/slip_.py +3 -4
  116. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/wall_model_.py +4 -5
  117. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/wall_energy_.py +3 -4
  118. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/wall_momentum_.py +3 -4
  119. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall_.py +3 -4
  120. luminarycloud/params/simulation/physics/fluid/boundary_conditions_fluid_.py +3 -4
  121. luminarycloud/params/simulation/physics/fluid/initialization/fluid_existing_solution_.py +3 -4
  122. luminarycloud/params/simulation/physics/fluid/initialization/fluid_farfield_values_.py +3 -4
  123. luminarycloud/params/simulation/physics/fluid/initialization/fluid_prescribed_values_.py +5 -6
  124. luminarycloud/params/simulation/physics/fluid/initialization/turbulence_initialization_.py +9 -10
  125. luminarycloud/params/simulation/physics/fluid/initialization_fluid_.py +3 -4
  126. luminarycloud/params/simulation/physics/fluid/physical_behavior/blade_element_airfoil_data_.py +4 -5
  127. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_blade_element_.py +3 -4
  128. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_radial_distribution_.py +6 -7
  129. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_uniform_thrust_.py +4 -5
  130. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/fan_curve_internal_.py +4 -5
  131. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model_.py +3 -4
  132. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_line_model/actuator_line_blade_element_.py +3 -4
  133. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_line_model_.py +3 -4
  134. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/blade_element_params_.py +5 -6
  135. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model/general_acceleration_source_.py +3 -4
  136. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model/general_mass_source_.py +4 -5
  137. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model_.py +3 -4
  138. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model_.py +3 -4
  139. luminarycloud/params/simulation/physics/fluid/physical_behavior_.py +3 -4
  140. luminarycloud/params/simulation/physics/fluid/porous_behavior_.py +4 -5
  141. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_explicit_relaxation_.py +3 -4
  142. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_off_.py +3 -4
  143. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_on_.py +4 -5
  144. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup_.py +3 -4
  145. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation_.py +9 -10
  146. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method_.py +3 -4
  147. luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method/cfl_based_.py +4 -5
  148. luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method/fixed_pseudo_time_step_.py +4 -5
  149. luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method_.py +3 -4
  150. luminarycloud/params/simulation/physics/fluid/solution_controls_fluid_.py +3 -4
  151. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/ec2_.py +3 -4
  152. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/fds_.py +5 -6
  153. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/ld2_.py +5 -6
  154. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/rhie_chow_.py +4 -5
  155. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_.py +3 -4
  156. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order/first_order_.py +3 -4
  157. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order/second_order_.py +8 -9
  158. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order_.py +3 -4
  159. luminarycloud/params/simulation/physics/fluid/spatial_discretization_fluid_.py +4 -5
  160. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/ddes_.py +3 -4
  161. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/ddes_vtm_.py +3 -4
  162. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/iddes_.py +3 -4
  163. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation_.py +3 -4
  164. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/constants/custom_komega_sst_constants_.py +16 -17
  165. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/constants/default_komega_sst_constants_.py +3 -4
  166. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/komega_sst_constants_.py +3 -4
  167. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst_.py +3 -4
  168. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/constants/custom_spalart_allmaras_constants_.py +15 -16
  169. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/constants/default_spalart_allmaras_constants_.py +3 -4
  170. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/spalart_allmaras_constants_.py +3 -4
  171. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras_.py +3 -4
  172. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/amd_.py +4 -5
  173. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/sigma_.py +4 -5
  174. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/smagorinsky_.py +4 -5
  175. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/vreman_.py +4 -5
  176. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/wale_.py +4 -5
  177. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model_.py +3 -4
  178. luminarycloud/params/simulation/physics/fluid/turbulence_.py +6 -7
  179. luminarycloud/params/simulation/physics/fluid_.py +8 -7
  180. luminarycloud/params/simulation/physics/heat/adjoint_controls_heat_.py +3 -4
  181. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_convection_.py +5 -6
  182. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_heat_flux_.py +4 -5
  183. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_integrated_heat_flux_.py +4 -5
  184. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_symmetry_.py +3 -4
  185. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_temperature_.py +4 -5
  186. luminarycloud/params/simulation/physics/heat/boundary_conditions_heat_.py +3 -4
  187. luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type/heat_source_type_power_.py +4 -5
  188. luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type/heat_source_type_power_per_unit_of_volume_.py +4 -5
  189. luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type_.py +3 -4
  190. luminarycloud/params/simulation/physics/heat/heat_source_.py +3 -4
  191. luminarycloud/params/simulation/physics/heat/initialization/heat_existing_solution_.py +3 -4
  192. luminarycloud/params/simulation/physics/heat/initialization/heat_prescribed_values_.py +4 -5
  193. luminarycloud/params/simulation/physics/heat/initialization_heat_.py +3 -4
  194. luminarycloud/params/simulation/physics/heat/solution_controls/heat_relaxation_method/heat_implicit_relaxation_.py +4 -5
  195. luminarycloud/params/simulation/physics/heat/solution_controls/heat_relaxation_method_.py +3 -4
  196. luminarycloud/params/simulation/physics/heat/solution_controls_heat_.py +4 -5
  197. luminarycloud/params/simulation/physics/heat/spatial_discretization_heat_.py +5 -6
  198. luminarycloud/params/simulation/physics/heat_.py +5 -5
  199. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/rotational_periodicity_.py +3 -4
  200. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type/translational_periodicity_.py +3 -4
  201. luminarycloud/params/simulation/physics/periodic_pair/periodicity_type_.py +3 -4
  202. luminarycloud/params/simulation/physics/periodic_pair_.py +3 -4
  203. luminarycloud/params/simulation/physics/solution_controls/linear_solver_type/gauss_seidel_.py +3 -4
  204. luminarycloud/params/simulation/physics/solution_controls/linear_solver_type/krylov_amg_.py +4 -5
  205. luminarycloud/params/simulation/physics/solution_controls/linear_solver_type_.py +4 -5
  206. luminarycloud/params/simulation/physics_.py +6 -5
  207. luminarycloud/params/simulation/simulation_param_.py +3 -4
  208. luminarycloud/params/simulation/sliding_interfaces_.py +3 -4
  209. luminarycloud/params/simulation/surface_name_.py +3 -4
  210. luminarycloud/params/simulation/time/compute_statistics/compute_statistics_off_.py +3 -4
  211. luminarycloud/params/simulation/time/compute_statistics/compute_statistics_on_.py +3 -4
  212. luminarycloud/params/simulation/time/compute_statistics_.py +3 -4
  213. luminarycloud/params/simulation/time/time_marching/time_explicit_.py +3 -4
  214. luminarycloud/params/simulation/time/time_marching/time_implicit_.py +3 -4
  215. luminarycloud/params/simulation/time/time_marching_.py +3 -4
  216. luminarycloud/params/simulation/time/time_step_ramp/time_step_ramp_off_.py +3 -4
  217. luminarycloud/params/simulation/time/time_step_ramp/time_step_ramp_on_.py +4 -5
  218. luminarycloud/params/simulation/time/time_step_ramp_.py +3 -4
  219. luminarycloud/params/simulation/time_.py +4 -5
  220. luminarycloud/params/simulation/volume_entity_.py +6 -5
  221. luminarycloud/physics_ai/__init__.py +2 -0
  222. luminarycloud/physics_ai/architectures.py +42 -8
  223. luminarycloud/physics_ai/inference.py +0 -2
  224. luminarycloud/physics_ai/models.py +44 -15
  225. luminarycloud/project.py +54 -3
  226. luminarycloud/simulation_template.py +21 -6
  227. luminarycloud/solution.py +1 -1
  228. luminarycloud/types/__init__.py +2 -0
  229. luminarycloud/types/adfloat.py +50 -8
  230. luminarycloud/types/ids.py +2 -0
  231. luminarycloud/vis/__init__.py +12 -2
  232. luminarycloud/vis/data_extraction.py +546 -0
  233. luminarycloud/vis/display.py +61 -6
  234. luminarycloud/vis/filters.py +199 -43
  235. luminarycloud/vis/interactive_scene.py +1 -0
  236. luminarycloud/vis/primitives.py +38 -0
  237. luminarycloud/vis/vis_util.py +56 -0
  238. luminarycloud/vis/visualization.py +224 -70
  239. {luminarycloud-0.15.3.dist-info → luminarycloud-0.15.5.dist-info}/METADATA +2 -1
  240. {luminarycloud-0.15.3.dist-info → luminarycloud-0.15.5.dist-info}/RECORD +241 -235
  241. luminarycloud/_proto/luminarycloud/luminarycloud_api.pb +0 -0
  242. {luminarycloud-0.15.3.dist-info → luminarycloud-0.15.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,546 @@
1
+ # Copyright 2023-2024 Luminary Cloud, Inc. All Rights Reserved.
2
+ import zstandard as zstd
3
+ import csv
4
+ import json
5
+ from .vis_util import _download_file, _InternalToken, generate_id, _get_status
6
+ from ..enum import ExtractStatusType, EntityType
7
+ from typing import List, Tuple, cast, Union
8
+ from abc import ABC, abstractmethod
9
+ from .._proto.api.v0.luminarycloud.vis import vis_pb2
10
+ from .primitives import Plane
11
+ from ..types.vector3 import _to_vector3
12
+ from .._client import get_default_client
13
+ import logging
14
+ from ..solution import Solution
15
+ from ..geometry import Geometry, get_geometry
16
+ from ..mesh import Mesh, get_mesh, get_mesh_metadata
17
+ from ..simulation import get_simulation
18
+ from .._helpers._get_project_id import _get_project_id
19
+ from .display import DisplayAttributes
20
+ from time import sleep, time
21
+ from luminarycloud.params.simulation.physics.fluid.boundary_conditions import Farfield
22
+ from ..exceptions import NotFoundError
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class DataExtract(ABC):
28
+ """
29
+ This is the base class for all data extracts. Each derived extract class
30
+ is responsible for providing a _to_proto method to convert to a filter
31
+ protobuf.
32
+
33
+ Attributes
34
+ ----------
35
+ id: str
36
+ A automatically generated uniqiue filter id.
37
+
38
+ .. warning:: This feature is experimental and may change or be removed in the future.
39
+ """
40
+
41
+ def __init__(self, id: str) -> None:
42
+ self.id = id
43
+ self._parent_id: str = ""
44
+
45
+ @abstractmethod
46
+ def _to_proto(self) -> vis_pb2.Filter:
47
+ pass
48
+
49
+
50
+ class IntersectionCurve(DataExtract):
51
+ """
52
+
53
+ Generate line data by computing intersections between solution surfaces and a slice plane.
54
+
55
+ Extracts 1D curves where surfaces intersect the specified cutting plane, preserving
56
+ solution field values at intersection points.
57
+
58
+ .. warning:: This feature is experimental and may change or be removed in the future.
59
+
60
+ Attributes:
61
+ -----------
62
+ plane : Plane
63
+ The slice plane.
64
+ name : str
65
+ A user provided name for the filter.
66
+ """
67
+
68
+ def __init__(self, name: str) -> None:
69
+ super().__init__(generate_id("intersection-curve"))
70
+ self.name = name
71
+ self._surface_names: List[str] = []
72
+ self._plane = Plane()
73
+ self.label: str = ""
74
+
75
+ @property
76
+ def plane(self) -> Plane:
77
+ return self._plane
78
+
79
+ @plane.setter
80
+ def plane(self, new_plane: Plane) -> None:
81
+ if not isinstance(new_plane, Plane):
82
+ raise TypeError(f"Expected 'Plane', got {type(new_plane).__name__}")
83
+ self._plane = new_plane
84
+
85
+ def add_surface(self, id: str) -> None:
86
+ """
87
+ Add a surface to compute the intersection curve on. Adding no
88
+ surfaces indicates that all surfaces will be used. The id can
89
+ either be a tag or explicit surface id. These values will be
90
+ validated by the DataExtractor before sending the request.
91
+
92
+ Parameters
93
+ ----------
94
+ id: str
95
+ A surface id or a tag id.
96
+ """
97
+ if not isinstance(id, str):
98
+ raise TypeError(f"Expected 'str', got {type(id).__name__}")
99
+ self._surface_names.append(id)
100
+
101
+ def _surfaces(self) -> List[str]:
102
+ """
103
+ Returns the current list of surfaces.
104
+ """
105
+ return self._surface_names
106
+
107
+ def _to_proto(self) -> vis_pb2.Filter:
108
+ vis_filter = vis_pb2.Filter()
109
+ vis_filter.id = self.id
110
+ vis_filter.name = self.name
111
+ vis_filter.intersection_curve.label = self.label
112
+
113
+ for id in self._surface_names:
114
+ vis_filter.intersection_curve.surfaces.append(id)
115
+
116
+ vis_filter.intersection_curve.plane.origin.CopyFrom(
117
+ _to_vector3(self.plane.origin)._to_proto()
118
+ )
119
+ vis_filter.intersection_curve.plane.normal.CopyFrom(
120
+ _to_vector3(self.plane.normal)._to_proto()
121
+ )
122
+ return vis_filter
123
+
124
+
125
+ class ExtractOutput:
126
+ """
127
+ The extract output represents the request to extract data from a solution,
128
+ and is contructed by the DataExtractor class. The operation exectutes
129
+ asyncronously, so the caller must check the status of the data extract. If
130
+ the status is completed, then the resuling data is available for download.
131
+
132
+ .. warning:: This class should not be directly instantiated by users.
133
+
134
+ .. warning:: This feature is experimental and may change or be removed in the future.
135
+
136
+ Attributes:
137
+ -----------
138
+ name: str
139
+ The user provided name of the extract.
140
+ description: str
141
+ The user provided description of the extract.
142
+ status: ExtractStatusType
143
+ The status of the extract (i.e., has it completed or not).
144
+ _extract_id: str
145
+ The unique indentifier of the extract.
146
+ _project_id: str
147
+ The project id associated with the extract.
148
+ _deleted: bool
149
+ Internal flag to track if the extract has been deleted.
150
+ """
151
+
152
+ def __init__(self, factory_token: _InternalToken):
153
+ if not isinstance(factory_token, _InternalToken):
154
+ raise ValueError("This class can only be constructed through the Scene class")
155
+
156
+ self._extract_id: str = ""
157
+ self._project_id: str = ""
158
+ self.status: ExtractStatusType = ExtractStatusType.INVALID
159
+ self.name: str = ""
160
+ self.description: str = ""
161
+ self._deleted = False
162
+
163
+ def _set_data(
164
+ self,
165
+ extract_id: str,
166
+ project_id: str,
167
+ name: str,
168
+ desciption: str,
169
+ status: ExtractStatusType,
170
+ ) -> None:
171
+ self._extract_id = extract_id
172
+ self._project_id = project_id
173
+ self.status = status
174
+ self.name = name
175
+ self.description = desciption
176
+
177
+ def __repr__(self) -> str:
178
+ return f"ExtractOutput (Id: {self._extract_id} status: {self.status})"
179
+
180
+ def refresh(self) -> "ExtractOutput":
181
+ """
182
+ Refesh the status of the ExtractOutput.
183
+
184
+ Returns
185
+ -------
186
+ self
187
+ """
188
+ self._fail_if_deleted()
189
+ self.status = _get_status(self._project_id, self._extract_id)
190
+ return self
191
+
192
+ def wait(
193
+ self, interval_seconds: float = 4, timeout_seconds: float = float("inf")
194
+ ) -> ExtractStatusType:
195
+ """
196
+ Wait until the ExtractOutput is completed or failed.
197
+
198
+ Parameters
199
+ ----------
200
+ interval : float, optional
201
+ Number of seconds between polls.
202
+ timeout : float, optional
203
+ Number of seconds before timeout.
204
+
205
+ Returns
206
+ -------
207
+ ExtractStatusType: Current status of the image extract.
208
+ """
209
+ self._fail_if_deleted()
210
+ deadline = time() + timeout_seconds
211
+ while True:
212
+ self.refresh()
213
+
214
+ if self.status in [
215
+ ExtractStatusType.COMPLETED,
216
+ ExtractStatusType.FAILED,
217
+ ExtractStatusType.INVALID,
218
+ ]:
219
+ return self.status
220
+ if time() >= deadline:
221
+ logger.error("`ExtractOutput: wait ` timed out.")
222
+ raise TimeoutError
223
+ sleep(max(-1, min(interval_seconds, deadline - time())))
224
+
225
+ def download_data(self) -> List[Tuple[List[List[Union[str, int, float]]], str]]:
226
+ """
227
+ Downloads the resulting data into memory. This is useful
228
+ for plotting data in notebooks. If that status is not complete, an
229
+ error will be raised.
230
+
231
+ Returns:
232
+ A list of results for each extract added to the request. Each result is a tuple
233
+ where the first entry is a in-memory csv file (List[List[Union[str, int, float]]]).
234
+ The first row is the header followed by the data rows. The second entry of the tuple
235
+ is the label provided by the user for the DataExtract.
236
+
237
+ .. warning:: This feature is experimental and may change or be removed in the future.
238
+
239
+ """
240
+ self._fail_if_deleted()
241
+ self.refresh()
242
+ if self.status != ExtractStatusType.COMPLETED:
243
+ raise Exception("download_data: status not complete.")
244
+ req = vis_pb2.DownloadExtractRequest()
245
+ req.extract_id = self._extract_id
246
+ req.project_id = self._project_id
247
+ res: vis_pb2.DownloadExtractResponse = get_default_client().DownloadExtract(req)
248
+
249
+ csv_files: List[Tuple[List[List[Union[str, int, float]]], str]] = []
250
+ if res.HasField("line_data"):
251
+ compressed_buffer = _download_file(res.line_data)
252
+ dctx = zstd.ZstdDecompressor()
253
+ decompressed_size = zstd.frame_content_size(compressed_buffer.getvalue())
254
+ serializedTable = dctx.decompress(
255
+ compressed_buffer.getvalue(), max_output_size=decompressed_size
256
+ )
257
+ line_data = vis_pb2.LineDataExtract()
258
+ # If uncompressed: this is
259
+ # line_data.ParseFromString(line_data_buffer.read())
260
+ line_data.ParseFromString(serializedTable)
261
+ ids = line_data.lines.keys()
262
+ # Each filter(id) produces a set of tables, one per line segment.
263
+ for id in ids:
264
+ header: List[Union[str, float, int]] = []
265
+ tables = line_data.lines[id]
266
+ # One table per line segment. First, figure out the
267
+ # shape of the data and validate what we expect.
268
+ total_rows = 0
269
+ n_cols = 0
270
+ for _, table in enumerate(tables.lines_table):
271
+ assert len(table.axis) == 1
272
+ assert len(table.record) == 1
273
+ n_rows = len(table.axis[0].coordinate)
274
+ total_rows += n_rows
275
+ n_cols = len(table.header.record_label)
276
+ if len(header) == 0:
277
+ for _, label in enumerate(table.header.record_label):
278
+ header.append(label.name)
279
+ # We also have a curve id we need to add.
280
+ header.append("curve id")
281
+ # verify what we expect to see
282
+ assert n_rows * n_cols == len(table.record[0].entry)
283
+ assert len(header) != 0
284
+ assert n_cols != 0
285
+ rows: List[List[Union[str, float, int]]] = []
286
+ rows.append(header)
287
+ for curve_id, table in enumerate(tables.lines_table):
288
+ n_rows = len(table.axis[0].coordinate)
289
+ new_rows: List[List[Union[str, float, int]]] = []
290
+ idx = 0
291
+ # The the shape of the values are in row-major ordering.
292
+ for r in range(n_rows):
293
+ row: List[Union[str, float, int]] = []
294
+ for c in range(n_cols):
295
+ row.append(table.record[0].entry[idx].adfloat.value)
296
+ idx += 1
297
+ new_rows.append(row)
298
+ # Now add the curve id to all the rows.
299
+ for row in new_rows:
300
+ row.append(curve_id)
301
+ rows = rows + new_rows
302
+ csv_files.append((rows, line_data.labels[id]))
303
+ return csv_files
304
+
305
+ def save_files(self, file_prefix: str, write_labels: bool = False) -> None:
306
+ """
307
+ A helper for downloading and save resulting csv files to the file system. If that status is not
308
+ complete, an error will be raised. csv_files will be of the form {file_prefix}_{index}.csv.
309
+ Optionally, a file will be written containing a list of file names and image labels. Labels
310
+ are an optional field in the DataExtracts.
311
+
312
+ .. warning:: This feature is experimental and may change or be removed in the future.
313
+
314
+ Parameters
315
+ ----------
316
+ file_prefix: str, required
317
+ The file prefix to save the extract. A file index and '.csv' will be
318
+ appended to the file names.
319
+ write_labels: bool, optional
320
+ Write a json file containing a list of csv file names and labels,
321
+ if True. The resulting json file is named '{file_prefix}.json' Default: False
322
+ """
323
+ if not file_prefix:
324
+ raise ValueError("file_prefix must be non-empty")
325
+
326
+ csv_files = self.download_data()
327
+ names_labels: List[Tuple[str, str]] = []
328
+ counter = 0
329
+ for csv_file in csv_files:
330
+ output_file = f"{file_prefix}_{counter}.csv"
331
+ with open(output_file, "w", newline="") as file:
332
+ writer = csv.writer(file)
333
+ writer.writerows(csv_file[0])
334
+ counter = counter + 1
335
+ names_labels.append((output_file, csv_file[1]))
336
+ if write_labels:
337
+ with open(f"{file_prefix}.json", "w") as json_file:
338
+ json.dump(names_labels, json_file, indent=1)
339
+
340
+ def _fail_if_deleted(self) -> None:
341
+ if self._deleted:
342
+ raise ValueError("RenderOutput has been deleted.")
343
+
344
+ def delete(self) -> None:
345
+ """Delete the the extracts."""
346
+ self._fail_if_deleted()
347
+ req = vis_pb2.DeleteExtractRequest()
348
+ req.extract_id = self._extract_id
349
+ req.project_id = self._project_id
350
+ get_default_client().DeleteExtract(req)
351
+ self._deleted = True
352
+
353
+
354
+ class DataExtractor:
355
+ """
356
+ I extract data from solutions.
357
+
358
+ .. warning:: This feature is experimental and may change or be removed in the future.
359
+
360
+ """
361
+
362
+ def __init__(self, solution: Solution):
363
+ if not isinstance(solution, Solution):
364
+ raise TypeError(f"Expected Solution got {type(solution).__name__}")
365
+ self._solution: Solution = solution
366
+ self._entity_type: EntityType = EntityType.SIMULATION
367
+ self._extracts: List[DataExtract] = []
368
+
369
+ # Meshes that are directly uploaded will not have tags.
370
+ self._has_tags: bool = True
371
+
372
+ project_id = _get_project_id(solution)
373
+ if not project_id:
374
+ raise ValueError("Unable to get project id from solution")
375
+
376
+ self._project_id = project_id
377
+
378
+ # Trace each entity all the way back to the geometry so we
379
+ # can accesss the tags, if they are present.
380
+ geom: Geometry | None = None
381
+ simulation = get_simulation(self._solution.simulation_id)
382
+ mesh_meta = get_mesh_metadata(simulation.mesh_id)
383
+ mesh = get_mesh(simulation.mesh_id)
384
+ try:
385
+ geom = mesh.geometry_version().geometry()
386
+ except NotFoundError:
387
+ self._has_tags = False
388
+ except Exception as e:
389
+ raise ValueError("An unknow error occurred retrieving tags")
390
+
391
+ self._surface_ids: List[str] = []
392
+ for zone in mesh_meta.zones:
393
+ for bound in zone.boundaries:
394
+ self._surface_ids.append(bound.name)
395
+
396
+ self._tag_ids: List[str] = []
397
+ if geom and self._has_tags:
398
+ tags = geom.list_tags()
399
+ for tag in tags:
400
+ self._tag_ids.append(tag.id)
401
+
402
+ self.far_field_boundary_ids: List[str] = []
403
+
404
+ # Find all the far field surfaces if we can get the params.
405
+ params = simulation.get_parameters()
406
+ for physics in params.physics:
407
+ if physics.fluid:
408
+ for bc in physics.fluid.boundary_conditions:
409
+ if isinstance(bc, Farfield):
410
+ for bc_surface in bc.surfaces:
411
+ self.far_field_boundary_ids.append(bc_surface)
412
+
413
+ def _validate_surfaces_and_tags(self, ids: List[str]) -> List[str]:
414
+ """
415
+ Validate a list of ids as either tags or ids. Returns a list of invalid ids. If the
416
+ length of the list is zero, the input list is valid.
417
+ """
418
+ bad_ids: List[str] = []
419
+ for id in ids:
420
+ if id in self._tag_ids:
421
+ continue
422
+ if id not in self._surface_ids:
423
+ bad_ids.append(id)
424
+ return bad_ids
425
+
426
+ def surface_ids(self) -> List[str]:
427
+ """Get a list of all the surface ids associated with the solution."""
428
+ return self._surface_ids
429
+
430
+ def tag_ids(self) -> List[str]:
431
+ """Get a list of all the tag ids associated with the solution."""
432
+ return self._tag_ids
433
+
434
+ def add_data_extract(self, extract: DataExtract) -> None:
435
+ """
436
+ Add a data extract.
437
+ """
438
+ if not isinstance(extract, DataExtract):
439
+ raise TypeError(f"Expected 'Filter', got {type(extract).__name__}")
440
+ self._extracts.append(extract)
441
+
442
+ def _create_request(self, name: str, description: str) -> vis_pb2.CreateExtractRequest:
443
+ req = vis_pb2.CreateExtractRequest()
444
+
445
+ # We have to add a bunch of dummy params to get the request to go through the same
446
+ # path as filters.
447
+ req.spec.global_display_attributes.CopyFrom(DisplayAttributes()._to_proto())
448
+ req.spec.animation_properties
449
+ req.spec.data_only = True
450
+ for extract in self._extracts:
451
+ if isinstance(extract, IntersectionCurve):
452
+ # Validate surfaces names
453
+ icurve = cast(IntersectionCurve, extract)
454
+ bad_ids = self._validate_surfaces_and_tags(icurve._surface_names)
455
+ if len(bad_ids) != 0:
456
+ raise ValueError(f"IntersectionCurve has invalid surfaces: {bad_ids}")
457
+
458
+ if isinstance(extract, DataExtract):
459
+ vis_filter: vis_pb2.Filter = extract._to_proto()
460
+ req.spec.filters.append(vis_filter)
461
+ # Add dummy display attrs
462
+ req.spec.display_attributes[extract.id].CopyFrom(DisplayAttributes()._to_proto())
463
+ else:
464
+ raise TypeError(f"Expected 'filter', got {type(filter).__name__}")
465
+
466
+ req.project_id = self._project_id
467
+ req.spec.entity_type.simulation.id = self._solution.simulation_id
468
+ req.spec.entity_type.simulation.solution_id = self._solution.id
469
+ req.spec.name = name
470
+ req.spec.description = description
471
+ return req
472
+
473
+ def create_extracts(self, name: str, description: str) -> ExtractOutput:
474
+ """
475
+ Create a request to extract data from a solution.
476
+
477
+ Parameters
478
+ ----------
479
+ name : str
480
+ A short name for the the extracts.
481
+ description : str
482
+ A longer description of the extracts.
483
+ """
484
+ req: vis_pb2.CreateExtractRequest = self._create_request(name=name, description=description)
485
+ res: vis_pb2.CreateExtractResponse = get_default_client().CreateExtract(req)
486
+ extract_output = ExtractOutput(_InternalToken())
487
+ extract_output._set_data(
488
+ extract_id=res.extract.extract_id,
489
+ project_id=self._project_id,
490
+ name=name,
491
+ desciption=description,
492
+ status=ExtractStatusType(res.extract.status),
493
+ )
494
+ return extract_output
495
+
496
+
497
+ def list_data_extracts(solution: Solution) -> List[ExtractOutput]:
498
+ """
499
+ Lists all previously created data extract associated with a project and a solution.
500
+
501
+ .. warning:: This feature is experimental and may change or be removed in the future.
502
+
503
+ Parameters
504
+ ----------
505
+ project_id : str
506
+ The project id to query.
507
+ entity : Geometry | Mesh | Solution
508
+ Specifies what types of rendering extracts to list(e.g., geometry, mesh or solution).
509
+
510
+ """
511
+
512
+ # Find out what we are working on.
513
+ if not isinstance(solution, Solution):
514
+ raise TypeError(f"Expected Solution got {type(solution).__name__}")
515
+
516
+ entity_type = EntityType.SIMULATION
517
+ project_id = _get_project_id(solution)
518
+ if not project_id:
519
+ raise ValueError("Unable to get project id from solution")
520
+
521
+ req = vis_pb2.ListExtractsRequest()
522
+ req.project_id = project_id
523
+
524
+ sim_entity = cast(Solution, solution)
525
+ req.entity.simulation.id = sim_entity.simulation_id
526
+ req.entity.simulation.solution_id = sim_entity.id
527
+
528
+ # We are requesting data not images
529
+ req.data_only = True
530
+ res: vis_pb2.ListExtractsResponse = get_default_client().ListExtracts(req)
531
+
532
+ results: List[ExtractOutput] = []
533
+ for extract in res.extracts:
534
+ result = ExtractOutput(_InternalToken())
535
+ result._set_data(
536
+ extract_id=extract.extract_id,
537
+ project_id=extract.project_id,
538
+ name=extract.name,
539
+ desciption=extract.description,
540
+ status=ExtractStatusType(extract.status),
541
+ )
542
+ # This need to be fixed on the backend, but manually refreshing works for now.
543
+ result.refresh()
544
+ results.append(result)
545
+
546
+ return results
@@ -1,7 +1,9 @@
1
+ # Copyright 2023-2024 Luminary Cloud, Inc. All Rights Reserved.
1
2
  import dataclasses as dc
2
3
  from luminarycloud.enum import Representation, ColorMapPreset, FieldComponent, VisQuantity
3
4
  from .._proto.api.v0.luminarycloud.vis import vis_pb2
4
5
  from typing import Optional
6
+ import luminarycloud.enum.quantity_type as quantity_type
5
7
 
6
8
 
7
9
  @dc.dataclass
@@ -16,15 +18,33 @@ class Field:
16
18
 
17
19
  quantity: VisQuantity = VisQuantity.ABSOLUTE_PRESSURE
18
20
  """The quantity to color by."""
19
- component: FieldComponent = FieldComponent.X
21
+ component: FieldComponent = FieldComponent.MAGNITUDE
20
22
  """
21
23
  The component of the field to use, applicable to vector fields. If the field is a
22
- scalar, use the default X component.
24
+ scalar, the component field is ignored. Default: MAGNITUDE.
23
25
  """
24
26
 
25
27
  def __hash__(self) -> int:
26
28
  return hash((self.quantity, self.component))
27
29
 
30
+ def _to_proto(self) -> vis_pb2.Field:
31
+ field = vis_pb2.Field()
32
+ field.quantity_typ = self.quantity.value
33
+ if quantity_type._is_vector(self.quantity):
34
+ field.component = self.component.value
35
+ else:
36
+ field.component = vis_pb2.Field.COMPONENT_UNSPECIFIED
37
+ return field
38
+
39
+ def _from_proto(self, field: vis_pb2.Field) -> None:
40
+ self.quantity = VisQuantity(field.quantity_typ)
41
+ if quantity_type._is_vector(self.quantity):
42
+ if field.component == vis_pb2.Field.COMPONENT_UNSPECIFIED:
43
+ raise ValueError("vector field must specify a component.")
44
+ else:
45
+ self.component = FieldComponent(field.component)
46
+ # If its a scalar, just ignore the component.
47
+
28
48
 
29
49
  @dc.dataclass
30
50
  class DisplayAttributes:
@@ -56,10 +76,14 @@ class DisplayAttributes:
56
76
  attrs = vis_pb2.DisplayAttributes()
57
77
  attrs.visible = self.visible
58
78
  attrs.representation = self.representation.value
59
- attrs.field.component = self.field.component.value
60
- attrs.field.quantity_typ = self.field.quantity.value
79
+ attrs.field.CopyFrom(self.field._to_proto())
61
80
  return attrs
62
81
 
82
+ def _from_proto(self, attrs: vis_pb2.DisplayAttributes) -> None:
83
+ self.visible = attrs.visible
84
+ self.representation = Representation(attrs.representation)
85
+ self.field._from_proto(attrs.field)
86
+
63
87
 
64
88
  @dc.dataclass
65
89
  class DataRange:
@@ -167,8 +191,7 @@ class ColorMap:
167
191
 
168
192
  def _to_proto(self) -> vis_pb2.ColorMap:
169
193
  res: vis_pb2.ColorMap = vis_pb2.ColorMap()
170
- res.field.component = self.field.component.value
171
- res.field.quantity_typ = self.field.quantity.value
194
+ res.field.CopyFrom(self.field._to_proto())
172
195
  res.name = self.preset.value
173
196
  res.discretize = self.discretize
174
197
  res.n_colors = self.n_colors
@@ -188,3 +211,35 @@ class ColorMap:
188
211
  res.lower_left_anchor_location.y = self.appearance.lower_left_y
189
212
  res.text_size = self.appearance.text_size
190
213
  return res
214
+
215
+ 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
+ )
220
+ if color_map.HasField("range"):
221
+ self.data_range = DataRange(
222
+ min_value=color_map.range.min,
223
+ max_value=color_map.range.max,
224
+ )
225
+
226
+ self.preset = ColorMapPreset(color_map.name)
227
+
228
+ if color_map.HasField("discretize"):
229
+ self.discretize = color_map.discretize
230
+ if color_map.HasField("n_colors"):
231
+ self.n_colors = color_map.n_colors
232
+
233
+ self.appearance = ColorMapAppearance()
234
+ if color_map.HasField("visible"):
235
+ self.appearance.visible = color_map.visible
236
+ l_typ = color_map.WhichOneof("location")
237
+ if l_typ == "lower_left_anchor_location":
238
+ self.appearance.lower_left_x = color_map.lower_left_anchor_location.x
239
+ self.appearance.lower_left_y = color_map.lower_left_anchor_location.y
240
+ if color_map.HasField("text_size"):
241
+ self.appearance.text_size = int(color_map.text_size)
242
+ if color_map.HasField("width"):
243
+ self.appearance.width = color_map.width
244
+ if color_map.HasField("height"):
245
+ self.appearance.height = color_map.height