def initialize(state: State) -> State: """Initialize a simulation state.""" for m in state.config.modules: with validation_context(f'{m.name} (initialization)'): state = m.initialize(state) # run validation after all initialization is done otherwise validation # could fail on a module's private state before it can initialize itself with validation_context('global initialization'): attr.validate(state) return state
def create(cls, config: 'SimulationConfig'): """Generate a new state object from a config.""" shape = ( config.getint('simulation', 'nz'), config.getint('simulation', 'ny'), config.getint('simulation', 'nx'), ) spacing = ( config.getfloat('simulation', 'dz'), config.getfloat('simulation', 'dy'), config.getfloat('simulation', 'dx'), ) grid = RectangularGrid.construct_uniform(shape, spacing) state = State(time=0.0, grid=grid, config=config) for module in state.config.modules: if hasattr(state, module.name): # prevent modules from overriding existing class attributes raise ValueError( f'The name "{module.name}" is a reserved token.') with validation_context(f'{module.name} (construction)'): state._extra[module.name] = module.StateClass( global_state=state) module.construct(state) return state
def advance(state: State, target_time: float) -> Iterator[State]: """Advance a simulation to the given target time.""" initial_time = state.time @dataclass(order=True) class ModuleUpdateEvent: event_time: float previous_update: float module: ModuleModel = field(compare=False) # Create and fill a queue of modules to run. This allows for modules to # operate on disparate time scales. Modules which do not have a time step # set will not be run. queue: PriorityQueue[ModuleUpdateEvent] = PriorityQueue() for module in state.config.modules: if module.time_step is not None and module.time_step > 0: queue.put( ModuleUpdateEvent(event_time=initial_time, previous_update=initial_time, module=module)) # run the simulation until we meet or surpass the desired time # while-loop conditional is on previous time so that all pending # modules are run on final iteration previous_time: float = initial_time while previous_time < target_time and not queue.empty(): # fill a list with update events that are concurrent, and randomize their order concurrent_update_events = [queue.get()] event_time = concurrent_update_events[0].event_time while not queue.empty(): update_event = queue.get() if update_event.event_time == event_time: concurrent_update_events.append(update_event) else: queue.put(update_event) break rg.shuffle(concurrent_update_events) for update_event in concurrent_update_events: m: ModuleModel = update_event.module previous_time = update_event.previous_update state.time = update_event.event_time with validation_context(m.name): state = m.advance(state, previous_time) attr.validate(state) # reinsert module with updated time queue.put( ModuleUpdateEvent(event_time=state.time + m.time_step, previous_update=state.time, module=m)) yield state
def create(cls, config: 'SimulationConfig'): """Generate a new state object from a config.""" voxel_volume = config.getfloat('simulation', 'voxel_volume') lung_tissue = get_geometry_file( config.get('simulation', 'geometry_path')) # python type checker isn't enough to understand this assert len(lung_tissue.shape) == 3 # noinspection PyTypeChecker shape: Tuple[int, int, int] = lung_tissue.shape space_volume = voxel_volume * np.product(shape) spacing = ( config.getfloat('simulation', 'dz'), config.getfloat('simulation', 'dy'), config.getfloat('simulation', 'dx'), ) grid = RectangularGrid.construct_uniform(shape, spacing) state = State( time=0.0, grid=grid, config=config, lung_tissue=lung_tissue, voxel_volume=voxel_volume, space_volume=space_volume, ) for module in state.config.modules: if hasattr(state, module.name): # prevent modules from overriding existing class attributes raise ValueError( f'The name "{module.name}" is a reserved token.') with validation_context(f'{module.name} (construction)'): state._extra[module.name] = module.StateClass( global_state=state) module.construct(state) return state
def finalize(state: State) -> State: for m in state.config.modules: with validation_context(m.name): state = m.finalize(state) return state