/// <summary> /// Creates a Linear Programming problem instance for creating divergence free flows using a tiling method. /// The instance is stored in <see cref="LpModel"/>. /// </summary> /// <param name="minXFlux">Minimum flux in X direction as an integer</param> /// <param name="maxXFlux">Maximum flux in X direction as an integer</param> /// <param name="minYFlux">Minimum flux in Y direction as an integer</param> /// <param name="maxYFlux">Maximum flux in Y direction as an integer</param> /// <param name="currentTileGrid"></param> public static void BuildInitialModel(int minXFlux, int maxXFlux, int minYFlux, int maxYFlux, TileGrid currentTileGrid, int?[, ][] boundaryConditions) { int gridDimension = currentTileGrid.Dimension; //Number of variables is 2*n*(n+1) for n x n - grid LpModel = lpsolve.make_lp(0, 2 * gridDimension * (gridDimension + 1)); //We always have four variables in the constraint int[] tileEdges = new int[4]; //Improves performance when adding rows to model lpsolve.set_add_rowmode(LpModel, 1); lpsolve.set_obj_fn(LpModel, new double[2 * gridDimension * (gridDimension + 1)]); //Add incompressibility constraint to all cells which don't already have a flowtile on them. for (int row = 0; row < gridDimension; row++) { for (int column = 0; column < gridDimension; column++) { if (!currentTileGrid.HasTile(row, column)) { tileEdges = TileEdgeIndices(row, column, gridDimension); lpsolve.add_constraintex(LpModel, 4, new double[] { -1, -1, 1, 1 }, tileEdges, lpsolve.lpsolve_constr_types.EQ, 0); } } } //Add row mode has to be turned off before continuing with other operations, if not then program crashes. lpsolve.set_add_rowmode(LpModel, 0); bool[] edgeSetFlag = new bool[2 * gridDimension * (gridDimension + 1) + 1]; //Iterate over all rows and columns in the grid and set bounds on variables. for (int row = 0; row < gridDimension; row++) { for (int column = 0; column < gridDimension; column++) { tileEdges = TileEdgeIndices(row, column, gridDimension); if (!currentTileGrid.HasTile(row, column)) { //Set the bounds for each edge flow. Zero if it is a boundary edge. if (!edgeSetFlag[tileEdges[(int)Direction.Top]]) { int?boundaryCondition = boundaryConditions[row, column][(int)Direction.Top]; if (boundaryCondition != null) { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Top], (double)boundaryCondition, (double)boundaryCondition); edgeSetFlag[tileEdges[(int)Direction.Top]] = true; } else { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Top], minYFlux, maxYFlux); } } if (!edgeSetFlag[tileEdges[(int)Direction.Right]]) { int?boundaryCondition = boundaryConditions[row, column][(int)Direction.Right]; if (boundaryCondition != null) { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Right], (double)boundaryCondition, (double)boundaryCondition); edgeSetFlag[tileEdges[(int)Direction.Right]] = true; } else { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Right], minXFlux, maxXFlux); } } if (!edgeSetFlag[tileEdges[(int)Direction.Bottom]]) { int?boundaryCondition = boundaryConditions[row, column][(int)Direction.Bottom]; if (boundaryCondition != null) { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Bottom], (double)boundaryCondition, (double)boundaryCondition); edgeSetFlag[tileEdges[(int)Direction.Bottom]] = true; } else { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Bottom], minYFlux, maxYFlux); } } if (!edgeSetFlag[tileEdges[(int)Direction.Left]]) { int?boundaryCondition = boundaryConditions[row, column][(int)Direction.Left]; if (boundaryCondition != null) { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Left], (double)boundaryCondition, (double)boundaryCondition); edgeSetFlag[tileEdges[(int)Direction.Left]] = true; } else { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Left], minXFlux, maxXFlux); } } } //Else there is a tile on the slot and the values are bounded by the flows from that tile else { lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Top], currentTileGrid.GetFlowTile(row, column).Flux.TopEdge, currentTileGrid.GetFlowTile(row, column).Flux.TopEdge); //Sets a flag that the edge flux has been set so that the value isn't overridden later. edgeSetFlag[tileEdges[(int)Direction.Top]] = true; lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Right], currentTileGrid.GetFlowTile(row, column).Flux.RightEdge, currentTileGrid.GetFlowTile(row, column).Flux.RightEdge); //Sets a flag that the edge flux has been set so that the value isn't overridden later. edgeSetFlag[tileEdges[(int)Direction.Right]] = true; lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Bottom], currentTileGrid.GetFlowTile(row, column).Flux.BottomEdge, currentTileGrid.GetFlowTile(row, column).Flux.BottomEdge); //Sets a flag that the edge flux has been set so that the value isn't overridden later. edgeSetFlag[tileEdges[(int)Direction.Bottom]] = true; lpsolve.set_bounds(LpModel, tileEdges[(int)Direction.Left], currentTileGrid.GetFlowTile(row, column).Flux.LeftEdge, currentTileGrid.GetFlowTile(row, column).Flux.LeftEdge); //Sets a flag that the edge flux has been set so that the value isn't overridden later. edgeSetFlag[tileEdges[(int)Direction.Left]] = true; } } } lpsolve.set_verbose(LpModel, 0); }