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

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

#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;
				};
				case State::Empty:
					return currentState;
				}
			}();
}

MpiWireworld::MpiWireworld(const MpiEnvironment& env, const Configuration& cfg)
    : tile_(Tile::Read(cfg, env)),
      comm_([&]() -> std::unique_ptr<Communicator> {
	      switch (cfg.CommMode) {
	      case CommunicationMode::Collective:
		      return std::make_unique<CollectiveCommunicator>(
		          env, cfg.CommMode, cfg.Procs, tile_.tileSize());
	      case CommunicationMode::P2P:
		      return std::make_unique<P2PCommunicator>(
		          env, cfg.CommMode, cfg.Procs, 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_;
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_);