コード例 #1
0
ファイル: engine.py プロジェクト: Arcayik/orbitx
def _one_request(request: Request, y0: PhysicsState) \
        -> PhysicsState:
    """Interface to set habitat controls.

    Use an argument to change habitat throttle or spinning, and simulation
    will restart with this new information."""
    log.info(f'At simtime={y0.timestamp}, '
             f'Got command {MessageToString(request, as_one_line=True)}')

    if request.ident != Request.TIME_ACC_SET:
        # Reveal the type of y0.craft as str (not None).
        assert y0.craft is not None

    if request.ident == Request.HAB_SPIN_CHANGE:
        if y0.navmode != Navmode['Manual']:
            # We're in autopilot, ignore this command
            return y0
        craft = y0.craft_entity()
        if not craft.landed():
            craft.spin += request.spin_change
    elif request.ident == Request.HAB_THROTTLE_CHANGE:
        y0.craft_entity().throttle += request.throttle_change
    elif request.ident == Request.HAB_THROTTLE_SET:
        y0.craft_entity().throttle = request.throttle_set
    elif request.ident == Request.TIME_ACC_SET:
        assert request.time_acc_set >= 0
        y0.time_acc = request.time_acc_set
    elif request.ident == Request.ENGINEERING_UPDATE:
        # Multiply this value by 100, because OrbitV considers engines at
        # 100% to be 100x the maximum thrust.
        common.craft_capabilities[HABITAT] = \
            common.craft_capabilities[HABITAT]._replace(
                thrust=100 * request.engineering_update.max_thrust)
        hab = y0[HABITAT]
        ayse = y0[AYSE]
        hab.fuel = request.engineering_update.hab_fuel
        ayse.fuel = request.engineering_update.ayse_fuel
        y0[HABITAT] = hab
        y0[AYSE] = ayse

        if request.engineering_update.module_state == \
                Request.DETACHED_MODULE and \
                MODULE not in y0._entity_names and \
                not hab.landed():
            # If the Habitat is freely floating and engineering asks us to
            # detach the Module, spawn in the Module.
            module = Entity(protos.Entity(
                name=MODULE, mass=100, r=10, artificial=True))
            module.pos = (hab.pos - (module.r + hab.r) *
                          calc.heading_vector(hab.heading))
            module.v = calc.rotational_speed(module, hab)

            y0_proto = y0.as_proto()
            y0_proto.entities.extend([module.proto])
            y0 = PhysicsState(None, y0_proto)

    elif request.ident == Request.UNDOCK:
        habitat = y0[HABITAT]

        if habitat.landed_on == AYSE:
            ayse = y0[AYSE]
            habitat.landed_on = ''

            norm = habitat.pos - ayse.pos
            unit_norm = norm / calc.fastnorm(norm)
            habitat.v += unit_norm * common.UNDOCK_PUSH
            habitat.spin = ayse.spin

            y0[HABITAT] = habitat

    elif request.ident == Request.REFERENCE_UPDATE:
        y0.reference = request.reference
    elif request.ident == Request.TARGET_UPDATE:
        y0.target = request.target
    elif request.ident == Request.LOAD_SAVEFILE:
        y0 = common.load_savefile(common.savefile(request.loadfile))
    elif request.ident == Request.NAVMODE_SET:
        y0.navmode = Navmode(request.navmode)
        if y0.navmode == Navmode['Manual']:
            y0.craft_entity().spin = 0
    elif request.ident == Request.PARACHUTE:
        y0.parachute_deployed = request.deploy_parachute
    elif request.ident == Request.IGNITE_SRBS:
        if round(y0.srb_time) == common.SRB_FULL:
            y0.srb_time = common.SRB_BURNTIME
    elif request.ident == Request.TOGGLE_COMPONENT:
        component = y0.engineering.components[request.component_to_toggle]
        component.connected = not component.connected
    elif request.ident == Request.TOGGLE_RADIATOR:
        radiator = y0.engineering.radiators[request.radiator_to_toggle]
        radiator.functioning = not radiator.functioning
    elif request.ident == Request.CONNECT_RADIATOR_TO_LOOP:
        radiator = y0.engineering.radiators[request.radiator_to_loop.rad]
        radiator.attached_to_coolant_loop = request.radiator_to_loop.loop
    elif request.ident == Request.TOGGLE_COMPONENT_COOLANT:
        component = y0.engineering.components[request.component_to_loop.component]
        # See comments on _component_coolant_cnxn_transitions for more info.
        component.coolant_connection = (
            _component_coolant_cnxn_transitions
            [request.component_to_loop.loop]
            [component.coolant_connection]
        )

    return y0
