diff --git a/src/pe/basic.h b/src/pe/basic.h
index 08051705953cf2e5641188942c29a11e0c0cf7ec..c56163c5d13cf5979f32bf51f7202b43a6995533 100644
--- a/src/pe/basic.h
+++ b/src/pe/basic.h
@@ -51,3 +51,5 @@
 
 #include "pe/utility/CreateWorld.h"
 #include "pe/utility/GetBody.h"
+
+#include "pe/world/World.h"
diff --git a/src/pe/world/World.cpp b/src/pe/world/World.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..002d3dfd13c3857f6464952dcfb23933c43eacff
--- /dev/null
+++ b/src/pe/world/World.cpp
@@ -0,0 +1,123 @@
+#include "World.h"
+
+#include "pe/cr/HCSITS.h"
+
+namespace walberla
+{
+namespace pe
+{
+
+
+
+World::World(shared_ptr<BlockForest> forest, shared_ptr<BodyStorage> globalBodyStorage,
+      BlockDataID storageID, BlockDataID ccdID, BlockDataID fcdID, boost::function<void(void)> syncCall,
+      boost::function<void(void)> syncCallWithoutTT, Config::BlockHandle peConfig, shared_ptr<WcTimingTree> timingTree) :
+      blockForest_(forest), globalBodyStorage_(globalBodyStorage), timingTree_(timingTree), storageID_(storageID), ccdID_(ccdID), fcdID_(fcdID), syncCall_(syncCall), syncCallWithoutTT_(syncCallWithoutTT)
+{
+
+  shortestDistanceInBlock_ = 0;
+  longestDistanceFromParticleCenterToEdge_ = 0;
+
+  real_t blockXSize = forest->getRootBlockXSize();
+  real_t blockYSize = forest->getRootBlockYSize();
+  real_t blockZSize = forest->getRootBlockZSize();
+
+  if (blockXSize <= blockYSize && blockXSize <= blockZSize)
+  {
+    shortestDistanceInBlock_ = blockXSize;
+  }
+  else if (blockYSize <= blockZSize)
+  {
+    shortestDistanceInBlock_ = blockYSize;
+  }
+  else
+  {
+    shortestDistanceInBlock_ = blockZSize;
+  }
+
+  serializeFilename_ = peConfig.getParameter<std::string>("SuspendFilename", "");
+
+  std::string collisionResolverString = peConfig.getParameter<std::string>("CollisionResolver", "DEM");
+
+  Config::BlockHandle collisionConf = peConfig.getBlock("CollisonResolverConfig");
+
+  if (collisionResolverString == "DEM")
+  {
+    shared_ptr<cr::DEM> cr = make_shared<cr::DEM>(globalBodyStorage_, forest, storageID_, ccdID_, fcdID_, timingTree_.get());
+    collisionResolver_ = cr;
+  }
+  else if (collisionResolverString == "HCSITS")
+  {
+    shared_ptr<cr::HCSITS> cr = make_shared<cr::HCSITS>(globalBodyStorage_, forest, storageID_, ccdID_, fcdID_, timingTree_.get());
+    configure(collisionConf, *cr);
+    collisionResolver_ = cr;
+  }
+  else
+  {
+    WALBERLA_ABORT("Solver not implemented in World" << collisionResolverString);
+  }
+
+  Vec3 globalLinearAcceleration = peConfig.getParameter<Vec3>("globalLinearAcceleration", Vec3(0, 0, 0));
+  WALBERLA_LOG_INFO_ON_ROOT("globalLinearAcceleration: " << globalLinearAcceleration);
+  collisionResolver_->setGlobalLinearAcceleration(globalLinearAcceleration);
+}
+
+
+void World::syncAfterParticleCreation()
+{
+  mpi::allReduceInplace(longestDistanceFromParticleCenterToEdge_, mpi::MAX);
+  uint_t maximumNumberOfSpannedBlocks = uint_c(ceil(longestDistanceFromParticleCenterToEdge_ / shortestDistanceInBlock_));
+  if (maximumNumberOfSpannedBlocks < 1)
+  {
+    maximumNumberOfSpannedBlocks = 1;
+  }
+  for (uint_t i = 0; i < maximumNumberOfSpannedBlocks; ++i)
+  {
+    syncCallWithoutTT_();
+  }
+}
+
+
+PlaneID World::createPlane(id_t uid, Vec3 normal, const Vec3 &gpos, MaterialID material)
+{
+  return pe::createPlane(*globalBodyStorage_, uid, normal, gpos, material);
+}
+
+
+SphereID World::createSphere(id_t uid, const Vec3 &gpos, real_t radius, MaterialID material, bool global, bool communicating, bool infiniteMass)
+{
+  if (radius > longestDistanceFromParticleCenterToEdge_)
+  {
+    longestDistanceFromParticleCenterToEdge_ = radius;
+  }
+  return pe::createSphere(*globalBodyStorage_, *blockForest_, storageID_, uid, gpos, radius, material, global, communicating, infiniteMass);
+}
+
+
+BoxID World::createBox(id_t uid, const Vec3& gpos, const Vec3& lengths, MaterialID material, bool global, bool communicating, bool infiniteMass)
+{
+  for(uint_t i = 0; i < 3; i++) {
+    if ((lengths[i]/2.0) > longestDistanceFromParticleCenterToEdge_)
+    {
+      longestDistanceFromParticleCenterToEdge_ = real_c(lengths[i]/2.0);
+    }
+  }
+
+  return pe::createBox(*globalBodyStorage_, *blockForest_, storageID_, uid, gpos, lengths, material, global, communicating, infiniteMass);
+}
+
+CapsuleID World::createCapsule(id_t uid, const Vec3& gpos, const real_t radius, const real_t length, MaterialID material, bool global, bool communicating, bool infiniteMass)
+{
+
+    if((length/2.0 + radius) > longestDistanceFromParticleCenterToEdge_)
+    {
+      longestDistanceFromParticleCenterToEdge_ = real_c(length/2.0 + radius);
+    }
+    return pe::createCapsule(*globalBodyStorage_, *blockForest_, storageID_, uid, gpos, radius, length, material, global, communicating, infiniteMass);
+}
+
+
+
+
+} // namespace pe
+} // namespace walberla
diff --git a/src/pe/world/World.h b/src/pe/world/World.h
new file mode 100644
index 0000000000000000000000000000000000000000..e8914accaeb2602259056c64b30db863c4d6ae9c
--- /dev/null
+++ b/src/pe/world/World.h
@@ -0,0 +1,142 @@
+#pragma once
+
+#include "blockforest/BlockForest.h"
+#include "core/DataTypes.h"
+#include "core/config/Config.h"
+#include "domain_decomposition/BlockDataID.h"
+#include "pe/basic.h"
+
+#include <fstream>
+
+namespace walberla
+{
+namespace pe
+{
+
+class World
+{
+public:
+  World(shared_ptr<BlockForest> forest, shared_ptr<BodyStorage> globalBodyStorage,
+        BlockDataID storageID, BlockDataID ccdID, BlockDataID fcdID, boost::function<void(void)> syncCall,
+        boost::function<void(void)> syncCallWithoutTT, Config::BlockHandle peConfig,  shared_ptr<WcTimingTree> timingTree = nullptr);
+
+  void syncAfterParticleCreation();
+  void sync() { syncCall_(); }
+  void timestep(const real_t dt = 1) { collisionResolver_->timestep(dt); }
+  void simulationStep(const real_t dt = 1)
+  {
+    timestep(dt);
+    sync();
+  }
+
+  void serialize() {
+    if(serializeFilename_ == "") return;
+    blockForest_->saveBlockData(serializeFilename_, storageID_);
+  };
+
+  void serialize(std::string filename) {
+    if(filename == "") return;
+    blockForest_->saveBlockData(filename, storageID_);
+  }
+
+  const BlockDataID &getStorageID() const { return storageID_; }
+  const BlockDataID &getFCDID() const { return fcdID_; }
+  shared_ptr<cr::ICR> getCollisionResolver() { return collisionResolver_; }
+  const Vec3 &getGlobalLinearAcceleration() const { return collisionResolver_->getGlobalLinearAcceleration(); };
+  void setGlobalLinearAcceleration(const Vec3 &newAcceleration) { collisionResolver_->setGlobalLinearAcceleration(newAcceleration); };
+
+  PlaneID createPlane(id_t uid, Vec3 normal, const Vec3 &gpos, MaterialID material = Material::find("iron"));
+  SphereID createSphere(id_t uid, const Vec3 &gpos, real_t radius, MaterialID material = Material::find("iron"), bool global = false, bool communicating = true, bool infiniteMass = false);
+  BoxID createBox(id_t uid, const Vec3& gpos, const Vec3& lengths, MaterialID material = Material::find("iron"), bool global = false, bool communicating = true, bool infiniteMass = false);
+  CapsuleID createCapsule(id_t uid, const Vec3& gpos, const real_t radius, const real_t length, MaterialID material = Material::find("iron"), bool global = false, bool communicating = true, bool infiniteMass = false);
+
+  template<typename functionType, typename... Rest>
+  BodyID createBody(functionType creationFunction, const real_t long_axis_length, Rest... args) {
+    if(long_axis_length/2 > longestDistanceFromParticleCenterToEdge_) {
+      longestDistanceFromParticleCenterToEdge_ = long_axis_length/2;
+    }
+
+    return creationFunction(*globalBodyStorage_, *blockForest_, storageID_, args...);
+  }
+
+
+private:
+  const shared_ptr<BlockForest> blockForest_;
+  const shared_ptr<BodyStorage> globalBodyStorage_;
+  const shared_ptr<WcTimingTree> timingTree_;
+
+  const BlockDataID storageID_;
+  const BlockDataID ccdID_;
+  const BlockDataID fcdID_;
+
+  shared_ptr<cr::ICR> collisionResolver_;
+
+  real_t shortestDistanceInBlock_;
+  real_t longestDistanceFromParticleCenterToEdge_;
+
+  std::string serializeFilename_;
+
+  const boost::function<void(void)> syncCall_;
+  const boost::function<void(void)> syncCallWithoutTT_;
+};
+
+template <typename BodyTuple>
+World createWorldFromConfig(shared_ptr<BlockForest> forest, Config::BlockHandle peConf, shared_ptr<WcTimingTree> timingTree) {
+
+  SetBodyTypeIDs<BodyTuple>::execute();
+
+  auto globalBodyStorage = make_shared<BodyStorage>();
+
+  std::string resumeString = peConf.getParameter<std::string>("ResumeFilename", "");
+
+  BlockDataID storageID;
+
+  // Support suspend resume
+  if(resumeString != "") {
+    std::ifstream f(resumeString.c_str());
+    if(f.good()) {
+      f.close();
+      storageID = forest->loadBlockData(resumeString.c_str(), createStorageDataHandling<BodyTuple>());
+    }
+    else {
+      f.close();
+      storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage");
+    }
+  }
+  else {
+    storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage");
+  }
+
+  //auto storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage");
+  auto ccdID = forest->addBlockData(ccd::createHashGridsDataHandling(globalBodyStorage, storageID), "CCD");
+  auto fcdID = forest->addBlockData(fcd::createSimpleFCDDataHandling<BodyTuple>(), "FCD");
+
+   for (auto blockIt = forest->begin(); blockIt != forest->end(); ++blockIt) { ccd::ICCD* ccd = blockIt->getData< ccd::ICCD >( ccdID ); ccd->reloadBodies(); }
+
+  boost::function<void(void)> syncCall;
+  boost::function<void(void)> syncCallWithoutTT;
+
+  bool largeParticleSupport = peConf.getParameter<bool>("LargeParticleSupport", false);
+  if (!largeParticleSupport)
+  {
+    syncCall = boost::bind(pe::syncNextNeighbors<BodyTuple>, boost::ref(*forest), storageID, timingTree.get(), real_c(0.0), false);
+  }
+  else
+  {
+    syncCall = boost::bind(pe::syncShadowOwners<BodyTuple>, boost::ref(*forest), storageID, timingTree.get(), real_c(0.0), false);
+  }
+
+  if (!largeParticleSupport)
+  {
+    syncCallWithoutTT = boost::bind(pe::syncNextNeighbors<BodyTuple>, boost::ref(*forest), storageID, static_cast<WcTimingTree *>(NULL), real_c(0.0), false);
+  }
+  else
+  {
+    syncCallWithoutTT = boost::bind(pe::syncShadowOwners<BodyTuple>, boost::ref(*forest), storageID, static_cast<WcTimingTree *>(NULL), real_c(0.0), false);
+  }
+
+  return World(forest, globalBodyStorage, storageID, ccdID, fcdID, syncCall, syncCallWithoutTT, peConf, timingTree);
+}
+
+} // namespace pe
+} // namespace walberla