Source code for genesis.options.scene

"""Every option a scene is created with, gathered as one object."""

from pydantic import Field, model_validator

import genesis as gs

from .options import Options
from .profiling import ProfilingOptions
from .renderers import Rasterizer, RendererOptions
from .solvers import (
    BaseCouplerOptions,
    FEMOptions,
    KinematicOptions,
    LegacyCouplerOptions,
    MPMOptions,
    PBDOptions,
    RigidOptions,
    SFOptions,
    SimOptions,
    SPHOptions,
    ToolOptions,
)
from .vis import ViewerOptions, VisOptions


[docs]class SceneOptions(Options): """Every option a scene is created with, as one object. Each field bears the name of the 'Scene' argument that carries it, without the '_options' suffix. Each states its own default and falls back to it when given None, so a scene names an option only to override it, and every field holds one once the options exist. Every option inherits from the simulation options the quantities it declares and leaves unset, which in practice is the solvers, since no other option shares a field with them. That resolution happens here, so every consumer reads the resolved value. """ sim: SimOptions = Field(default_factory=SimOptions) tool: ToolOptions = Field(default_factory=ToolOptions) rigid: RigidOptions = Field(default_factory=RigidOptions) kinematic: KinematicOptions = Field(default_factory=KinematicOptions) mpm: MPMOptions = Field(default_factory=MPMOptions) sph: SPHOptions = Field(default_factory=SPHOptions) fem: FEMOptions = Field(default_factory=FEMOptions) sf: SFOptions = Field(default_factory=SFOptions) pbd: PBDOptions = Field(default_factory=PBDOptions) coupler: BaseCouplerOptions = Field(default_factory=LegacyCouplerOptions) vis: VisOptions = Field(default_factory=VisOptions) viewer: ViewerOptions = Field(default_factory=ViewerOptions) profiling: ProfilingOptions = Field(default_factory=ProfilingOptions) renderer: RendererOptions = Field(default_factory=Rasterizer) @model_validator(mode="before") @classmethod def _given_or_declared(cls, values): """Give a field passed as None its declared default, exactly like a field the caller omits. 'Scene.__init__' passes every field, as None where the caller gave nothing, so the declared default is reached here rather than by omission. """ if isinstance(values, dict): return {name: value for name, value in values.items() if value is not None} return values def model_post_init(self, context) -> None: # Validate rigid_options against sim_options if self.rigid.box_box_detection is None: self.rigid.box_box_detection = not self.sim.requires_grad elif self.rigid.box_box_detection and self.sim.requires_grad: gs.raise_exception( "`rigid_options.box_box_detection` cannot be True when `sim_options.requires_grad` is True." ) if self.rigid.use_gjk_collision is None: self.rigid.use_gjk_collision = self.sim.requires_grad elif not self.rigid.use_gjk_collision and self.sim.requires_grad: gs.raise_exception( "`rigid_options.use_gjk_collision` cannot be False when `sim_options.requires_grad` is True." ) if self.rigid.enable_mujoco_compatibility and self.sim.requires_grad: gs.raise_exception( "`rigid_options.enable_mujoco_compatibility` cannot be True when `sim_options.requires_grad` is True." ) # Each option inherits from the simulation options the values it leaves unset, on fields it declares. # 'model_copy_from' fills only fields both sides declare, so one loop covers every option by name. for name, option in dict(self).items(): if name != "sim": self.__dict__[name] = option.model_copy_from(self.sim)