diff --git a/examples/modular/force_reduction.cpp b/examples/modular/force_reduction.cpp
index e884b3d0acab91189ffd4465364d9e24319e8992..7e770a58704c2ff928a395f9111094557406af43 100644
--- a/examples/modular/force_reduction.cpp
+++ b/examples/modular/force_reduction.cpp
@@ -15,8 +15,8 @@ int main(int argc, char **argv) {
     // Create bodies
     pairs::id_t pUid = pairs::create_sphere(pairs_runtime, 0.0499,   0.0499,   0.07,   0.5, 0.5, 0 ,   1000, 0.0045, 0, 0);
     
-    // setup_sim after creating all bodies
-    pairs_sim->setup_sim();
+    // setup_cells after creating all bodies
+    pairs_sim->setup_cells();
     pairs_sim->update_mass_and_inertia();
 
     // Track particle
diff --git a/examples/modular/sd_1.cpp b/examples/modular/sd_1.cpp
index e56cfc5943c7626d97d6b872524655b9dd0dca0b..d7e0dcae798ee4b8e46013bd9e872ad97ea76eb4 100644
--- a/examples/modular/sd_1.cpp
+++ b/examples/modular/sd_1.cpp
@@ -21,7 +21,7 @@ int main(int argc, char **argv) {
     pairs::create_sphere(pairs_runtime, 0.6, 0.6, 0.7,      -2, -2, 0,  1000, 0.05, 0, 0);
     pairs::create_sphere(pairs_runtime, 0.4, 0.4, 0.68,    2, 2, 0,    1000, 0.05, 0, 0);
 
-    pairs_sim->setup_sim(0.1, 0.1, 0.1, 0.1);
+    pairs_sim->setup_cells(0.1, 0.1, 0.1, 0.1);
     pairs_sim->update_mass_and_inertia();
 
     int num_timesteps = 2000;
diff --git a/examples/modular/sd_2.cpp b/examples/modular/sd_2.cpp
index 4bdb4c85a37efef0391ccecc9d2a1e1df6e3313d..3ae126949f934aa2d1eff1eb3d0d895a26afec9d 100644
--- a/examples/modular/sd_2.cpp
+++ b/examples/modular/sd_2.cpp
@@ -42,7 +42,7 @@ int main(int argc, char **argv) {
     pairs::create_sphere(pairs_runtime, 0.6, 0.6, 0.7,      -2, -2, 0,  1000, 0.05, 0, 0);
     pairs::create_sphere(pairs_runtime, 0.4, 0.4, 0.68,    2, 2, 0,    1000, 0.05, 0, 0);
 
-    pairs_sim->setup_sim(0.1, 0.1, 0.1, 0.1);
+    pairs_sim->setup_cells(0.1, 0.1, 0.1, 0.1);
     pairs_sim->update_mass_and_inertia();
 
     int num_timesteps = 2000;
diff --git a/examples/modular/sd_3_GPU.cu b/examples/modular/sd_3_GPU.cu
index b44af846643ed9cf0a730b07c5f60543560e29b8..bf95b2037f366cb417320f22a34c24591a1f7ebc 100644
--- a/examples/modular/sd_3_GPU.cu
+++ b/examples/modular/sd_3_GPU.cu
@@ -65,7 +65,7 @@ int main(int argc, char **argv) {
 
     auto pIsLocalInMyRank = [&](pairs::id_t uid){return ac->uidToIdxLocal(uid) != ac->getInvalidIdx();};
 
-    pairs_sim->setup_sim(0.1, 0.1, 0.1, 0.1);
+    pairs_sim->setup_cells(0.1, 0.1, 0.1, 0.1);
     pairs_sim->update_mass_and_inertia();
 
     pairs_sim->communicate(0);
diff --git a/examples/modular/sd_4.cpp b/examples/modular/sd_4.cpp
index 80ec40313e13692d1b41fcee01f6b5ce7d4ef91b..e83e66d1d4e29f2d281b2673ace6047cba7a3eb2 100644
--- a/examples/modular/sd_4.cpp
+++ b/examples/modular/sd_4.cpp
@@ -41,7 +41,6 @@ int main(int argc, char **argv) {
 
     pairs_runtime->initDomain(&argc, &argv, 
                     0, 0, 0, 20, 20, 20,    // Domain bounds
-                    false, false, false,    // PBCs --------------> TODO: runtime pbc
                     true                    // Enable dynamic load balancing (does initial refinement on a <1,1,1> blockforest)
                 ); 
 
@@ -61,7 +60,7 @@ int main(int argc, char **argv) {
     
     double lcw = diameter_max * 1.01;       // Linked-cell width
     double interaction_radius = diameter_max;
-    pairs_sim->setup_sim(lcw, lcw, lcw, interaction_radius);
+    pairs_sim->setup_cells(lcw, lcw, lcw, interaction_radius);
 
     pairs_sim->update_mass_and_inertia();
 
diff --git a/runtime/domain/block_forest.cpp b/runtime/domain/block_forest.cpp
index 0851f2c1733243792909dff5e15b8a8ddc9bb2e6..d19fcc683dc3037dd331af148d1f5bb89e1dae16 100644
--- a/runtime/domain/block_forest.cpp
+++ b/runtime/domain/block_forest.cpp
@@ -24,8 +24,8 @@ namespace pairs {
 
 BlockForest::BlockForest(
         PairsRuntime *ps_,
-        real_t xmin, real_t xmax, real_t ymin, real_t ymax, real_t zmin, real_t zmax, bool pbcx, bool pbcy, bool pbcz, bool balance_workload_) :
-        DomainPartitioner(xmin, xmax, ymin, ymax, zmin, zmax), ps(ps_), globalPBC{pbcx, pbcy, pbcz}, balance_workload(balance_workload_) {
+        real_t xmin, real_t xmax, real_t ymin, real_t ymax, real_t zmin, real_t zmax, bool balance_workload_) :
+        DomainPartitioner(xmin, xmax, ymin, ymax, zmin, zmax), ps(ps_), balance_workload(balance_workload_) {
 
         subdom = new real_t[ndims * 2];
 }
@@ -35,8 +35,7 @@ BlockForest::BlockForest(PairsRuntime *ps_, const std::shared_ptr<walberla::bloc
         DomainPartitioner(bf->getDomain().xMin(), bf->getDomain().xMax(),
                         bf->getDomain().yMin(), bf->getDomain().yMax(),
                         bf->getDomain().zMin(), bf->getDomain().zMax()), 
-        ps(ps_), 
-        globalPBC{bf->isXPeriodic(), bf->isYPeriodic(), bf->isZPeriodic()} {
+        ps(ps_) {
             subdom = new real_t[ndims * 2];
             mpiManager = walberla::mpi::MPIManager::instance();
             world_size = mpiManager->numProcesses();
@@ -60,10 +59,8 @@ void BlockForest::updateNeighborhood() {
         for(uint neigh = 0; neigh < block->getNeighborhoodSize(); ++neigh) {
             auto neighbor_rank = walberla::int_c(block->getNeighborProcess(neigh));
 
-            // Neighbor blocks that belong to the same rank should be added to 
-            // neighboorhood only if there's PBC along any dim, otherwise they should be skipped.
             // TODO: Make PBCs work with runtime load balancing
-            if((neighbor_rank != me) || globalPBC[0] || globalPBC[1] || globalPBC[2]) {
+            // if(neighbor_rank != me) {
                 const walberla::BlockID& neighbor_id = block->getNeighborId(neigh);
                 walberla::math::AABB neighbor_aabb = block->getNeighborAABB(neigh);
                 auto begin = blocks_pushed[neighbor_rank].begin();
@@ -73,7 +70,7 @@ void BlockForest::updateNeighborhood() {
                     neighborhood[neighbor_rank].push_back(neighbor_aabb);
                     blocks_pushed[neighbor_rank].push_back(neighbor_id);
                 }
-            }
+            // }
         }
     }
 
@@ -264,7 +261,8 @@ void BlockForest::initialize(int *argc, char ***argv) {
 
     auto ref_level = balance_workload ? getInitialRefinementLevel(procs) : 0;
 
-    walberla::Vector3<bool> pbc(globalPBC[0], globalPBC[1], globalPBC[2]);
+    // PBC's are forced to true here and sperately handled when determining ghosts 
+    walberla::Vector3<bool> pbc(true, true, true);
 
     forest = walberla::blockforest::createBlockForest(domain, block_config, pbc, procs, ref_level);
 
@@ -298,7 +296,7 @@ void BlockForest::update() {
         // PAIRS_DEBUG("Rebalance\n");
         if (rank==0) std::cout << "Rebalance" << std::endl;
         forest->refresh(); 
-}
+    }
 
     this->updateNeighborhood();
     this->setBoundingBox();
diff --git a/runtime/domain/block_forest.hpp b/runtime/domain/block_forest.hpp
index d814d02c423358b99f11622bbe2ed7f88b557f97..bfda71b0d7acc2f0c353c43b7de61c572a762edc 100644
--- a/runtime/domain/block_forest.hpp
+++ b/runtime/domain/block_forest.hpp
@@ -40,14 +40,13 @@ private:
     std::vector<double> aabbs;
     PairsRuntime *ps;
     real_t *subdom;
-    const bool globalPBC[3];
     int world_size, rank, nranks, total_aabbs;
     bool balance_workload = false;
 
 public:
     BlockForest(
         PairsRuntime *ps_,
-        real_t xmin, real_t xmax, real_t ymin, real_t ymax, real_t zmin, real_t zmax, bool pbcx, bool pbcy, bool pbcz, bool balance_workload_);
+        real_t xmin, real_t xmax, real_t ymin, real_t ymax, real_t zmin, real_t zmax, bool balance_workload_);
 
     BlockForest(PairsRuntime *ps_, const std::shared_ptr<walberla::blockforest::BlockForest> &bf);
 
diff --git a/runtime/pairs.cpp b/runtime/pairs.cpp
index d1c2d635d230c727853159c5f1ac4abec446591f..351aa36b364ee200b29f06cb9d6cf9827f2dd381 100644
--- a/runtime/pairs.cpp
+++ b/runtime/pairs.cpp
@@ -15,7 +15,6 @@ namespace pairs {
 void PairsRuntime::initDomain(
     int *argc, char ***argv,
     real_t xmin, real_t ymin, real_t zmin, real_t xmax, real_t ymax, real_t zmax, 
-    bool pbcx, bool pbcy, bool pbcz, 
     bool balance_workload) {
 
     int mpi_initialized=0;
@@ -40,7 +39,7 @@ void PairsRuntime::initDomain(
     
 #ifdef USE_WALBERLA
     else if(dom_part_type == BlockForestPartitioning) {
-        dom_part = new BlockForest(this, xmin, xmax, ymin, ymax, zmin, zmax, pbcx, pbcy, pbcz, balance_workload);
+        dom_part = new BlockForest(this, xmin, xmax, ymin, ymax, zmin, zmax, balance_workload);
     } 
 #endif
 
diff --git a/runtime/pairs.hpp b/runtime/pairs.hpp
index 14ffc5a283394cf23806b6c59916778da2b9be07..d582d277333ab10995d9973e3ab7791ba977bdba 100644
--- a/runtime/pairs.hpp
+++ b/runtime/pairs.hpp
@@ -321,7 +321,7 @@ public:
     void initDomain(
         int *argc, char ***argv,
         real_t xmin, real_t ymin, real_t zmin, real_t xmax, real_t ymax, real_t zmax, 
-        bool pbcx = 0, bool pbcy = 0, bool pbcz = 0, bool balance_workload = 0);
+        bool balance_workload = false);
 
     template<typename Domain_T>
     void useDomain(const std::shared_ptr<Domain_T> &domain_ptr);
diff --git a/src/pairs/code_gen/cgen.py b/src/pairs/code_gen/cgen.py
index 028c09087aff54ffae963332b6f2e7b4549b20a5..c8078a873ec21f05423591b30c623508bc479a7a 100644
--- a/src/pairs/code_gen/cgen.py
+++ b/src/pairs/code_gen/cgen.py
@@ -186,12 +186,6 @@ class CGen:
         if not module.interface:
             module_params += ["PairsRuntime *pairs_runtime", "struct PairsObjects *pobj"]
 
-        if module.name=="initialize" and self.sim.create_domain_at_initialization:
-            module_params += ["int argc", "char **argv"]
-
-        if module.name=="set_domain":
-            module_params += ["int argc", "char **argv"]
-
         module_params += [f"{Types.c_keyword(self.sim, param.type())} {param.name()}" for param in module.parameters()]
 
         print_params = ", ".join(module_params)
@@ -923,9 +917,6 @@ class CGen:
         if isinstance(ast_node, ModuleCall):
             module_params = ["pairs_runtime", "pobj"]
 
-            if ast_node.module.name=="init_domain":
-                module_params += ["argc", "argv"]
-
             module_params += [f"{param.name()}" for param in ast_node.module.parameters()]
 
             print_params = ", ".join(module_params)
@@ -1106,9 +1097,6 @@ class CGen:
             if ast_node.name().startswith("pairs::"):
                 extra_params += ["pairs_runtime"]
 
-            if ast_node.name() == "pairs_runtime->initDomain":
-                extra_params += ["&argc", "&argv"]
-
             params = ", ".join(extra_params + [str(self.generate_expression(p)) for p in ast_node.parameters()])
             return f"{ast_node.name()}({params})"
 
diff --git a/src/pairs/code_gen/interface.py b/src/pairs/code_gen/interface.py
index cf59ca280fe9425b6fa8b364f4e643e60a051140..78d5a851fc55c61187e7aea84181cbc5a3f9e0a3 100644
--- a/src/pairs/code_gen/interface.py
+++ b/src/pairs/code_gen/interface.py
@@ -28,7 +28,7 @@ class InterfaceModules:
 
     def create_all(self):
         self.initialize()
-        self.setup_sim()
+        self.setup_cells()
         self.update_domain()
         self.update_cells(self.sim.reneighbor_frequency) 
         self.communicate(self.sim.reneighbor_frequency)
@@ -60,6 +60,9 @@ class InterfaceModules:
         PrintCode(self.sim, f"pairs_runtime = new PairsRuntime({nprops}, {ncontactprops}, {narrays}, {part});")
         PrintCode(self.sim, f"pobj = new PairsObjects();")
 
+        if self.sim.grid is None:
+            self.sim.grid = MutableGrid(self.sim, self.sim.dims)
+
         inits = Block.from_list(self.sim, [
             DeclareVariables(self.sim),
             DeclareArrays(self.sim),
@@ -70,16 +73,11 @@ class InterfaceModules:
             RegisterMarkers(self.sim)
         ])
 
-        if self.sim.create_domain_at_initialization:
-            self.sim.add_statement(Block.merge_blocks(inits, self.sim.create_domain))
-        else:
-            assert self.sim.grid is None, "A grid already exists"
-            self.sim.grid = MutableGrid(self.sim, self.sim.dims)
-            self.sim.add_statement(inits)
+        self.sim.add_statement(inits)
 
     @pairs_interface_block
-    def setup_sim(self):
-        self.sim.module_name('setup_sim')
+    def setup_cells(self):
+        self.sim.module_name('setup_cells')
         
         if self.sim.cell_lists.runtime_spacing:
             for d in range(self.sim.dims):
@@ -88,7 +86,6 @@ class InterfaceModules:
         if self.sim.cell_lists.runtime_cutoff_radius:
             Assign(self.sim, self.sim.cell_lists.cutoff_radius, Parameter(self.sim, 'cutoff_radius', Types.Real))
 
-        self.sim.add_statement(self.sim.setup_particles)
         # This update assumes all particles have been created exactly in the rank that contains them 
         self.sim.add_statement(UpdateDomain(self.sim))  
         self.sim.add_statement(BuildCellListsStencil(self.sim, self.sim.cell_lists))
diff --git a/src/pairs/sim/domain.py b/src/pairs/sim/domain.py
index 2560f1a8759727a4d7c0167da12955a88c67aa63..dfa73de79e07c2deb4476c3ae4e4f8902c685b63 100644
--- a/src/pairs/sim/domain.py
+++ b/src/pairs/sim/domain.py
@@ -1,14 +1,6 @@
 from pairs.ir.block import pairs_inline
 from pairs.sim.lowerable import Lowerable
 
-class InitializeDomain(Lowerable):
-    def __init__(self, sim):
-        super().__init__(sim)
-
-    @pairs_inline
-    def lower(self):
-        self.sim.domain_partitioning().initialize()
-
 class UpdateDomain(Lowerable):
     def __init__(self, sim):
         super().__init__(sim)
diff --git a/src/pairs/sim/domain_partitioning.py b/src/pairs/sim/domain_partitioning.py
index e72e445bf5c3f029c0b112aacbe8d125fdc53a56..10fac21f2b0010ee3de8b5fc1c8b19e973576c09 100644
--- a/src/pairs/sim/domain_partitioning.py
+++ b/src/pairs/sim/domain_partitioning.py
@@ -45,10 +45,6 @@ class DimensionRanges:
     def reduce_sum_step_indexes(self, step, array):
        return sum([array[i] for i in self.step_indexes(step)])
 
-    def initialize(self):
-        grid_array = [self.sim.grid.min(d) for d in range(self.sim.ndims())] + [self.sim.grid.max(d) for d in range(self.sim.ndims())]
-        Call_Void(self.sim, "pairs_runtime->initDomain", grid_array)
-
     def update(self):
         Call_Void(self.sim, "pairs_runtime->updateDomain", [])
         Assign(self.sim, self.rank, Call_Int(self.sim, "pairs_runtime->getDomainPartitioner()->getRank", []))
@@ -140,25 +136,10 @@ class BlockForest:
             
         return self.reduce_step
 
-    def initialize(self):
-        grid_array = [self.sim.grid.min(d) for d in range(self.sim.ndims())] + [self.sim.grid.max(d) for d in range(self.sim.ndims())]
-
-        Call_Void(self.sim, "pairs_runtime->initDomain", 
-                  grid_array + self.sim._pbc + ([True] if self.load_balancer is not None else []))
-        
-        if self.load_balancer is not None:
-            PrintCode(self.sim, "pairs_runtime->getDomainPartitioner()->initWorkloadBalancer"
-                      f"({LoadBalancingAlgorithms.c_keyword(self.load_balancer)}, {self.regrid_min}, {self.regrid_max});")
-
-            # Call_Void(self.sim, "pairs_runtime->getDomainPartitioner()->initWorkloadBalancer", 
-            #           [self.load_balancer, self.regrid_min, self.regrid_max])
-
     def update(self):
         Call_Void(self.sim, "pairs_runtime->updateDomain", [])
         Assign(self.sim, self.rank, Call_Int(self.sim, "pairs_runtime->getDomainPartitioner()->getRank", []))
         Assign(self.sim, self.nranks, Call_Int(self.sim, "pairs_runtime->getNumberOfNeighborRanks", []))
-
-        # for _ in Filter(self.sim, ScalarOp.neq(self.nranks, 0)): # TODO: Test different block configs with PBC
         Assign(self.sim, self.ntotal_aabbs, Call_Int(self.sim, "pairs_runtime->getNumberOfNeighborAABBs", []))
 
         for _ in Filter(self.sim, self.nranks_capacity < self.nranks):
@@ -229,7 +210,7 @@ class BlockForest:
                             for _ in Filter(self.sim, full_cond):
                                 yield i, r, self.ranks[r], pbc_shifts
 
-                        for _ in Filter(self.sim, ScalarOp.cmp(self.ranks[r] , self.rank)):     # if my neighbor is me (cuz I'm the only rank in a dimension that has pbc)
+                        for _ in Filter(self.sim, ScalarOp.cmp(self.ranks[r] , self.rank)):     # if my neighbor is me
                             pbc_shifts = []
                             isghost = Lit(self.sim, 0)
 
diff --git a/src/pairs/sim/simulation.py b/src/pairs/sim/simulation.py
index 432d7e5a61acc7d5000a7204f3570c02ef9b48a7..9ac99771b38b07f860657e2b9bc9f8555526fff4 100644
--- a/src/pairs/sim/simulation.py
+++ b/src/pairs/sim/simulation.py
@@ -17,7 +17,7 @@ from pairs.sim.comm import Comm, Synchronize, Borders, Exchange, ReverseComm
 from pairs.sim.contact_history import ContactHistory, BuildContactHistory, ClearUnusedContactHistory, ResetContactHistoryUsageStatus
 from pairs.sim.copper_fcc_lattice import CopperFCCLattice
 from pairs.sim.dem_sc_grid import DEMSCGrid
-from pairs.sim.domain import InitializeDomain, UpdateDomain
+from pairs.sim.domain import UpdateDomain
 from pairs.sim.domain_partitioners import DomainPartitioners
 from pairs.sim.domain_partitioning import BlockForest, DimensionRanges
 from pairs.sim.load_balancing_algorithms import LoadBalancingAlgorithms
@@ -93,11 +93,6 @@ class Simulation:
         self._capture_statements = True
         self._block = Block(self, [])
 
-        # Different segments of particle code/functions
-        self.create_domain = Block(self, [])
-        self.create_domain_at_initialization = False
-
-        self.setup_particles = Block(self, [])
         self.module_list = []
         self.kernel_list = []
 
@@ -311,35 +306,12 @@ class Simulation:
         return self.vars.find(var_name)
 
     def set_domain(self, grid):
-        """Set domain bounds. 
-        If the domain is set through this function, the 'set_domain' module won't be generated in the modular version.
-        Use this function only if you do not need to set domain at runtime.
-        This function is required only for whole-program generation."""
-        self.create_domain_at_initialization = True
+        """Set domain bounds if they are known at P4IRS compile time"""
         self.grid = Grid3D(self, grid[0], grid[1], grid[2], grid[3], grid[4], grid[5])
-        self.create_domain.add_statement(InitializeDomain(self))
 
     def reneighbor_every(self, frequency):
         self.reneighbor_frequency = frequency
 
-    def create_particle_lattice(self, grid, spacing, props={}):
-        self.setup_particles.add_statement(ParticleLattice(self, grid, spacing, props, self.position()))
-
-    def read_particle_data(self, filename, prop_names, shape_id):
-        """Generate statement to read particle data from file"""
-        props = [self.property(prop_name) for prop_name in prop_names]
-        self.setup_particles.add_statement(ReadParticleData(self, filename, props, shape_id))
-
-    def copper_fcc_lattice(self, nx, ny, nz, rho, temperature, ntypes):
-        """Specific initialization for MD Copper FCC lattice case"""
-        self.setup_particles.add_statement(CopperFCCLattice(self, nx, ny, nz, rho, temperature, ntypes))
-
-    def dem_sc_grid(self, xmax, ymax, zmax, spacing, diameter, min_diameter, max_diameter, initial_velocity, particle_density, ntypes):
-        """Specific initialization for DEM grid"""
-        self.setup_particles.add_statement(
-            DEMSCGrid(self, xmax, ymax, zmax, spacing, diameter, min_diameter, max_diameter,
-                      initial_velocity, particle_density, ntypes))
-
     def build_cell_lists(self, spacing=None, store_neighbors_per_cell=False):
         """Add routines to build the linked-cells acceleration structure.
         Leave spacing as None so it can be set at runtime."""
@@ -546,7 +518,7 @@ class Simulation:
 
         # Generate getters for the runtime functions
         self.code_gen.generate_interfaces()
-
+"""
     def generate_program(self):
         assert self.grid, "No domain is created. Set domain bounds with 'set_domain'."
 
@@ -652,3 +624,4 @@ class Simulation:
 
         # Generate getters for the runtime functions
         self.code_gen.generate_interfaces()
+"""
\ No newline at end of file