luminarycloud 0.22.2__py3-none-any.whl → 0.23.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 (267) hide show
  1. luminarycloud/__init__.py +9 -3
  2. luminarycloud/_client/client.py +1 -1
  3. luminarycloud/_client/retry_interceptor.py +13 -2
  4. luminarycloud/_helpers/_code_representation.py +1 -0
  5. luminarycloud/_helpers/_create_simulation.py +5 -1
  6. luminarycloud/_helpers/proto_decorator.py +46 -38
  7. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +214 -137
  8. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +152 -0
  9. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +103 -0
  10. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +40 -0
  11. luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2.py +25 -3
  12. luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2.pyi +30 -0
  13. luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2_grpc.py +34 -0
  14. luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2_grpc.pyi +12 -0
  15. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +155 -59
  16. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +252 -22
  17. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.py +102 -0
  18. luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.pyi +36 -0
  19. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.py +94 -71
  20. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.pyi +46 -0
  21. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.py +35 -0
  22. luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.pyi +16 -0
  23. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +25 -3
  24. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +32 -0
  25. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.py +34 -0
  26. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.pyi +12 -0
  27. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +68 -21
  28. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +119 -0
  29. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.py +33 -0
  30. luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.pyi +10 -0
  31. luminarycloud/_proto/client/simulation_pb2.py +405 -346
  32. luminarycloud/_proto/client/simulation_pb2.pyi +175 -21
  33. luminarycloud/_proto/clusterconfig/clusterconfig_pb2.py +273 -0
  34. luminarycloud/_proto/clusterconfig/clusterconfig_pb2.pyi +808 -0
  35. luminarycloud/_proto/geometry/geometry_pb2.pyi +1 -1
  36. luminarycloud/_proto/lcstatus/details/geometry/geometry_pb2.py +256 -0
  37. luminarycloud/_proto/lcstatus/details/geometry/geometry_pb2.pyi +472 -0
  38. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2.py +2 -2
  39. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.py +102 -0
  40. luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.pyi +36 -0
  41. luminarycloud/_proto/quantity/quantity_pb2.pyi +1 -1
  42. luminarycloud/enum/__init__.py +1 -0
  43. luminarycloud/enum/gpu_type.py +42 -7
  44. luminarycloud/enum/vis_enums.py +18 -0
  45. luminarycloud/geometry.py +81 -0
  46. luminarycloud/geometry_contacts.py +222 -0
  47. luminarycloud/meshing/mesh_generation_params.py +4 -4
  48. luminarycloud/meshing/sizing_strategy/__init__.py +0 -1
  49. luminarycloud/meshing/sizing_strategy/sizing_strategies.py +0 -20
  50. luminarycloud/params/geometry/shapes.py +137 -31
  51. luminarycloud/params/outputs/__init__.py +0 -2
  52. luminarycloud/params/outputs/output.py +17 -5
  53. luminarycloud/params/simulation/__init__.py +2 -0
  54. luminarycloud/params/simulation/adaptive_mesh_refinement/active_region_.py +1 -1
  55. luminarycloud/params/simulation/adaptive_mesh_refinement/boundary_layer_profile_.py +1 -1
  56. luminarycloud/params/simulation/adaptive_mesh_refinement_.py +1 -1
  57. luminarycloud/params/simulation/adjoint_.py +8 -5
  58. luminarycloud/params/simulation/basic/gravity/gravity_off_.py +1 -1
  59. luminarycloud/params/simulation/basic/gravity/gravity_on_.py +1 -1
  60. luminarycloud/params/simulation/basic/gravity_.py +1 -1
  61. luminarycloud/params/simulation/body_frame_.py +1 -1
  62. luminarycloud/params/simulation/entity_relationships/volume_material_relationship_.py +1 -1
  63. luminarycloud/params/simulation/entity_relationships/volume_physics_relationship_.py +1 -1
  64. luminarycloud/params/simulation/entity_relationships_.py +1 -1
  65. luminarycloud/params/simulation/general_.py +1 -1
  66. luminarycloud/params/simulation/material/fluid/boussinesq_approximation/boussinesq_off_.py +1 -1
  67. luminarycloud/params/simulation/material/fluid/boussinesq_approximation/boussinesq_on_.py +1 -1
  68. luminarycloud/params/simulation/material/fluid/boussinesq_approximation_.py +1 -1
  69. luminarycloud/params/simulation/material/fluid/material_model/__init__.py +2 -0
  70. luminarycloud/params/simulation/material/fluid/material_model/ideal_gas_.py +1 -1
  71. luminarycloud/params/simulation/material/fluid/material_model/incompressible_fluid_.py +1 -1
  72. luminarycloud/params/simulation/material/fluid/material_model/incompressible_fluid_with_energy_.py +1 -1
  73. luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend/__init__.py +2 -0
  74. luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend/real_gas_coolprop_.py +43 -0
  75. luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend/real_gas_polynomial_.py +53 -0
  76. luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend_.py +29 -0
  77. luminarycloud/params/simulation/material/fluid/material_model_.py +1 -1
  78. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/prescribed_conductivity_.py +1 -1
  79. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/prescribed_prandtl_number_.py +1 -1
  80. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/temperature_dependent_conductivity_.py +1 -1
  81. luminarycloud/params/simulation/material/fluid/thermal_conductivity_model_.py +1 -1
  82. luminarycloud/params/simulation/material/fluid/viscosity_model/prescribed_viscosity_.py +1 -1
  83. luminarycloud/params/simulation/material/fluid/viscosity_model/sutherland_.py +1 -1
  84. luminarycloud/params/simulation/material/fluid/viscosity_model/temperature_dependent_viscosity_.py +1 -1
  85. luminarycloud/params/simulation/material/fluid/viscosity_model_.py +1 -1
  86. luminarycloud/params/simulation/material/material_fluid_.py +78 -2
  87. luminarycloud/params/simulation/material/material_solid_.py +1 -1
  88. luminarycloud/params/simulation/material_entity_.py +1 -1
  89. luminarycloud/params/simulation/monitor_plane_.py +1 -1
  90. luminarycloud/params/simulation/motion_data/frame_transforms/no_transform_.py +1 -1
  91. luminarycloud/params/simulation/motion_data/frame_transforms/rotational_transform_.py +1 -1
  92. luminarycloud/params/simulation/motion_data/frame_transforms/translational_transform_.py +1 -1
  93. luminarycloud/params/simulation/motion_data/frame_transforms_.py +1 -1
  94. luminarycloud/params/simulation/motion_data/motion_type/constant_angular_motion_.py +1 -1
  95. luminarycloud/params/simulation/motion_data/motion_type/constant_translation_motion_.py +1 -1
  96. luminarycloud/params/simulation/motion_data/motion_type_.py +1 -1
  97. luminarycloud/params/simulation/motion_data_.py +1 -1
  98. luminarycloud/params/simulation/multi_physics_coupling_options_.py +1 -1
  99. luminarycloud/params/simulation/nonlinear_control/__init__.py +1 -0
  100. luminarycloud/params/simulation/nonlinear_control/nonlinear_control_system_.py +48 -0
  101. luminarycloud/params/simulation/nonlinear_control_.py +61 -0
  102. luminarycloud/params/simulation/output_.py +1 -1
  103. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection/actuator_disk_specify_normal_vector_.py +1 -1
  104. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection/actuator_disk_specify_rotation_angles_.py +1 -1
  105. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection_.py +1 -1
  106. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk_.py +1 -1
  107. luminarycloud/params/simulation/particle_group/particle_group_type/actuator_line_.py +1 -1
  108. luminarycloud/params/simulation/particle_group/particle_group_type/probe_points_.py +1 -1
  109. luminarycloud/params/simulation/particle_group/particle_group_type/source_points_.py +1 -1
  110. luminarycloud/params/simulation/particle_group/particle_group_type_.py +1 -1
  111. luminarycloud/params/simulation/particle_group_.py +1 -1
  112. luminarycloud/params/simulation/physics/fluid/adjoint_controls_fluid_.py +1 -1
  113. luminarycloud/params/simulation/physics/fluid/basic_fluid_.py +1 -1
  114. luminarycloud/params/simulation/physics/fluid/boundary_conditions/farfield_.py +1 -1
  115. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/fan_curve_inlet_.py +1 -1
  116. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mach_inlet_.py +1 -1
  117. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mass_flow_inlet_.py +1 -1
  118. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/total_pressure_inlet_.py +1 -1
  119. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_components_inlet_.py +1 -1
  120. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_magnitude_inlet_.py +1 -1
  121. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet_.py +1 -1
  122. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/fan_curve_outlet_.py +1 -1
  123. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_pressure_.py +1 -1
  124. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_target_corrected_mass_flow_rate_.py +1 -1
  125. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_target_mass_flow_rate_.py +1 -1
  126. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy_.py +1 -1
  127. luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet_.py +1 -1
  128. luminarycloud/params/simulation/physics/fluid/boundary_conditions/symmetry_.py +1 -1
  129. luminarycloud/params/simulation/physics/fluid/boundary_conditions/turbulence_boundary_conditions_.py +1 -1
  130. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/energy/prescribed_heat_flux_.py +1 -1
  131. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/energy/prescribed_temperature_.py +1 -1
  132. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/no_slip_.py +1 -1
  133. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/slip_.py +1 -1
  134. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/wall_model_.py +1 -1
  135. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/wall_energy_.py +1 -1
  136. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/wall_momentum_.py +1 -1
  137. luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall_.py +1 -1
  138. luminarycloud/params/simulation/physics/fluid/boundary_conditions_fluid_.py +1 -1
  139. luminarycloud/params/simulation/physics/fluid/initialization/__init__.py +1 -0
  140. luminarycloud/params/simulation/physics/fluid/initialization/fluid_existing_solution_.py +1 -1
  141. luminarycloud/params/simulation/physics/fluid/initialization/fluid_farfield_values_.py +14 -1
  142. luminarycloud/params/simulation/physics/fluid/initialization/fluid_initialization_per_zone_.py +48 -0
  143. luminarycloud/params/simulation/physics/fluid/initialization/fluid_prescribed_values_.py +14 -1
  144. luminarycloud/params/simulation/physics/fluid/initialization/turbulence_initialization_.py +1 -1
  145. luminarycloud/params/simulation/physics/fluid/initialization_fluid_.py +1 -1
  146. luminarycloud/params/simulation/physics/fluid/physical_behavior/blade_element_airfoil_data_.py +1 -1
  147. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_blade_element_.py +1 -1
  148. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_radial_distribution_.py +1 -1
  149. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_uniform_thrust_.py +1 -1
  150. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/fan_curve_internal_.py +1 -1
  151. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model_.py +1 -1
  152. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_line_model/actuator_line_blade_element_.py +1 -1
  153. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_line_model_.py +1 -1
  154. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/blade_element_params_.py +1 -1
  155. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model/general_acceleration_source_.py +1 -1
  156. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model/general_mass_source_.py +1 -1
  157. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model_.py +1 -1
  158. luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model_.py +1 -1
  159. luminarycloud/params/simulation/physics/fluid/physical_behavior_.py +1 -1
  160. luminarycloud/params/simulation/physics/fluid/porous_behavior_.py +1 -1
  161. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_explicit_relaxation_.py +1 -1
  162. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_auto_.py +1 -1
  163. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_off_.py +1 -1
  164. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_on_.py +1 -1
  165. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup_.py +1 -1
  166. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation_.py +1 -1
  167. luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method_.py +1 -1
  168. luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method/cfl_based_.py +1 -1
  169. luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method/fixed_pseudo_time_step_.py +1 -1
  170. luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method_.py +1 -1
  171. luminarycloud/params/simulation/physics/fluid/solution_controls_fluid_.py +1 -1
  172. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/ec2_.py +1 -1
  173. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/fds_.py +1 -1
  174. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/ld2_.py +1 -1
  175. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/rhie_chow_.py +1 -1
  176. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_.py +1 -1
  177. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order/first_order_.py +1 -1
  178. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order/second_order_.py +1 -1
  179. luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order_.py +1 -1
  180. luminarycloud/params/simulation/physics/fluid/spatial_discretization_fluid_.py +1 -1
  181. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/ddes_.py +1 -1
  182. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/ddes_vtm_.py +1 -1
  183. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/iddes_.py +1 -1
  184. luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation_.py +1 -1
  185. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/constants/custom_komega_sst_constants_.py +1 -1
  186. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/constants/default_komega_sst_constants_.py +1 -1
  187. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/komega_sst_constants_.py +1 -1
  188. luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst_.py +1 -1
  189. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/constants/custom_spalart_allmaras_constants_.py +1 -1
  190. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/constants/default_spalart_allmaras_constants_.py +1 -1
  191. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/spalart_allmaras_constants_.py +1 -1
  192. luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras_.py +1 -1
  193. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/amd_.py +1 -1
  194. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/sigma_.py +1 -1
  195. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/smagorinsky_.py +1 -1
  196. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/vreman_.py +1 -1
  197. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/wale_.py +1 -1
  198. luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model_.py +1 -1
  199. luminarycloud/params/simulation/physics/fluid/turbulence_.py +1 -1
  200. luminarycloud/params/simulation/physics/fluid_.py +1 -1
  201. luminarycloud/params/simulation/physics/heat/adjoint_controls_heat_.py +1 -1
  202. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_convection_.py +1 -1
  203. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_heat_flux_.py +1 -1
  204. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_integrated_heat_flux_.py +1 -1
  205. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_symmetry_.py +1 -1
  206. luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_temperature_.py +1 -1
  207. luminarycloud/params/simulation/physics/heat/boundary_conditions_heat_.py +1 -1
  208. luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type/heat_source_type_power_.py +1 -1
  209. luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type/heat_source_type_power_per_unit_of_volume_.py +1 -1
  210. luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type_.py +1 -1
  211. luminarycloud/params/simulation/physics/heat/heat_source_.py +1 -1
  212. luminarycloud/params/simulation/physics/heat/initialization/__init__.py +1 -0
  213. luminarycloud/params/simulation/physics/heat/initialization/heat_existing_solution_.py +1 -1
  214. luminarycloud/params/simulation/physics/heat/initialization/heat_prescribed_values/__init__.py +1 -0
  215. luminarycloud/params/simulation/physics/heat/initialization/heat_prescribed_values/heat_initialization_per_zone_.py +40 -0
  216. luminarycloud/params/simulation/physics/heat/initialization/heat_prescribed_values_.py +14 -1
  217. luminarycloud/params/simulation/physics/heat/initialization_heat_.py +1 -1
  218. luminarycloud/params/simulation/physics/heat/solution_controls/heat_relaxation_method/heat_implicit_relaxation_.py +1 -1
  219. luminarycloud/params/simulation/physics/heat/solution_controls/heat_relaxation_method_.py +1 -1
  220. luminarycloud/params/simulation/physics/heat/solution_controls_heat_.py +1 -1
  221. luminarycloud/params/simulation/physics/heat/spatial_discretization_heat_.py +1 -1
  222. luminarycloud/params/simulation/physics/heat_.py +1 -1
  223. luminarycloud/params/simulation/physics/periodic_pair_.py +5 -1
  224. luminarycloud/params/simulation/physics/solution_controls/linear_solver_type/gauss_seidel_.py +1 -1
  225. luminarycloud/params/simulation/physics/solution_controls/linear_solver_type/krylov_amg_.py +1 -1
  226. luminarycloud/params/simulation/physics/solution_controls/linear_solver_type_.py +1 -1
  227. luminarycloud/params/simulation/physics_.py +1 -1
  228. luminarycloud/params/simulation/simulation_param_.py +8 -1
  229. luminarycloud/params/simulation/sliding_interfaces_.py +1 -1
  230. luminarycloud/params/simulation/surface_name_.py +1 -1
  231. luminarycloud/params/simulation/time/compute_statistics/compute_statistics_off_.py +1 -1
  232. luminarycloud/params/simulation/time/compute_statistics/compute_statistics_on_.py +1 -1
  233. luminarycloud/params/simulation/time/compute_statistics_.py +1 -1
  234. luminarycloud/params/simulation/time/time_marching/time_explicit_.py +1 -1
  235. luminarycloud/params/simulation/time/time_marching/time_implicit_.py +1 -1
  236. luminarycloud/params/simulation/time/time_marching_.py +1 -1
  237. luminarycloud/params/simulation/time/time_step_ramp/time_step_ramp_off_.py +1 -1
  238. luminarycloud/params/simulation/time/time_step_ramp/time_step_ramp_on_.py +1 -1
  239. luminarycloud/params/simulation/time/time_step_ramp_.py +1 -1
  240. luminarycloud/params/simulation/time_.py +1 -1
  241. luminarycloud/params/simulation/volume_entity_.py +1 -1
  242. luminarycloud/physics_ai/__init__.py +17 -0
  243. luminarycloud/physics_ai/architectures.py +130 -18
  244. luminarycloud/physics_ai/datasets.py +301 -0
  245. luminarycloud/physics_ai/training_jobs.py +50 -4
  246. luminarycloud/pipelines/__init__.py +16 -4
  247. luminarycloud/pipelines/api.py +192 -17
  248. luminarycloud/pipelines/arguments.py +8 -3
  249. luminarycloud/pipelines/core.py +296 -45
  250. luminarycloud/pipelines/flowables.py +138 -0
  251. luminarycloud/pipelines/stages.py +34 -55
  252. luminarycloud/pipelines/user_code_validation.py +122 -0
  253. luminarycloud/project.py +0 -14
  254. luminarycloud/simulation.py +42 -11
  255. luminarycloud/simulation_param.py +0 -62
  256. luminarycloud/simulation_template.py +1 -11
  257. luminarycloud/types/adfloat.py +48 -4
  258. luminarycloud/types/matrix3.py +9 -9
  259. luminarycloud/types/vector3.py +52 -23
  260. luminarycloud/vis/__init__.py +3 -0
  261. luminarycloud/vis/visualization.py +88 -0
  262. luminarycloud/volume_selection.py +29 -17
  263. {luminarycloud-0.22.2.dist-info → luminarycloud-0.23.0.dist-info}/METADATA +1 -1
  264. {luminarycloud-0.22.2.dist-info → luminarycloud-0.23.0.dist-info}/RECORD +266 -249
  265. luminarycloud/params/outputs/residual_output.py +0 -24
  266. /luminarycloud/params/{simulation/_lib.py → _lib.py} +0 -0
  267. {luminarycloud-0.22.2.dist-info → luminarycloud-0.23.0.dist-info}/WHEEL +0 -0
