Skip to content
MpiWireworld.cpp 2.61 KiB
Newer Older
#include "MpiWireworld.hpp"

#include <algorithm>
#include <array>
#include <iostream>
#include <mpi.h>
#include <string>
#include <vector>

Thomas Steinreiter's avatar
Thomas Steinreiter committed
#include "gsl/gsl"

#include "FileIO.hpp"
#include "State.hpp"
#include "Tile.hpp"
#include "Util.hpp"

void MpiWireworld::processCell(std::size_t x, std::size_t y) {
	const auto& _model = _tile.model();
	auto& _nextModel = _tile.nextModel();
Thomas Steinreiter's avatar
Thomas Steinreiter committed
	const auto currentState = _model[y][x];
Thomas Steinreiter's avatar
Thomas Steinreiter committed
	_nextModel[y][x] = [&]() {
		switch (currentState) {
		case State::ElectronHead:
			return State::ElectronTail;
			break;
		case State::ElectronTail:
			return State::Conductor;
			break;
		case State::Conductor: {
			const std::array<State, 9> mooreNeighborhood = {
			    _model[y - 1][x - 1], //
			    _model[y + 0][x - 1], //
			    _model[y + 1][x - 1], //
			    _model[y - 1][x + 0], //
			    _model[y + 1][x + 0], //
			    _model[y - 1][x + 1], //
			    _model[y + 0][x + 1], //
			    _model[y + 1][x + 1]  //
			};

			const auto headCount =
			    std::count(std::begin(mooreNeighborhood),
			               std::end(mooreNeighborhood), State::ElectronHead);

			return (1 == headCount || headCount == 2) ? State::ElectronHead
			                                          : State::Conductor;
		} break;
		default:
			return currentState;
			break;
		}
	}();
}

MpiWireworld::MpiWireworld(const MpiEnvironment& env, const Configuration& cfg)
    : _env(env), _cfg(cfg), _tile(Tile::Read(cfg, env)),
      _comm(env, cfg.CommMode, cfg.Grid, _tile.tileSize()) {
	_comm.Communicate(_tile.model());
}

std::ostream& operator<<(std::ostream& out, const MpiWireworld& g) {
	// for now, put only our local tile
	out << g._tile;
	return out;
}

void MpiWireworld::write() const { _tile.write(); }

void MpiWireworld::simulateStep() {
	auto& _model = _tile.model();
	auto& _nextModel = _tile.nextModel();
	const auto _tileSize = _tile.tileSize();

	//// compute the border area first, then comm
	//// and compute the core async

	/// border area
	// top
	for (std::size_t x{1}; x <= _tileSize.Cols; ++x) {
		const auto y = 1;
		processCell(x, y);
	}

	// left and right
	for (std::size_t y{2}; y < _tileSize.Rows; ++y) {
		const auto x1 = 1;
		const auto x2 = _tileSize.Cols;
		processCell(x1, y);
		processCell(x2, y);
	}

	// bottom
	for (std::size_t x{1}; x <= _tileSize.Cols; ++x) {
		const auto y = _tileSize.Rows;
		processCell(x, y);
	}

	// start communication of border while processing the core
	auto req = _comm.AsyncCommunicate(_nextModel);
	/// core
	for (std::size_t y{2}; y < _tileSize.Rows; ++y) {
		for (std::size_t x{2}; x < _tileSize.Cols; ++x) { processCell(x, y); }
	}
	req.Wait();

	std::swap(_model, _nextModel);
}