Skip to content
MpiWireworld.cpp 2.96 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 "CommunicatorFactory.hpp"
#include "FileIO.hpp"
#include "State.hpp"
#include "Tile.hpp"
#include "Util.hpp"

void MpiWireworld::processArea(Coord start, Size size) {
	auto& model_ = tile_.model();
	auto& nextModel_ = tile_.nextModel();
	const auto modelWidth = tile_.modelWidth();

	// std::size_t is unsigned. modulo arithmetics is used for calculating the
	// index
	const std::size_t leftOffset = std::numeric_limits<std::size_t>::max(); // -1;
	const std::size_t rightOffset = 1;
	const std::size_t downOffset = modelWidth;
	const std::size_t upOffset = -downOffset;

	for (std::size_t y{start.Y}; y < start.Y + size.Rows; ++y) {
		for (std::size_t x{start.X}; x < start.X + size.Cols; ++x) {
			const auto idx = y * modelWidth + x;

			const auto currentState = model_[idx];
			nextModel_[idx] = [&]() {
				switch (currentState) {
				case State::ElectronHead:
					return State::ElectronTail;
				case State::ElectronTail:
					return State::Conductor;
				case State::Conductor: {
					const auto isHead = [&](std::size_t i) {
						return model_[i] == State::ElectronHead ? 1 : 0;
					};
					const auto headCount =
					    isHead(idx + leftOffset + upOffset) +   //
					    isHead(idx + upOffset) +                //
					    isHead(idx + rightOffset + upOffset) +  //
					    isHead(idx + leftOffset) +              //
					    isHead(idx + rightOffset) +             //
					    isHead(idx + leftOffset + downOffset) + //
					    isHead(idx + downOffset) +              //
					    isHead(idx + rightOffset + downOffset); //

					return (1 == headCount || headCount == 2)
					           ? State::ElectronHead
					           : State::Conductor;
Thomas Steinreiter's avatar
Thomas Steinreiter committed
				default:
					return currentState;
				}
			}();
}

MpiWireworld::MpiWireworld(const MpiEnvironment& env, const Configuration& cfg)
Thomas Steinreiter's avatar
Thomas Steinreiter committed
    : tile_(Tile::Read(cfg, env)), comm_(CommunicatorFactory::Create(cfg, tile_, env)) {
	comm_->Communicate(tile_.model());
}

std::ostream& operator<<(std::ostream& out, const MpiWireworld& g) {
	// for now, put only our local tile
	out << g.tile_;
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
	processArea({1, 1}, {tileSize_.Cols, 1});

	// left and right
	processArea({1, 2}, {1, tileSize_.Rows - 2});
	processArea({tileSize_.Cols, 2}, {1, tileSize_.Rows - 2});
	processArea({1, tileSize_.Rows}, {tileSize_.Cols, 1});

	// start communication of border while processing the core
	auto req = comm_->AsyncCommunicate(nextModel_);
	processArea({2, 2}, {tileSize_.Cols - 2, tileSize_.Rows - 2});
	std::swap(model_, nextModel_);