@@ -1,12 +1,28 @@
1
1
  # Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
2
+ from __future__ import annotations
2
3
  from abc import ABC, abstractmethod
3
4
  from dataclasses import is_dataclass, fields
4
- from typing import Any, Type, TypeVar, Generic
5
+ from typing import Any, Callable, Mapping, Type, TypeVar, Generic, TYPE_CHECKING
6
+ from .user_code_validation import validate_portable_top_level_function
5
7
  from typing_extensions import Self
8
+ import builtins
9
+ import inspect
6
10
  import re
11
+ import textwrap
7
12
  import yaml
8
13
 
9
14
  from ..pipeline_util.yaml import ensure_yamlizable
15
+ from .flowables import (
16
+ PipelineOutput,
17
+ PipelineInput,
18
+ FlowableType,
19
+ flowable_class_to_name,
20
+ flowable_name_to_class,
21
+ FlowableIOSchema,
22
+ )
23
+
24
+ if TYPE_CHECKING:
25
+ from .arguments import PipelineArgValueType
10
26
 
11
27
 
12
28
  class PipelineParameterRegistry:
@@ -86,40 +102,12 @@ class PipelineParameter(ABC):
86
102
  return self.__hash__() == other.__hash__()
87
103
 
88
104
 
89
- class PipelineInput:
90
- """
91
- A named input for a Stage. Explicitly connected to a PipelineOutput.
92
- """
93
-
94
- def __init__(self, upstream_output: "PipelineOutput", owner: "Stage", name: str):
95
- self.upstream_output = upstream_output
96
- self.owner = owner
97
- self.name = name
98
-
99
- def _to_dict(self, id_for_stage: dict) -> dict:
100
- if self.upstream_output.owner not in id_for_stage:
101
- raise ValueError(
102
- f"Stage {self.owner} depends on a stage, {self.upstream_output.owner}, that isn't in the Pipeline. Did you forget to add it?"
103
- )
104
- upstream_stage_id = id_for_stage[self.upstream_output.owner]
105
- upstream_output_name = self.upstream_output.name
106
- return {self.name: f"{upstream_stage_id}.{upstream_output_name}"}
107
-
108
-
109
- class PipelineOutput(ABC):
105
+ class StopRun(RuntimeError):
110
106
  """
111
- A named output for a Stage. Can be used to spawn any number of connected PipelineInputs.
107
+ Raised by RunScript code to indicate that the pipeline run should stop intentionally.
112
108
  """
