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.
- luminarycloud/__init__.py +9 -3
- luminarycloud/_client/client.py +1 -1
- luminarycloud/_client/retry_interceptor.py +13 -2
- luminarycloud/_helpers/_code_representation.py +1 -0
- luminarycloud/_helpers/_create_simulation.py +5 -1
- luminarycloud/_helpers/proto_decorator.py +46 -38
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +214 -137
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +152 -0
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +103 -0
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +40 -0
- luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2.py +25 -3
- luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2.pyi +30 -0
- luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/named_variable_set/named_variable_set_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +155 -59
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +252 -22
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.py +102 -0
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.pyi +36 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.py +94 -71
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.pyi +46 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.py +35 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.pyi +16 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +25 -3
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +32 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +68 -21
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +119 -0
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.py +33 -0
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2_grpc.pyi +10 -0
- luminarycloud/_proto/client/simulation_pb2.py +405 -346
- luminarycloud/_proto/client/simulation_pb2.pyi +175 -21
- luminarycloud/_proto/clusterconfig/clusterconfig_pb2.py +273 -0
- luminarycloud/_proto/clusterconfig/clusterconfig_pb2.pyi +808 -0
- luminarycloud/_proto/geometry/geometry_pb2.pyi +1 -1
- luminarycloud/_proto/lcstatus/details/geometry/geometry_pb2.py +256 -0
- luminarycloud/_proto/lcstatus/details/geometry/geometry_pb2.pyi +472 -0
- luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2.py +2 -2
- luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.py +102 -0
- luminarycloud/_proto/physicsaitrainingservice/physicsaitrainingservice_pb2_grpc.pyi +36 -0
- luminarycloud/_proto/quantity/quantity_pb2.pyi +1 -1
- luminarycloud/enum/__init__.py +1 -0
- luminarycloud/enum/gpu_type.py +42 -7
- luminarycloud/enum/vis_enums.py +18 -0
- luminarycloud/geometry.py +81 -0
- luminarycloud/geometry_contacts.py +222 -0
- luminarycloud/meshing/mesh_generation_params.py +4 -4
- luminarycloud/meshing/sizing_strategy/__init__.py +0 -1
- luminarycloud/meshing/sizing_strategy/sizing_strategies.py +0 -20
- luminarycloud/params/geometry/shapes.py +137 -31
- luminarycloud/params/outputs/__init__.py +0 -2
- luminarycloud/params/outputs/output.py +17 -5
- luminarycloud/params/simulation/__init__.py +2 -0
- luminarycloud/params/simulation/adaptive_mesh_refinement/active_region_.py +1 -1
- luminarycloud/params/simulation/adaptive_mesh_refinement/boundary_layer_profile_.py +1 -1
- luminarycloud/params/simulation/adaptive_mesh_refinement_.py +1 -1
- luminarycloud/params/simulation/adjoint_.py +8 -5
- luminarycloud/params/simulation/basic/gravity/gravity_off_.py +1 -1
- luminarycloud/params/simulation/basic/gravity/gravity_on_.py +1 -1
- luminarycloud/params/simulation/basic/gravity_.py +1 -1
- luminarycloud/params/simulation/body_frame_.py +1 -1
- luminarycloud/params/simulation/entity_relationships/volume_material_relationship_.py +1 -1
- luminarycloud/params/simulation/entity_relationships/volume_physics_relationship_.py +1 -1
- luminarycloud/params/simulation/entity_relationships_.py +1 -1
- luminarycloud/params/simulation/general_.py +1 -1
- luminarycloud/params/simulation/material/fluid/boussinesq_approximation/boussinesq_off_.py +1 -1
- luminarycloud/params/simulation/material/fluid/boussinesq_approximation/boussinesq_on_.py +1 -1
- luminarycloud/params/simulation/material/fluid/boussinesq_approximation_.py +1 -1
- luminarycloud/params/simulation/material/fluid/material_model/__init__.py +2 -0
- luminarycloud/params/simulation/material/fluid/material_model/ideal_gas_.py +1 -1
- luminarycloud/params/simulation/material/fluid/material_model/incompressible_fluid_.py +1 -1
- luminarycloud/params/simulation/material/fluid/material_model/incompressible_fluid_with_energy_.py +1 -1
- luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend/__init__.py +2 -0
- luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend/real_gas_coolprop_.py +43 -0
- luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend/real_gas_polynomial_.py +53 -0
- luminarycloud/params/simulation/material/fluid/material_model/real_gas_backend_.py +29 -0
- luminarycloud/params/simulation/material/fluid/material_model_.py +1 -1
- luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/prescribed_conductivity_.py +1 -1
- luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/prescribed_prandtl_number_.py +1 -1
- luminarycloud/params/simulation/material/fluid/thermal_conductivity_model/temperature_dependent_conductivity_.py +1 -1
- luminarycloud/params/simulation/material/fluid/thermal_conductivity_model_.py +1 -1
- luminarycloud/params/simulation/material/fluid/viscosity_model/prescribed_viscosity_.py +1 -1
- luminarycloud/params/simulation/material/fluid/viscosity_model/sutherland_.py +1 -1
- luminarycloud/params/simulation/material/fluid/viscosity_model/temperature_dependent_viscosity_.py +1 -1
- luminarycloud/params/simulation/material/fluid/viscosity_model_.py +1 -1
- luminarycloud/params/simulation/material/material_fluid_.py +78 -2
- luminarycloud/params/simulation/material/material_solid_.py +1 -1
- luminarycloud/params/simulation/material_entity_.py +1 -1
- luminarycloud/params/simulation/monitor_plane_.py +1 -1
- luminarycloud/params/simulation/motion_data/frame_transforms/no_transform_.py +1 -1
- luminarycloud/params/simulation/motion_data/frame_transforms/rotational_transform_.py +1 -1
- luminarycloud/params/simulation/motion_data/frame_transforms/translational_transform_.py +1 -1
- luminarycloud/params/simulation/motion_data/frame_transforms_.py +1 -1
- luminarycloud/params/simulation/motion_data/motion_type/constant_angular_motion_.py +1 -1
- luminarycloud/params/simulation/motion_data/motion_type/constant_translation_motion_.py +1 -1
- luminarycloud/params/simulation/motion_data/motion_type_.py +1 -1
- luminarycloud/params/simulation/motion_data_.py +1 -1
- luminarycloud/params/simulation/multi_physics_coupling_options_.py +1 -1
- luminarycloud/params/simulation/nonlinear_control/__init__.py +1 -0
- luminarycloud/params/simulation/nonlinear_control/nonlinear_control_system_.py +48 -0
- luminarycloud/params/simulation/nonlinear_control_.py +61 -0
- luminarycloud/params/simulation/output_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection/actuator_disk_specify_normal_vector_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection/actuator_disk_specify_rotation_angles_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk/actuator_disk_orientation_selection_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type/actuator_disk_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type/actuator_line_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type/probe_points_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type/source_points_.py +1 -1
- luminarycloud/params/simulation/particle_group/particle_group_type_.py +1 -1
- luminarycloud/params/simulation/particle_group_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/adjoint_controls_fluid_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/basic_fluid_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/farfield_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/fan_curve_inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mach_inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mass_flow_inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/total_pressure_inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_components_inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_magnitude_inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/fan_curve_outlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_pressure_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_target_corrected_mass_flow_rate_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy/outlet_target_mass_flow_rate_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet/outlet_strategy_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/outlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/symmetry_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/turbulence_boundary_conditions_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/energy/prescribed_heat_flux_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/energy/prescribed_temperature_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/no_slip_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/slip_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/momentum/wall_model_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/wall_energy_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall/wall_momentum_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/wall_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions_fluid_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/initialization/__init__.py +1 -0
- luminarycloud/params/simulation/physics/fluid/initialization/fluid_existing_solution_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/initialization/fluid_farfield_values_.py +14 -1
- luminarycloud/params/simulation/physics/fluid/initialization/fluid_initialization_per_zone_.py +48 -0
- luminarycloud/params/simulation/physics/fluid/initialization/fluid_prescribed_values_.py +14 -1
- luminarycloud/params/simulation/physics/fluid/initialization/turbulence_initialization_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/initialization_fluid_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/blade_element_airfoil_data_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_blade_element_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_radial_distribution_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/actuator_disk_uniform_thrust_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model/fan_curve_internal_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_disk_model_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_line_model/actuator_line_blade_element_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/actuator_line_model_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/blade_element_params_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model/general_acceleration_source_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model/general_mass_source_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model/particle_source_model_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior/physical_behavior_model_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/physical_behavior_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/porous_behavior_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_explicit_relaxation_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_auto_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_off_.py +1 -1
- 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/robust_startup_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method/cfl_based_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method/fixed_pseudo_time_step_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/pseudo_time_step_method_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls_fluid_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/ec2_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/fds_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/ld2_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme/rhie_chow_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order/first_order_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order/second_order_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization/convective_scheme_order_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/spatial_discretization_fluid_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/ddes_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/ddes_vtm_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation/iddes_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/des_formulation_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/constants/custom_komega_sst_constants_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/constants/default_komega_sst_constants_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst/komega_sst_constants_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/komega_sst_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/constants/custom_spalart_allmaras_constants_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/constants/default_spalart_allmaras_constants_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras/spalart_allmaras_constants_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/spalart_allmaras_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/amd_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/sigma_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/smagorinsky_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/vreman_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model/wale_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence/sub_grid_scale_model_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/turbulence_.py +1 -1
- luminarycloud/params/simulation/physics/fluid_.py +1 -1
- luminarycloud/params/simulation/physics/heat/adjoint_controls_heat_.py +1 -1
- luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_convection_.py +1 -1
- luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_heat_flux_.py +1 -1
- luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_integrated_heat_flux_.py +1 -1
- luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_symmetry_.py +1 -1
- luminarycloud/params/simulation/physics/heat/boundary_conditions/heat_bc_temperature_.py +1 -1
- luminarycloud/params/simulation/physics/heat/boundary_conditions_heat_.py +1 -1
- luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type/heat_source_type_power_.py +1 -1
- luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type/heat_source_type_power_per_unit_of_volume_.py +1 -1
- luminarycloud/params/simulation/physics/heat/heat_source/heat_source_type_.py +1 -1
- luminarycloud/params/simulation/physics/heat/heat_source_.py +1 -1
- luminarycloud/params/simulation/physics/heat/initialization/__init__.py +1 -0
- luminarycloud/params/simulation/physics/heat/initialization/heat_existing_solution_.py +1 -1
- luminarycloud/params/simulation/physics/heat/initialization/heat_prescribed_values/__init__.py +1 -0
- luminarycloud/params/simulation/physics/heat/initialization/heat_prescribed_values/heat_initialization_per_zone_.py +40 -0
- luminarycloud/params/simulation/physics/heat/initialization/heat_prescribed_values_.py +14 -1
- luminarycloud/params/simulation/physics/heat/initialization_heat_.py +1 -1
- luminarycloud/params/simulation/physics/heat/solution_controls/heat_relaxation_method/heat_implicit_relaxation_.py +1 -1
- luminarycloud/params/simulation/physics/heat/solution_controls/heat_relaxation_method_.py +1 -1
- luminarycloud/params/simulation/physics/heat/solution_controls_heat_.py +1 -1
- luminarycloud/params/simulation/physics/heat/spatial_discretization_heat_.py +1 -1
- luminarycloud/params/simulation/physics/heat_.py +1 -1
- luminarycloud/params/simulation/physics/periodic_pair_.py +5 -1
- luminarycloud/params/simulation/physics/solution_controls/linear_solver_type/gauss_seidel_.py +1 -1
- luminarycloud/params/simulation/physics/solution_controls/linear_solver_type/krylov_amg_.py +1 -1
- luminarycloud/params/simulation/physics/solution_controls/linear_solver_type_.py +1 -1
- luminarycloud/params/simulation/physics_.py +1 -1
- luminarycloud/params/simulation/simulation_param_.py +8 -1
- luminarycloud/params/simulation/sliding_interfaces_.py +1 -1
- luminarycloud/params/simulation/surface_name_.py +1 -1
- luminarycloud/params/simulation/time/compute_statistics/compute_statistics_off_.py +1 -1
- luminarycloud/params/simulation/time/compute_statistics/compute_statistics_on_.py +1 -1
- luminarycloud/params/simulation/time/compute_statistics_.py +1 -1
- luminarycloud/params/simulation/time/time_marching/time_explicit_.py +1 -1
- luminarycloud/params/simulation/time/time_marching/time_implicit_.py +1 -1
- luminarycloud/params/simulation/time/time_marching_.py +1 -1
- luminarycloud/params/simulation/time/time_step_ramp/time_step_ramp_off_.py +1 -1
- luminarycloud/params/simulation/time/time_step_ramp/time_step_ramp_on_.py +1 -1
- luminarycloud/params/simulation/time/time_step_ramp_.py +1 -1
- luminarycloud/params/simulation/time_.py +1 -1
- luminarycloud/params/simulation/volume_entity_.py +1 -1
- luminarycloud/physics_ai/__init__.py +17 -0
- luminarycloud/physics_ai/architectures.py +130 -18
- luminarycloud/physics_ai/datasets.py +301 -0
- luminarycloud/physics_ai/training_jobs.py +50 -4
- luminarycloud/pipelines/__init__.py +16 -4
- luminarycloud/pipelines/api.py +192 -17
- luminarycloud/pipelines/arguments.py +8 -3
- luminarycloud/pipelines/core.py +296 -45
- luminarycloud/pipelines/flowables.py +138 -0
- luminarycloud/pipelines/stages.py +34 -55
- luminarycloud/pipelines/user_code_validation.py +122 -0
- luminarycloud/project.py +0 -14
- luminarycloud/simulation.py +42 -11
- luminarycloud/simulation_param.py +0 -62
- luminarycloud/simulation_template.py +1 -11
- luminarycloud/types/adfloat.py +48 -4
- luminarycloud/types/matrix3.py +9 -9
- luminarycloud/types/vector3.py +52 -23
- luminarycloud/vis/__init__.py +3 -0
- luminarycloud/vis/visualization.py +88 -0
- luminarycloud/volume_selection.py +29 -17
- {luminarycloud-0.22.2.dist-info → luminarycloud-0.23.0.dist-info}/METADATA +1 -1
- {luminarycloud-0.22.2.dist-info → luminarycloud-0.23.0.dist-info}/RECORD +266 -249
- luminarycloud/params/outputs/residual_output.py +0 -24
- /luminarycloud/params/{simulation/_lib.py → _lib.py} +0 -0
- {luminarycloud-0.22.2.dist-info → luminarycloud-0.23.0.dist-info}/WHEEL +0 -0
luminarycloud/pipelines/core.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
107
|
+
Raised by RunScript code to indicate that the pipeline run should stop intentionally.
|
|
112
108
|
"""
|
|
113
109
|
|
|
114
|
-
|
|
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["
|
|
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
|
|
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
|
-
|
|
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"
|
|
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 =
|
|
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
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
+
}
|