コード例 #2
0
ファイル: engine.py プロジェクト: Arcayik/orbitx
    def _run_simulation(self, t: float, y: PhysicsState) -> None:
        # An overview of how time is managed:
        #
        # self._last_simtime is the main thread's latest idea of
        # what the current time is in the simulation. Every call to
        # get_state(), self._timetime_of_last_request is incremented by the
        # amount of time that passed since the last call to get_state(),
        # factoring in time_acc
        #
        # self._solutions is a fixed-size queue of ODE solutions.
        # Each element has an attribute, t_max, which describes the largest
        # time that the solution can be evaluated at and still be accurate.
        # The highest such t_max should always be larger than the current
        # simulation time, i.e. self._last_simtime
        proto_state = y._proto_state

        while not self._stopping_simthread:
            derive_func = functools.partial(
                self._derive, pass_through_state=proto_state)

            events: List[Event] = [
                CollisionEvent(y, self.R), HabFuelEvent(y), LiftoffEvent(y),
                SrbFuelEvent(), HabReactorTempEvent(), AyseReactorTempEvent()
            ]
            if y.craft is not None:
                events.append(HighAccEvent(
                    derive_func,
                    self._artificials,
                    TIME_ACC_TO_BOUND[round(y.time_acc)],
                    y.time_acc,
                    len(y)))

            ivp_out = scipy.integrate.solve_ivp(
                fun=derive_func,
                t_span=[t, t + min(y.time_acc, 10 * self.MAX_STEP_SIZE)],
                # solve_ivp requires a 1D y0 array
                y0=y.y0(),
                events=events,
                dense_output=True,
                max_step=self.MAX_STEP_SIZE
            )

            if not ivp_out.success:
                # Integration error
                raise Exception(ivp_out.message)

            # When we create a new solution, let other people know.
            with self._solutions_cond:
                # If adding another solution to our max-sized deque would drop
                # our oldest solution, and the main thread is still asking for
                # state in the t interval of our oldest solution, take a break
                # until the main thread has caught up.
                self._solutions_cond.wait_for(
                    lambda:
                    len(self._solutions) < SOLUTION_CACHE_SIZE or
                    self._last_simtime > self._solutions[0].t_max or
                    self._stopping_simthread
                )
                if self._stopping_simthread:
                    break

                # self._solutions contains ODE solutions for the interval
                # [self._solutions[0].t_min, self._solutions[-1].t_max].
                self._solutions.append(ivp_out.sol)
                self._solutions_cond.notify_all()

            y = PhysicsState(ivp_out.y[:, -1], proto_state)
            t = ivp_out.t[-1]

            if ivp_out.status > 0:
                log.info(f'Got event: {ivp_out.t_events} at t={t}.')
                for index, event_t in enumerate(ivp_out.t_events):
                    if len(event_t) == 0:
                        # If this event didn't occur, then event_t == []
                        continue
                    event = events[index]
                    if isinstance(event, CollisionEvent):
                        # Collision, simulation ended. Handled it and continue.
                        assert len(ivp_out.t_events[0]) == 1
                        assert len(ivp_out.t) >= 2
                        y = _collision_decision(t, y, events[0])
                        y = _reconcile_entity_dynamics(y)
                    if isinstance(event, HabFuelEvent):
                        # Something ran out of fuel.
                        for artificial_index in self._artificials:
                            artificial = y[artificial_index]
                            if round(artificial.fuel) != 0:
                                continue
                            log.info(f'{artificial.name} ran out of fuel.')
                            # This craft is out of fuel, the next iteration
                            # won't consume any fuel. Set throttle to zero.
                            artificial.throttle = 0
                            # Set fuel to a negative value, so it doesn't
                            # trigger the event function.
                            artificial.fuel = 0
                    if isinstance(event, LiftoffEvent):
                        # A craft has a TWR > 1
                        craft = y.craft_entity()
                        log.info(
                            'We have liftoff of the '
                            f'{craft.name} from {craft.landed_on} at {t}.')
                        craft.landed_on = ''
                    if isinstance(event, SrbFuelEvent):
                        # SRB fuel exhaustion.
                        log.info('SRB exhausted.')
                        y.srb_time = common.SRB_EMPTY
                    if isinstance(event, HighAccEvent):
                        # The acceleration acting on the craft is high, might
                        # result in inaccurate results. SLOOWWWW DOWWWWNNNN.
                        slower_time_acc_index = list(
                            TIME_ACC_TO_BOUND.keys()
                        ).index(round(y.time_acc)) - 1
                        assert slower_time_acc_index >= 0
                        slower_time_acc = \
                            common.TIME_ACCS[slower_time_acc_index]
                        assert slower_time_acc.value > 0
                        log.info(
                            f'{y.time_acc} is too fast, '
                            f'slowing down to {slower_time_acc.value}')
                        # We should lower the time acc.
                        y.time_acc = slower_time_acc.value
                        raise PhysicsEngine.RestartSimulationException(t, y)
                    if isinstance(event, HabReactorTempEvent):
                        y.engineering.hab_reactor_alarm = not y.engineering.hab_reactor_alarm
                    if isinstance(event, AyseReactorTempEvent):
                        y.engineering.ayse_reactor_alarm = not y.engineering.ayse_reactor_alarm