113
109
 
114
- def __init__(self, owner: "Stage", name: str):
115
- self.owner = owner
116
- self.name = name
117
- self.downstream_inputs: list[PipelineInput] = []
118
-
119
- def _spawn_input(self, owner: "Stage", name: str) -> PipelineInput:
120
- input = PipelineInput(self, owner, name)
121
- self.downstream_inputs.append(input)
122
- return input
110
+ pass
123
111
 
124
112
 
125
113
  class StageInputs:
@@ -187,11 +175,52 @@ class StageOutputs(ABC):
187
175
  return inputs
188
176
 
189
177
 
178
+ class DynamicStageOutputs(StageOutputs):
179
+ def __init__(self, owner: "RunScript", output_types: dict[str, FlowableType]):
180
+ self.owner = owner
181
+ self._order = list(output_types.keys())
182
+ self.outputs: dict[str, PipelineOutput] = {}
183
+ for name in self._order:
184
+ output_type = output_types[name]
185
+ output_cls = flowable_name_to_class(output_type)
186
+ self.outputs[name] = output_cls(owner, name)
187
+
188
+ def downstream_inputs(self) -> list[PipelineInput]:
189
+ inputs = []
190
+ for output in self.outputs.values():
191
+ inputs.extend(output.downstream_inputs)
192
+ return inputs
193
+
194
+ def __getattr__(self, name: str) -> PipelineOutput:
195
+ return self.outputs[name]
196
+
197
+ def __getitem__(self, key: int | str) -> PipelineOutput:
198
+ if isinstance(key, int):
199
+ name = self._order[key]
200
+ return self.outputs[name]
201
+ return self.outputs[key]
202
+
203
+ def __iter__(self):
204
+ return iter(self._order)
205
+
206
+ def __len__(self) -> int:
207
+ return len(self.outputs)
208
+
209
+ def keys(self):
210
+ return self.outputs.keys()
211
+
212
+ def values(self):
213
+ return self.outputs.values()
214
+
215
+ def items(self):
216
+ return self.outputs.items()
217
+
218
+
190
219
  class StageRegistry:
