def board_locations_from_spinner(filename): """Utility function which converts a CSV file produced by the `spinner-ethernet-chips <http://spinner.readthedocs.org/en/stable/spinner-ethernet-chips.html>`_ utility into a ``board_locations`` dictionary suitable for defining :py:class:`.Machine` objects. Parameters ---------- filename : str The name of a CSV file produced by spinner-ethernet-chips defining the relationship between Ethernet connected chip coordinates and physical board locations. This file is expected to have five columns (named in the first line of the CSV) named 'board', 'cabinet', 'frame', 'x', and 'y'. Returns ------- {(x, y, z): (c, f, b), ...} The mapping from board coordinates to physical locations. """ # Extract lookup from Ethernet connected chips to locations chip_locations = {} with open(filename, "r") as f: for entry in csv.DictReader(f): cfb = tuple(map(int, (entry["cabinet"], entry["frame"], entry["board"]))) chip_xy = (int(entry["x"]), int(entry["y"])) assert chip_xy not in chip_locations chip_locations[chip_xy] = cfb # Infer machine dimensions max_x, max_y = map(max, zip(*chip_locations)) width_triads = (max_x // 12) + 1 height_triads = (max_y // 12) + 1 # Convert from chip to board coordinates return { chip_to_board(chip_x, chip_y, width_triads * 12, height_triads * 12): cfb for (chip_x, chip_y), cfb in iteritems(chip_locations) }
def where_is(self, **kwargs): """Find out where a SpiNNaker board or chip is located, logically and physically. May be called in one of the following styles:: >>> # Query by logical board coordinate within a machine. >>> where_is(machine=..., x=..., y=..., z=...) >>> # Query by physical board location within a machine. >>> where_is(machine=..., cabinet=..., frame=..., board=...) >>> # Query by chip coordinate (as if the machine were booted as >>> # one large machine). >>> where_is(machine=..., chip_x=..., chip_y=...) >>> # Query by chip coordinate, within the boards allocated to a >>> # job. >>> where_is(job_id=..., chip_x=..., chip_y=...) Returns ------- {"machine": ..., "logical": ..., "physical": ..., "chip": ..., \ "board_chip": ..., "job_chip": ..., "job_id": ...} or None If a board exists at the supplied location, a dictionary giving the location of the board/chip, supplied in a number of alternative forms. If the supplied coordinates do not specify a specific chip, the chip coordinates given are those of the Ethernet connected chip on that board. If no board exists at the supplied position, None is returned instead. ``machine`` gives the name of the machine containing the board. ``logical`` the logical board coordinate, (x, y, z) within the machine. ``physical`` the physical board location, (cabinet, frame, board), within the machine. ``chip`` the coordinates of the chip, (x, y), if the whole machine were booted as a single machine. ``board_chip`` the coordinates of the chip, (x, y), within its board. ``job_id`` is the job ID of the job currently allocated to the board identified or None if the board is not allocated to a job. ``job_chip`` the coordinates of the chip, (x, y), within its job, if a job is allocated to the board or None otherwise. """ with self._lock: # Initially, we normalise the input coordinate into: # # machine_name, chip_x, chip_y # # and then convert this back into all the output formats required. # At various points, if we encounter a board/job/chip which doesn't # exist we'll drop out. keywords = set(kwargs) if keywords == set("machine x y z".split()): # Covert from logical position machine_name = kwargs["machine"] chip_x, chip_y = board_to_chip( kwargs["x"], kwargs["y"], kwargs["z"]) elif keywords == set("machine cabinet frame board".split()): # Covert from physical position (fail if location does not # exist) machine_name = kwargs["machine"] xyz = self.get_board_at_position(machine_name, kwargs["cabinet"], kwargs["frame"], kwargs["board"]) if xyz is None: return None chip_x, chip_y = board_to_chip(*xyz) elif keywords == set("machine chip_x chip_y".split()): # Covert from chip location machine_name = kwargs["machine"] chip_x = kwargs["chip_x"] chip_y = kwargs["chip_y"] elif keywords == set("job_id chip_x chip_y".split()): # Covert from job-relative chip location job = self._jobs.get(kwargs["job_id"], None) if job is None or job.boards is None: return None machine_name = job.allocated_machine.name job_x, job_y, job_z = map(min, zip(*job.boards)) dx, dy = board_to_chip(job_x, job_y, job_z) chip_x = kwargs["chip_x"] + dx chip_y = kwargs["chip_y"] + dy # NB: We double-check later that this coordinate is actually a # board within the boards allocated to the job! else: raise TypeError( "Invalid arguments: {}".format(", ".join(keywords))) # Get the actual Machine machine = self._machines.get(machine_name, None) if machine is None: return None # Compensate chip coordinates for wrap-around chip_w, chip_h = triad_dimensions_to_chips( self._machines[machine_name].width, self._machines[machine_name].height, WrapAround.both) chip_x %= chip_w chip_y %= chip_h # Determine the chip within the board # Workaround: spinn5_chip_coord (until at least Rig 0.13.2) returns # numpy integer types which are not JSON serialiseable. board_chip_x, board_chip_y = map( int, spinn5_chip_coord(chip_x, chip_y)) # Determine the logical board coordinates (and compensate for # wrap-around) x, y, z = chip_to_board(chip_x, chip_y, chip_w, chip_h) # Determine the board's physical location (fail if board does not # exist) cfb = self.get_board_position(machine_name, x, y, z) if cfb is None: return None cabinet, frame, board = cfb # Determine what job is running on that board for job_id, job in iteritems(self._jobs): # NB: If machine is defined, boards must also be defined. if (job.allocated_machine == machine and (x, y, z) in job.boards): # Found the job break else: # No job is allocated to the board job_id = None job = None # If selected by job, make sure the board found is actually running # that job (this won't be the case, e.g. if a user specifies a # board within their machine which is actually dead or allocated to # a neighbouring job) if "job_id" in kwargs and job_id != kwargs["job_id"]: return None # Determine chip coordinate within job if job is not None: # Determine the board coordinate within the job job_x, job_y, job_z = map(min, zip(*job.boards)) job_x = x - job_x job_y = y - job_y job_z = z - job_z # Turn that into a chip coordinate and wrap-around according to # the boards actually available in the allocated machine job_chip_x, job_chip_y = board_to_chip(job_x, job_y, job_z) job_chip = ((job_chip_x + board_chip_x) % job.width, (job_chip_y + board_chip_y) % job.height) else: job_chip = None return { "machine": machine_name, "logical": (x, y, z), "physical": (cabinet, frame, board), "chip": (chip_x, chip_y), "board_chip": (board_chip_x, board_chip_y), "job_id": job_id, "job_chip": job_chip, }
def test_chip_to_board(bxyz, cxywh): assert chip_to_board(*cxywh) == bxyz