void Configuration::loadParameters(Parameters & parameters, const MPI_Comm & communicator,std::string parallelTag){
    tinyxml2::XMLDocument confFile;
    tinyxml2::XMLElement *node;
    tinyxml2::XMLElement *subNode;


    int rank;

    MPI_Comm_rank(communicator, &rank);

    if (rank == 0){

        // Parse the configuration file and check validity
        confFile.LoadFile(_filename.c_str());
        if (confFile.FirstChildElement() == NULL){
            handleError(1, "Error parsing the configuration file");
        }

        //--------------------------------------------------
        // Load geometric parameters
        //--------------------------------------------------
        node = confFile.FirstChildElement()->FirstChildElement("geometry");

        if (node == NULL){
            handleError(1, "Error loading geometry properties");
        }

        readIntMandatory(parameters.geometry.sizeX, node, "sizeX");
        readIntMandatory(parameters.geometry.sizeY, node, "sizeY");
        readIntOptional (parameters.geometry.sizeZ, node, "sizeZ");

        if (parameters.geometry.sizeX < 2 || parameters.geometry.sizeY < 2 ||
                parameters.geometry.sizeZ < 0){
            handleError(1, "Invalid size specified in configuration file");
        }

        parameters.geometry.dim = 0;
        if (node->QueryIntAttribute("dim", &(parameters.geometry.dim)) !=
                tinyxml2::XML_WRONG_ATTRIBUTE_TYPE){
            if (parameters.geometry.dim == 0){
                if (parameters.geometry.sizeZ == 0){
                    parameters.geometry.sizeZ = 1;
                    parameters.geometry.dim = 2;
                } else {
                    parameters.geometry.dim = 3;
                }
            }
        }

        if (parameters.geometry.dim == 3 && parameters.geometry.sizeZ == 1){
            handleError(1, "Inconsistent data: 3D geometry specified with Z size zero");
        }

        // Determine the sizes of the cells
        readFloatOptional (parameters.geometry.dx, node, "dx");
        readFloatOptional (parameters.geometry.dy, node, "dy");
        readFloatOptional (parameters.geometry.dz, node, "dz");

        FLOAT lengthX = 0,
              lengthY = 0,
              lengthZ = 0;

        readFloatOptional (lengthX, node, "lengthX");
        readFloatOptional (lengthY, node, "lengthY");
        readFloatOptional (lengthZ, node, "lengthZ");


        // Do not define both overall length and cell length
        if ((parameters.geometry.dx && lengthX) || !(parameters.geometry.dx || lengthX)){
            handleError(1,"Invalid definition for size. Define either length of element or length of domain for dimension X");
        }
        if ((parameters.geometry.dy && lengthY) || !(parameters.geometry.dy || lengthY)){
            handleError(1,"Invalid definition for size. Define either length of element or length of domain for dimension Y");
        }
        if ((parameters.geometry.dz && lengthZ) || !(parameters.geometry.dz || lengthZ)){
            handleError(1,"Invalid definition for size. Define either length of element or length of domain for dimension Z");
        }

        if (lengthX){
            parameters.geometry.dx = lengthX / parameters.geometry.sizeX;
        }
        if (lengthY){
            parameters.geometry.dy = lengthY / parameters.geometry.sizeY;
        }
        if (lengthZ){
            parameters.geometry.dz = lengthZ / parameters.geometry.sizeZ;
        }
        // Now, the size of the elements should be set


        if (parameters.geometry.dx <= 0 || parameters.geometry.dy <= 0){
            handleError(1, "Invalid specification for dx or dy in the configuration file");
        }

        if (parameters.geometry.dz <= 0 && parameters.geometry.dim == 3){
            handleError(1, "Invalid specification for dz in the configuration file");
        }

        _dim = parameters.geometry.dim;

        //--------------------------------------------------
        // Timestep parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("timestep");

        if (node == NULL){
            handleError(1, "Error loading timestep parameters");
        }

        readFloatOptional(parameters.timestep.dt, node, "dt", 1);
        readFloatOptional(parameters.timestep.tau, node, "tau", 0.5);

        //--------------------------------------------------
        // Flow parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("flow");

        if (node == NULL){
            handleError(1, "Error loading flow parameters");
        }

        readFloatMandatory(parameters.flow.Re, node, "Re");

        //--------------------------------------------------
        // Solver parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("solver");

        if (node == NULL){
            handleError(1, "Error loading solver parameters");
        }

        readFloatMandatory(parameters.solver.gamma, node, "gamma");
        readIntOptional (parameters.solver.maxIterations, node, "maxIterations");

        //--------------------------------------------------
        // Environmental parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("environment");

        if (node == NULL){
            handleError(1, "Error loading environmental  parameters");
        }

        readFloatOptional(parameters.environment.gx, node, "gx");
        readFloatOptional(parameters.environment.gy, node, "gy");
        readFloatOptional(parameters.environment.gz, node, "gz");

        //--------------------------------------------------
        // Simulation parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("simulation");

        if (node == NULL){
            handleError(1, "Error loading simulation parameters");
        }

        readFloatMandatory(parameters.simulation.finalTime, node, "finalTime");

        subNode = node->FirstChildElement("type");
        if (subNode != NULL){
            readStringMandatory(parameters.simulation.type, subNode);
        } else {
            handleError (1, "Missing type in simulation parameters");
        }

        subNode = node->FirstChildElement("scenario");
        if (subNode != NULL){
            readStringMandatory(parameters.simulation.scenario, subNode);
        } else {
            handleError (1, "Missing scenario in simulation parameters");
        }

        //--------------------------------------------------
        // VTK parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("vtk");

        if (node == NULL){
            handleError(1, "Error loading VTK parameters");
        }

        readFloatOptional(parameters.vtk.interval, node, "interval");
        readStringMandatory(parameters.vtk.prefix, node);

        //--------------------------------------------------
        // StdOut parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("stdOut");

        if (node == NULL){
            handleError(1, "Error loading StdOut parameters");
        }

        // If no value given, print every step
        readIntOptional(parameters.stdOut.interval, node, "interval", 1);

        //--------------------------------------------------
        // Parallel parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement(parallelTag.c_str());

        if (node == NULL){
            handleError(1, "Error loading parallel parameters");
        }

        readIntOptional(parameters.parallel.numProcessors[0], node, "numProcessorsX", 1);
        readIntOptional(parameters.parallel.numProcessors[1], node, "numProcessorsY", 1);
        readIntOptional(parameters.parallel.numProcessors[2], node, "numProcessorsZ", 1);

        // Start neighbors on null in case that no parallel configuration is used later.
        parameters.parallel.leftNb = MPI_PROC_NULL;
        parameters.parallel.rightNb = MPI_PROC_NULL;
        parameters.parallel.bottomNb = MPI_PROC_NULL;
        parameters.parallel.topNb = MPI_PROC_NULL;
        parameters.parallel.frontNb = MPI_PROC_NULL;
        parameters.parallel.backNb = MPI_PROC_NULL;

        // Yet more parameters initialized in case that no parallel configuration is applied
        parameters.parallel.localSize[0] = parameters.geometry.sizeX;
        parameters.parallel.localSize[1] = parameters.geometry.sizeY;
        parameters.parallel.localSize[2] = parameters.geometry.sizeZ;

        parameters.parallel.firstCorner[0] = 0;
        parameters.parallel.firstCorner[1] = 0;
        parameters.parallel.firstCorner[2] = 0;

        // VTK output is named after the rank, so we define it here, again, in case that it's not
        // initialized anywhere else.
        parameters.parallel.rank = rank;

        //--------------------------------------------------
        // Walls
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("walls");

        if (node == NULL){
            handleError(1, "Error loading wall parameters");
        }

        readBoolOptional(parameters.walls.periodicX, node, "periodicX");
        readBoolOptional(parameters.walls.periodicY, node, "periodicY");
        readBoolOptional(parameters.walls.periodicZ, node, "periodicZ");

        tinyxml2::XMLElement *wall;
        wall = node->FirstChildElement("left");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorLeft, parameters.walls.scalarLeft);
        }

        wall = node->FirstChildElement("right");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorRight, parameters.walls.scalarRight);
        }

        wall = node->FirstChildElement("bottom");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorBottom, parameters.walls.scalarBottom);
        }

        wall = node->FirstChildElement("top");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorTop, parameters.walls.scalarTop);
        }

        wall = node->FirstChildElement("front");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorFront, parameters.walls.scalarFront);
        }

        wall = node->FirstChildElement("back");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorBack, parameters.walls.scalarBack);
        }

        // Set the scalar values to zero
        parameters.walls.scalarLeft   = 0.0;
        parameters.walls.scalarRight  = 0.0;
        parameters.walls.scalarBottom = 0.0;
        parameters.walls.scalarTop    = 0.0;
        parameters.walls.scalarFront  = 0.0;
        parameters.walls.scalarBack   = 0.0;


        //--------------------------------------------------
        // Lattice Boltzmann
        //--------------------------------------------------
        node = confFile.FirstChildElement()->FirstChildElement("lb");
        if (node != NULL){
            readFloatMandatory(parameters.lb.tau, node, "tau");
            parameters.lb.reciprocalTau = 1.0 / parameters.lb.tau;
            parameters.lb.viscosity = 1.0 / 3.0 * (parameters.lb.tau - 0.5);
        }


        //--------------------------------------------------
        // Backward facing step
        //--------------------------------------------------
        parameters.bfStep.xRatio = -1.0;
        parameters.bfStep.yRatio = -1.0;
        node = confFile.FirstChildElement()->FirstChildElement("backwardFacingStep");
        if (node != NULL){
            readFloatMandatory(parameters.bfStep.xRatio, node, "xRatio");
            readFloatMandatory(parameters.bfStep.yRatio, node, "yRatio");
        }
        parameters.bfStep.width = (int)(parameters.geometry.sizeX * parameters.bfStep.xRatio);
        parameters.bfStep.height = (int)(parameters.geometry.sizeY * parameters.bfStep.yRatio);


        //--------------------------------------------------
        // Coupling
        //--------------------------------------------------
        node = confFile.FirstChildElement()->FirstChildElement("coupling");
        if (node != NULL){
            int value;

            readIntMandatory(value, node, "offsetNSX");
            parameters.coupling.offsetNS[0] = value + 2;
            readIntMandatory(value, node, "offsetNSY");
            parameters.coupling.offsetNS[1] = value + 2;
            readIntMandatory(value, node, "offsetNSZ");
            parameters.coupling.offsetNS[2] = value + 2;

            readIntMandatory(parameters.coupling.sizeNS[0], node, "sizeNSX");
            readIntMandatory(parameters.coupling.sizeNS[1], node, "sizeNSY");
            readIntMandatory(parameters.coupling.sizeNS[2], node, "sizeNSZ");

            readIntMandatory(parameters.coupling.ratio, node, "ratio");
            readFloatMandatory(parameters.coupling.refLength, node, "refLength");
            readIntOptional (parameters.coupling.overlap, node, "overlap", 2);

            readBoolOptional (parameters.coupling.set, node, "active", true);
        } else {
            parameters.coupling.set = false;
        }

        //--------------------------------------------------
        // Time dependency
        //--------------------------------------------------
        node = confFile.FirstChildElement()->FirstChildElement("timeDependency");
        if (node != NULL){
            FLOAT value;
            readFloatOptional(value, node, "period", 0);
            parameters.timeDependency.period = value;
            if (value) {
                parameters.timeDependency.reciprocalPeriod = 1.0 / value;
            } else {
                parameters.timeDependency.reciprocalPeriod = 0.0;
            }
        }

    }

    // Broadcasting of the values
    MPI_Bcast(&(parameters.geometry.sizeX), 1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.sizeY), 1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.sizeZ), 1, MPI_INT, 0, communicator);

    MPI_Bcast(&(parameters.geometry.dim), 1, MPI_INT, 0, communicator);

    MPI_Bcast(&(parameters.geometry.dx), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.dy), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.dz), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.timestep.dt),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.timestep.tau), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.flow.Re), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.solver.gamma),         1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.solver.maxIterations), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.environment.gx), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.environment.gy), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.environment.gz), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.simulation.finalTime), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.vtk.interval), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.stdOut.interval), 1, MPI_INT, 0, communicator);

    broadcastString (parameters.vtk.prefix, communicator);
    broadcastString (parameters.simulation.type, communicator);
    broadcastString (parameters.simulation.scenario, communicator);

    MPI_Bcast(&(parameters.lb.tau), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.lb.reciprocalTau), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.lb.viscosity), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.bfStep.xRatio), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.bfStep.yRatio), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.bfStep.width), 1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.bfStep.height), 1, MPI_INT, 0, communicator);

    MPI_Bcast(parameters.parallel.numProcessors, 3, MPI_INT, 0, communicator);

    MPI_Bcast(&(parameters.walls.scalarLeft),   1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarRight),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarBottom), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarTop),    1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarFront),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarBack),   1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(parameters.walls.vectorLeft,   3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorRight,  3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorBottom, 3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorTop,    3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorFront,  3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorBack,   3, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(parameters.coupling.offsetNS,   3, MPI_INT, 0, communicator);
    MPI_Bcast(parameters.coupling.sizeNS,     3, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.coupling.ratio),   1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.coupling.overlap), 1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.coupling.refLength),   1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.coupling.set), sizeof(bool), MPI_BYTE, 0, communicator);

    MPI_Bcast(&(parameters.timeDependency.period), 1, MY_MPI_FLOAT, 0, communicator);

    // No bool data type in C MPI. Therefore, using a bad method. This will not work if boolean is
    // represented differently in cores in the network
    MPI_Bcast(&(parameters.walls.periodicX), sizeof(bool), MPI_BYTE, 0, communicator);
    MPI_Bcast(&(parameters.walls.periodicY), sizeof(bool), MPI_BYTE, 0, communicator);
    MPI_Bcast(&(parameters.walls.periodicZ), sizeof(bool), MPI_BYTE, 0, communicator);
}
void Configuration::loadParameters(Parameters & parameters, const MPI_Comm & communicator){
    tinyxml2::XMLDocument confFile;
    tinyxml2::XMLElement *node;
    tinyxml2::XMLElement *subNode;


    int rank;

    MPI_Comm_rank(communicator, &rank);

    // we only read on rank 0; afterwards, all configuration parameters are broadcasted to all processes.
    // So, if you add new parameters in the configuration, make sure to broadcast them to the other processes!
    if (rank == 0){

        // Parse the configuration file and check validity
        confFile.LoadFile(_filename.c_str());
        if (confFile.FirstChildElement() == NULL){
            handleError(1, "Error parsing the configuration file");
        }

        //--------------------------------------------------
        // Load geometric parameters
        //--------------------------------------------------
        node = confFile.FirstChildElement()->FirstChildElement("geometry");

        if (node == NULL){
            handleError(1, "Error loading geometry properties");
        }

        readIntMandatory(parameters.geometry.sizeX, node, "sizeX");
        readIntMandatory(parameters.geometry.sizeY, node, "sizeY");
        readIntOptional (parameters.geometry.sizeZ, node, "sizeZ");

        if (parameters.geometry.sizeX < 2 || parameters.geometry.sizeY < 2 ||
                parameters.geometry.sizeZ < 0){
            handleError(1, "Invalid size specified in configuration file");
        }

        parameters.geometry.dim = 0;
        if (node->QueryIntAttribute("dim", &(parameters.geometry.dim)) !=
                tinyxml2::XML_WRONG_ATTRIBUTE_TYPE){
            if (parameters.geometry.dim == 0){
                if (parameters.geometry.sizeZ == 0){
                    parameters.geometry.sizeZ = 1;
                    parameters.geometry.dim = 2;
                } else {
                    parameters.geometry.dim = 3;
                }
            }
        }

        if (parameters.geometry.dim == 3 && parameters.geometry.sizeZ == 1){
            handleError(1, "Inconsistent data: 3D geometry specified with Z size zero");
        }

        // Determine the sizes of the cells

        readFloatMandatory (parameters.geometry.lengthX, node, "lengthX");
        readFloatMandatory (parameters.geometry.lengthY, node, "lengthY");
        readFloatMandatory (parameters.geometry.lengthZ, node, "lengthZ");
        // read geometry->meshsize parameters
        std::string meshsizeType="";
        subNode = node->FirstChildElement("mesh");
        readStringMandatory(meshsizeType,subNode);
        if (meshsizeType == "uniform"){
          parameters.geometry.meshsizeType = Uniform;
        } else if (meshsizeType == "stretched"){
          parameters.geometry.meshsizeType = TanhStretching;
          bool buffer=false;
          readBoolMandatory(buffer, node,"stretchX");
          parameters.geometry.stretchX = (int) buffer;
          readBoolMandatory(buffer, node,"stretchY");
          parameters.geometry.stretchY = (int) buffer;
          if (parameters.geometry.dim == 3){
            readBoolMandatory(buffer, node,"stretchZ");
            parameters.geometry.stretchZ = (int) buffer;
          } else {
            parameters.geometry.stretchZ = false;
          }
        } else {
          handleError(1, "Unknown 'mesh'!");
        }

        // Now, the size of the elements should be set

        _dim = parameters.geometry.dim;

        //--------------------------------------------------
        // Timestep parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("timestep");

        if (node == NULL){
            handleError(1, "Error loading timestep parameters");
        }

        readFloatOptional(parameters.timestep.dt, node, "dt", 1);
        readFloatOptional(parameters.timestep.tau, node, "tau", 0.5);

        //--------------------------------------------------
        // Flow parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("flow");

        if (node == NULL){
            handleError(1, "Error loading flow parameters");
        }

        readFloatMandatory(parameters.flow.Re, node, "Re");

        //--------------------------------------------------
        // Solver parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("solver");

        if (node == NULL){
            handleError(1, "Error loading solver parameters");
        }

        readFloatMandatory(parameters.solver.gamma, node, "gamma");
        readIntOptional (parameters.solver.maxIterations, node, "maxIterations");

        //--------------------------------------------------
        // Environmental parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("environment");

        if (node == NULL){
            handleError(1, "Error loading environmental  parameters");
        }

        readFloatOptional(parameters.environment.gx, node, "gx");
        readFloatOptional(parameters.environment.gy, node, "gy");
        readFloatOptional(parameters.environment.gz, node, "gz");

        //--------------------------------------------------
        // Simulation parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("simulation");

        if (node == NULL){
            handleError(1, "Error loading simulation parameters");
        }

        readFloatMandatory(parameters.simulation.finalTime, node, "finalTime");

        subNode = node->FirstChildElement("type");
        if (subNode != NULL){
            readStringMandatory(parameters.simulation.type, subNode);
        } else {
            handleError (1, "Missing type in simulation parameters");
        }

        subNode = node->FirstChildElement("scenario");
        if (subNode != NULL){
            readStringMandatory(parameters.simulation.scenario, subNode);
        } else {
            handleError (1, "Missing scenario in simulation parameters");
        }

        //--------------------------------------------------
        // VTK parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("vtk");

        if (node == NULL){
            handleError(1, "Error loading VTK parameters");
        }

        readFloatOptional(parameters.vtk.interval, node, "interval");
        readStringMandatory(parameters.vtk.prefix, node);

        //--------------------------------------------------
        // StdOut parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("stdOut");

        if (node == NULL){
            handleError(1, "Error loading StdOut parameters");
        }

        // If no value given, print every step
        readFloatOptional(parameters.stdOut.interval, node, "interval", 1);

        //--------------------------------------------------
        // Parallel parameters
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("parallel");

        if (node == NULL){
            handleError(1, "Error loading parallel parameters");
        }

        readIntOptional(parameters.parallel.numProcessors[0], node, "numProcessorsX", 1);
        readIntOptional(parameters.parallel.numProcessors[1], node, "numProcessorsY", 1);
        readIntOptional(parameters.parallel.numProcessors[2], node, "numProcessorsZ", 1);

        // Start neighbors on null in case that no parallel configuration is used later.
        parameters.parallel.leftNb = MPI_PROC_NULL;
        parameters.parallel.rightNb = MPI_PROC_NULL;
        parameters.parallel.bottomNb = MPI_PROC_NULL;
        parameters.parallel.topNb = MPI_PROC_NULL;
        parameters.parallel.frontNb = MPI_PROC_NULL;
        parameters.parallel.backNb = MPI_PROC_NULL;

        // Yet more parameters initialized in case that no parallel configuration is applied
        parameters.parallel.localSize[0] = parameters.geometry.sizeX;
        parameters.parallel.localSize[1] = parameters.geometry.sizeY;
        parameters.parallel.localSize[2] = parameters.geometry.sizeZ;

        parameters.parallel.firstCorner[0] = 0;
        parameters.parallel.firstCorner[1] = 0;
        parameters.parallel.firstCorner[2] = 0;

        // VTK output is named after the rank, so we define it here, again, in case that it's not
        // initialized anywhere else.
        parameters.parallel.rank = rank;

        //--------------------------------------------------
        // Walls
        //--------------------------------------------------

        node = confFile.FirstChildElement()->FirstChildElement("walls");

        if (node == NULL){
            handleError(1, "Error loading wall parameters");
        }


        tinyxml2::XMLElement *wall;
        wall = node->FirstChildElement("left");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorLeft, parameters.walls.scalarLeft);
        }

        wall = node->FirstChildElement("right");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorRight, parameters.walls.scalarRight);
        }

        wall = node->FirstChildElement("bottom");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorBottom, parameters.walls.scalarBottom);
        }

        wall = node->FirstChildElement("top");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorTop, parameters.walls.scalarTop);
        }

        wall = node->FirstChildElement("front");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorFront, parameters.walls.scalarFront);
        }

        wall = node->FirstChildElement("back");
        if (wall != NULL){
            readWall(wall, parameters.walls.vectorBack, parameters.walls.scalarBack);
        }

        // Set the scalar values to zero;
        // do not set the left pressure value to zero, if we have a pressure-channel
        // scenario -> in this case, we need a fixed pressure value there
        if (parameters.simulation.scenario != "pressure-channel" ){
          parameters.walls.scalarLeft   = 0.0;
        }
        parameters.walls.scalarRight  = 0.0;
        parameters.walls.scalarBottom = 0.0;
        parameters.walls.scalarTop    = 0.0;
        parameters.walls.scalarFront  = 0.0;
        parameters.walls.scalarBack   = 0.0;


        //--------------------------------------------------
        // Backward facing step
        //--------------------------------------------------
        parameters.bfStep.xRatio = -1.0;
        parameters.bfStep.yRatio = -1.0;
        node = confFile.FirstChildElement()->FirstChildElement("backwardFacingStep");
        if (node != NULL){
            readFloatMandatory(parameters.bfStep.xRatio, node, "xRatio");
            readFloatMandatory(parameters.bfStep.yRatio, node, "yRatio");
        }


        //------------------------------------------------------
        // TODO WS2: Turbulence
        //------------------------------------------------------
    }

    // Broadcasting of the values
    MPI_Bcast(&(parameters.geometry.sizeX), 1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.sizeY), 1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.sizeZ), 1, MPI_INT, 0, communicator);

    MPI_Bcast(&(parameters.geometry.dim), 1, MPI_INT, 0, communicator);

    MPI_Bcast(&(parameters.geometry.meshsizeType), 1, MPI_INT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.stretchX),1,MPI_INT,0,communicator);
    MPI_Bcast(&(parameters.geometry.stretchY),1,MPI_INT,0,communicator);
    MPI_Bcast(&(parameters.geometry.stretchZ),1,MPI_INT,0,communicator);
    MPI_Bcast(&(parameters.geometry.lengthX),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.lengthY),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.geometry.lengthZ),  1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.timestep.dt),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.timestep.tau), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.flow.Re), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.solver.gamma),         1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.solver.maxIterations), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.environment.gx), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.environment.gy), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.environment.gz), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.simulation.finalTime), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(&(parameters.vtk.interval), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.stdOut.interval), 1, MPI_INT, 0, communicator);

    broadcastString (parameters.vtk.prefix, communicator);
    broadcastString (parameters.simulation.type, communicator);
    broadcastString (parameters.simulation.scenario, communicator);

    MPI_Bcast(&(parameters.bfStep.xRatio), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.bfStep.yRatio), 1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(parameters.parallel.numProcessors, 3, MPI_INT, 0, communicator);

    MPI_Bcast(&(parameters.walls.scalarLeft),   1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarRight),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarBottom), 1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarTop),    1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarFront),  1, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(&(parameters.walls.scalarBack),   1, MY_MPI_FLOAT, 0, communicator);

    MPI_Bcast(parameters.walls.vectorLeft,   3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorRight,  3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorBottom, 3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorTop,    3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorFront,  3, MY_MPI_FLOAT, 0, communicator);
    MPI_Bcast(parameters.walls.vectorBack,   3, MY_MPI_FLOAT, 0, communicator);

    // TODO WS2: broadcast turbulence parameters


}