191
220
  def __init__(self):
192
221
  self.stages = {}
193
222
 
194
- def register(self, stage_class: Type["Stage"]) -> None:
223
+ def register(self, stage_class: Type["StandardStage"] | Type["RunScript"]) -> None:
195
224
  self.stages[stage_class.__name__] = stage_class
196
225
 
197
226
  def get(self, stage_type_name: str) -> Type["Stage"]:
@@ -203,7 +232,7 @@ class StageRegistry:
203
232
  TOutputs = TypeVar("TOutputs", bound=StageOutputs)
204
233
 
205
234
 
206
- class Stage(Generic[TOutputs], ABC):
235
+ class StandardStage(Generic[TOutputs], ABC):
207
236
  def __init__(
208
237
  self,
209
238
  stage_name: str | None,
@@ -268,7 +297,7 @@ class Stage(Generic[TOutputs], ABC):
268
297
 
269
298
  def __init_subclass__(cls, **kwargs):
270
299
  super().__init_subclass__(**kwargs)
271
- Stage._registry.register(cls)
300
+ StandardStage._registry.register(cls)
272
301
 
273
302
  @classmethod
274
303
  def _get_subclass(cls, stage_type_name: str) -> Type["Stage"]:
@@ -281,6 +310,212 @@ class Stage(Generic[TOutputs], ABC):
281
310
  return params
282
311
 
283
312
 
313
+ class RunScript:
314
+ """
315
+ RunScript is a stage that runs a user-provided Python function.
316
+
317
+ While you can instantiate a RunScript stage directly, the usual way to construct one is to
318
+ decorate a function with the `@stage` decorator.
319
+
320
+ Examples
321
+ --------
322
+ >>> @pipelines.stage(
323
+ ... inputs={"geometry": read_geo.outputs.geometry},
324
+ ... outputs={"geometry": pipelines.PipelineOutputGeometry},
325
+ ... )
326
+ ... def ensure_single_volume(geometry: lc.Geometry):
327
+ ... _, volumes = geometry.list_entities()
328
+ ... if len(volumes) != 1:
329
+ ... raise pipelines.StopRun("expected exactly one volume")
330
+ ... return {"geometry": geometry}
331
+ """
332
+
333
+ def __init__(
334
+ self,
335
+ script: Callable[..., dict[str, Any] | None] | str,
336
+ *,
337
+ stage_name: str | None = None,
338
+ inputs: dict[str, PipelineOutput] | None = None,
339
+ outputs: Mapping[str, type[PipelineOutput] | str] | None = None,
340
+ entrypoint: str | None = None,
341
+ params: dict[str, Any] | None = None,
342
+ ):
343
+ inputs = inputs or {}
344
+ params = params or {}
345
+ outputs = outputs or {}
346
+ overlapping = set(inputs.keys()).intersection(params.keys())
347
+ if overlapping:
348
+ overlap = ", ".join(sorted(overlapping))
349
+ raise ValueError(f"RunScript params and inputs cannot share names: {overlap}")
350
+
351
+ inputs_and_params = set(inputs.keys()).union(params.keys())
352
+ validated_source = validate_portable_top_level_function(
353
+ script, must_have_params=inputs_and_params, can_have_params={"context"}
354
+ )
355
+ self._stage_type_name = "RunScript"
356
+ self._entrypoint = entrypoint or validated_source.entrypoint
357
+ self._name = (
358
+ stage_name if stage_name is not None else self._default_stage_name(self._entrypoint)
359
+ )
360
+
361
+ for input_name, upstream_output in inputs.items():
362
+ if not isinstance(upstream_output, PipelineOutput):
363
+ raise TypeError(
364
+ f"Input '{input_name}' must be a PipelineOutput, got {type(upstream_output).__name__}"
365
+ )
366
+
367
+ stage_inputs_kwargs = {
368
+ input_name: (PipelineOutput, upstream_output)
369
+ for input_name, upstream_output in inputs.items()
370
+ }
371
+ self._inputs = StageInputs(self, **stage_inputs_kwargs)
372
+
373
+ input_types = {
374
+ input_name: flowable_class_to_name(type(upstream_output))
375
+ for input_name, upstream_output in inputs.items()
376
+ }
377
+ output_flowable_types = self._normalize_output_types(outputs)
378
+ self._io_schema = FlowableIOSchema(
379
+ inputs=input_types,
380
+ outputs=output_flowable_types,
381
+ )
382
+
383
+ self.outputs = DynamicStageOutputs(self, output_flowable_types)
384
+
385
+ reserved_params = {
386
+ "$script": validated_source.source,
387
+ "$output_types": {name: ft.value for name, ft in output_flowable_types.items()},
388
+ "$entrypoint": self._entrypoint,
389
+ }
390
+ user_params = dict(params or {})
391
+ invalid_param_names = ({"context"} | reserved_params.keys()).intersection(
392
+ user_params.keys()
393
+ )
394
+ if invalid_param_names:
395
+ invalid = ", ".join(sorted(invalid_param_names))
396
+ raise ValueError(f"RunScript params cannot use reserved names: {invalid}")
397
+ overlapping_input_names = set(inputs.keys()).intersection(user_params.keys())
398
+ if overlapping_input_names:
399
+ overlap = ", ".join(sorted(overlapping_input_names))
400
+ raise ValueError(f"RunScript params and inputs cannot share names: {overlap}")
401
+ if "context" in inputs.keys():
402
+ raise ValueError("RunScript inputs cannot include reserved name 'context'")
403
+
404
+ self._params = reserved_params | user_params
405
+ ensure_yamlizable(self._params_dict()[0], "RunScript parameters")
406
+
407
+ @staticmethod
408
+ def _default_stage_name(entrypoint: str) -> str:
409
+ words = entrypoint.replace("_", " ").split()
410
+ if not words:
411
+ return "RunScript"
412
+ return " ".join(word.capitalize() for word in words)
413
+
414
+ @staticmethod
415
+ def _normalize_output_types(
416
+ output_types: Mapping[str, type[PipelineOutput] | str | FlowableType],
417
+ ) -> dict[str, FlowableType]:
418
+ normalized: dict[str, FlowableType] = {}
419
+ for name, value in output_types.items():
420
+ if isinstance(value, FlowableType):
421
+ normalized[name] = value
422
+ elif isinstance(value, str):
423
+ normalized[name] = FlowableType(value)
424
+ elif isinstance(value, type) and issubclass(value, PipelineOutput):
425
+ normalized[name] = flowable_class_to_name(value)
426
+ else:
427
+ raise TypeError(
428
+ f"Output '{name}' must be a PipelineOutput subclass or flowable type string, got {value}"
429
+ )
430
+ return normalized
431
+
432
+ def is_source(self) -> bool:
433
+ return len(self._inputs.inputs) == 0
434
+
435
+ def inputs_dict(self) -> dict[str, tuple["Stage", str]]:
436
+ inputs: dict[str, tuple["Stage", str]] = {}
437
+ for pipeline_input in self._inputs.inputs:
438
+ inputs[pipeline_input.name] = (
439
+ pipeline_input.upstream_output.owner,
440
+ pipeline_input.upstream_output.name,
441
+ )
442
+ return inputs
443
+
444
+ def downstream_stages(self) -> list["Stage"]:
445
+ return [inp.owner for inp in self.outputs.downstream_inputs()]
446
+
447
+ def _params_dict(self) -> tuple[dict, set[PipelineParameter]]:
448
+ d: dict[str, Any] = {}
449
+ pipeline_params = set()
450
+ for name, value in self._params.items():
451
+ if hasattr(value, "_to_pipeline_dict"):
452
+ d[name], downstream_params = value._to_pipeline_dict()
453
+ for param in downstream_params:
454
+ if not isinstance(param, PipelineParameter):
455
+ raise ValueError(
456
+ f"Expected `_to_pipeline_dict()` to only return PipelineParameters, but got {type(param)}"
457
+ )
458
+ pipeline_params.update(downstream_params)
459
+ else:
460
+ d[name] = value
461
+ d = {k: v for k, v in d.items() if v is not None}
462
+ return d, pipeline_params
463
+
464
+ def _to_dict(self, id_for_task: dict) -> tuple[dict, set[PipelineParameter]]:
465
+ params, pipeline_params = self._params_dict()
466
+ d = {
467
+ "name": self._name,
468
+ "operator": self._stage_type_name,
469
+ "params": params,
470
+ "inputs": self._inputs._to_dict(id_for_task),
471
+ }
472
+ return d, pipeline_params
473
+
474
+ @classmethod
475
+ def _parse_params(cls, params: dict) -> dict:
476
+ return params
477
+
478
+
479
+ def stage(
480
+ *,
481
+ inputs: dict[str, PipelineOutput] | None = None,
482
+ outputs: dict[str, type[PipelineOutput]] | None = None,
483
+ stage_name: str | None = None,
484
+ params: dict[str, PipelineParameter | PipelineArgValueType] | None = None,
485
+ ) -> Callable[[Callable[..., dict[str, Any] | None]], RunScript]:
486
+ """
487
+ Decorator for building a RunScript stage from a Python function.
488
+
489
+ Examples
490
+ --------
491
+ >>> @pipelines.stage(
492
+ ... inputs={"geometry": read_geo.outputs.geometry},
493
+ ... outputs={"geometry": pipelines.PipelineOutputGeometry},
494
+ ... )
495
+ ... def ensure_single_volume(geometry: lc.Geometry):
496
+ ... _, volumes = geometry.list_entities()
497
+ ... if len(volumes) != 1:
498
+ ... raise pipelines.StopRun("expected exactly one volume")
499
+ ... return {"geometry": geometry}
500
+ """
501
+
502
+ def decorator(fn: Callable[..., dict[str, Any] | None]) -> RunScript:
503
+ return RunScript(
504
+ script=fn,
505
+ stage_name=stage_name,
506
+ inputs=inputs,
507
+ outputs=outputs,
508
+ params=params,
509
+ )
510
+
511
+ return decorator
512
+
513
+
514
+ StandardStage._registry.register(RunScript)
515
+
516
+ Stage = StandardStage | RunScript
517
+
518
+
284
519
  class Pipeline:
285
520
  def __init__(self, stages: list[Stage]):
286
521
  self.stages = stages
@@ -350,7 +585,7 @@ class Pipeline:
350
585
 
351
586
  # first, parse the pipeline parameters...
352
587
  parsed_params = {}
353
- for param_name, param_metadata in d.get("params", {}).items():
588
+ for param_name, param_metadata in (d.get("params") or {}).items():
354
589
  parsed_params[param_name] = PipelineParameter._get_subclass(param_metadata["type"])(
355
590
  param_name
356
591
  )
@@ -405,7 +640,7 @@ def _parse_stage(pipeline_dict: dict, stage_id: str, all_stages: dict[str, Stage
405
640
  stage_type_name = stage_dict[
406
641
  "operator"
407
642
  ] # TODO: change key to "stage_type" when we're ready to bump the yaml schema version
408
- stage_class = Stage._get_subclass(stage_type_name)
643
+ stage_class = StandardStage._get_subclass(stage_type_name)
409
644
 
410
645
  parsed_inputs = {}
411
646
  for input_name, input_value in stage_dict["inputs"].items():
@@ -414,13 +649,29 @@ def _parse_stage(pipeline_dict: dict, stage_id: str, all_stages: dict[str, Stage
414
649
  source_output = getattr(source_stage.outputs, source_output_name)
415
650
  parsed_inputs[input_name] = source_output
416
651
 
417
- parsed_params = stage_class._parse_params(stage_dict["params"])
418
-
419
- stage_params = {
420
- "stage_name": stage_dict["name"],
421
- **parsed_params,
422
- **parsed_inputs,
423
- }
424
- stage = stage_class(**stage_params)
652
+ parsed_params = stage_class._parse_params(stage_dict.get("params"))
653
+
654
+ if stage_class == RunScript:
655
+ user_params = parsed_params.copy()
656
+ script = user_params.pop("$script", None)
657
+ output_types = user_params.pop("$output_types", None)
658
+ entrypoint = user_params.pop("$entrypoint", None)
659
+ if script is None or output_types is None:
660
+ raise ValueError("RunScript stages must define both `$script` and `$output_types`")
661
+ stage = RunScript(
662
+ stage_name=stage_dict["name"],
663
+ script=script,
664
+ inputs=parsed_inputs,
665
+ outputs=output_types,
666
+ entrypoint=entrypoint,
667
+ params=user_params,
668
+ )
669
+ else:
670
+ stage_params = {
671
+ "stage_name": stage_dict["name"],
672
+ **parsed_params,
673
+ **parsed_inputs,
674
+ }
675
+ stage = stage_class(**stage_params)
425
676
  all_stages[stage_id] = stage
426
677
  return stage
@@ -0,0 +1,138 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC
4
+ from dataclasses import dataclass, field
5
+ from enum import Enum
6
+ from typing import TYPE_CHECKING, Type, Mapping
7
+
8
+ if TYPE_CHECKING:
9
+ from .core import Stage
10
+
11
+
12
+ class PipelineInput:
13
+ """
14
+ A named input for a Stage. Explicitly connected to a PipelineOutput.
15
+ """
16
+
17
+ def __init__(self, upstream_output: "PipelineOutput", owner: "Stage", name: str):
18
+ self.upstream_output = upstream_output
19
+ self.owner = owner
20
+ self.name = name
21
+
22
+ def _to_dict(self, id_for_stage: dict) -> dict:
23
+ if self.upstream_output.owner not in id_for_stage:
24
+ raise ValueError(
25
+ f"Stage {self.owner} depends on a stage, {self.upstream_output.owner}, that isn't in the Pipeline. Did you forget to add it?"
26
+ )
27
+ upstream_stage_id = id_for_stage[self.upstream_output.owner]
28
+ upstream_output_name = self.upstream_output.name
29
+ return {self.name: f"{upstream_stage_id}.{upstream_output_name}"}
30
+
31
+
32
+ class PipelineOutput(ABC):
33
+ """
34
+ A named output for a Stage. Can be used to spawn any number of connected PipelineInputs.
35
+ """
36
+
37
+ def __init__(self, owner: "Stage", name: str):
38
+ self.owner = owner
39
+ self.name = name
40
+ self.downstream_inputs: list[PipelineInput] = []
41
+
42
+ def _spawn_input(self, owner: "Stage", name: str) -> PipelineInput:
43
+ input = PipelineInput(self, owner, name)
44
+ self.downstream_inputs.append(input)
45
+ return input
46
+
47
+
48
+ # Concrete PipelineOutput classes, i.e. the things that can "flow" in a Pipeline
49
+
50
+
51
+ class PipelineOutputGeometry(PipelineOutput):
52
+ """A representation of a Geometry in a Pipeline."""
53
+
54
+ pass
55
+
56
+
57
+ class PipelineOutputMesh(PipelineOutput):
58
+ """A representation of a Mesh in a Pipeline."""
59
+
60
+ pass
61
+
62
+
63
+ class PipelineOutputSimulation(PipelineOutput):
64
+ """A representation of a Simulation in a Pipeline."""
65
+
66
+ pass
67
+
68
+
69
+ # We don't inherit from StrEnum because that was added in Python 3.11, but we still want to support
70
+ # older versions. Inheriting from str and Enum gives us the StrEnum-like behavior we want.
71
+ class FlowableType(str, Enum):
72
+ """Canonical flowable type identifiers."""
73
+
74
+ GEOMETRY = "Geometry"
75
+ MESH = "Mesh"
76
+ SIMULATION = "Simulation"
77
+
78
+ def __str__(self) -> str:
79
+ return self.value
80
+
81
+
82
+ _FLOWABLE_NAME_TO_CLASS: dict[FlowableType, Type[PipelineOutput]] = {
83
+ FlowableType.GEOMETRY: PipelineOutputGeometry,
84
+ FlowableType.MESH: PipelineOutputMesh,
85
+ FlowableType.SIMULATION: PipelineOutputSimulation,
86
+ }
87
+
88
+
89
+ def flowable_class_to_name(output_cls: Type[PipelineOutput]) -> FlowableType:
90
+ """
91
+ Convert a PipelineOutput subclass to the canonical flowable type name used in pipeline YAML.
92
+ """
93
+ for flowable_type, cls in _FLOWABLE_NAME_TO_CLASS.items():
94
+ if issubclass(output_cls, cls):
95
+ return flowable_type
96
+ raise ValueError(f"Unsupported PipelineOutput subclass: {output_cls.__name__}")
97
+
98
+
99
+ def flowable_name_to_class(name: str | FlowableType) -> Type[PipelineOutput]:
100
+ """
101
+ Convert a canonical flowable type name into the corresponding PipelineOutput subclass.
102
+ """
103
+ try:
104
+ flowable_type = FlowableType(name)
105
+ except ValueError as exc:
106
+ supported = ", ".join(ft.value for ft in FlowableType)
107
+ raise ValueError(
108
+ f"Unknown flowable type '{name}'. Supported types are: {supported}"
109
+ ) from exc
110
+ return _FLOWABLE_NAME_TO_CLASS[flowable_type]
111
+
112
+
113
+ def _ensure_flowable_mapping(data: Mapping[str, FlowableType | str]) -> dict[str, FlowableType]:
114
+ mapping: dict[str, FlowableType] = {}
115
+ for name, value in data.items():
116
+ mapping[name] = value if isinstance(value, FlowableType) else FlowableType(value)
117
+ return mapping
118
+
119
+
120
+ @dataclass(slots=True)
121
+ class FlowableIOSchema:
122
+ """Typed representation of RunScript input/output schema."""
123
+
124
+ inputs: dict[str, FlowableType] = field(default_factory=dict)
125
+ outputs: dict[str, FlowableType] = field(default_factory=dict)
126
+
127
+ @classmethod
128
+ def from_dict(cls, data: Mapping[str, Mapping[str, FlowableType | str]]) -> "FlowableIOSchema":
129
+ return cls(
130
+ inputs=_ensure_flowable_mapping(data["inputs"]),
131
+ outputs=_ensure_flowable_mapping(data["outputs"]),
132
+ )
133
+
134
+ def to_dict(self) -> dict[str, dict[str, str]]:
135
+ return {
136
+ "inputs": {name: flowable.value for name, flowable in self.inputs.items()},
137
+ "outputs": {name: flowable.value for name, flowable in self.outputs.items()},
138
+ }