ATTENTIONThis FlexSim Community Forum is read-only. Please post any new questions, ideas, or discussions to our new community (we call it Answers) at https://answers.flexsim.com/. Our new Question & Answer site brings a modern, mobile-friendly interface and more focus on getting answers quickly. There are a few differences between how our new Q&A community works vs. a classic, threaded-conversation-style forum like the one below, so be sure to read our Answers Best Practices. |
flexsim.com |
#1
|
|||
|
|||
Automated Layout of Pipes - Stage 1
Hi,
i have been working on automated layout of pipes as part of the model I am building. I provide a full description below as a learning resource for the community out of respect to all who have answered my questions along the way (e.g. Jason Lightfoot, Phil Bobo etc). Thanks to all of you. The task at hand was to create a means of automatic layout of SIS (Surface to In Seam) gas wells, given any (x, y) location. SIS Wells produce both gas and water, and so two fluid generators are needed. Each well is actually a little template that includes a local reservoir for water. The gas and the water are piped to a common extraction plant and central reservoir respectively. The template needs to be rotated depending on the angle between it and the extraction plant (i.e. the template at -90 degrees is the mirror of the template at 90 degrees). I use two different approaches to calculate the angles, firstly a vector approach and secondly a trigonmetric approach, so as to demonstrate the concept most fully (i.e. either approach can be used by itself). In the file attachments I include a powerpoint file which outlines all of the calculations involved, and the code is fully commented. The coordinates of the central extraction plant and central reservoir are stored as two global arrays, and a global table lists the names and details of each well. I then use two user commands to draw all of the connections as laid out below: -firstly, the command DrawSISWell(num x, num y, num wellnum), where the well number is the row of the global table. This user command uses the vector mathematics approach. Code:
/**Custom Code*/ /* setup the draw SIS Well User Command using the following algorithm Input is: - x loc of the top left corner of the well -y loc of the top left corner of the well - well number, and row number in the global table, SISTable Note that then all other locations (e.g. connecting pipes) depend on the x,y dimensions of the well image, which are the variables wellxdim, wellydim. */ // ALGORITHM // 0. Initialise User command // 1. Create VisualTool and set name based on Global Table "SISTable" // 2. Create Fluid Generator - H2O inside Visual Tool, set location and name // 3. Create a 2nd Fluid Generator (CH4) - inside Visual Tool, set location and name // 4. Hide contents of myVT and change shape to cylinder // 5. Create the other objects needed at the origin // 6. Setup the Pipe Sections // 7. Connect the objects together // 8. Determine angle between extractor pipe and SISWell // 9. Choose layout case and layout the well, then call the PipeConnect User command // //First setup variables treenode myVT; treenode GasGen; treenode H2OGen; treenode H2OPipe; treenode CH4Pipe; treenode H2OPipeR; treenode H2OReservoir; treenode SectionTable; treenode RPipe; treenode EPipe; int myx; //input x location for well int myy; //input y location for well double wellx; //unit vector, x component for well location double welly; //unit vector, y component for well location double alpha; // angle between unit vector and EPipe int EPipex = 1; // unit vector, x component for exhaust pipe int EPipey = 0; //unit vector, y component for exhaust pipe int wellnum; // row number from global table SISTable string tmpstring; int wellxdim = 1; int wellydim =1; int wellzdim =2; // Get argument parameters myx = parval(1); myy = parval(2); wellnum = parval(3); RPipe = node("/ReservoirPipe", model()); EPipe = node("/ExhaustPipe", model()); // // Start algorithm // // 1. Create VisualTool and set name based on Global Table "SISTable" myVT = createinstance(node("/VisualTool",library()),model()); tmpstring = gettablestr("SISTable", wellnum, 2); setname(myVT, tmpstring); // // 2. Create Fluid Generator - H2O inside Visual Tool, set location and name H2OGen = createinstance(node("/FluidGenerator",library()),myVT); tmpstring = concat("Water", numtostring(wellnum)); setname(H2OGen, tmpstring); setloc(H2OGen, 1, -3, 0); // // 3. Create a 2nd Fluid Generator (CH4) - inside Visual Tool, set location and name GasGen = createinstance(node("/FluidGenerator",library()),myVT); tmpstring = concat("Methane", numtostring(wellnum)); setname(GasGen, tmpstring); setloc(GasGen, 5, -3, 0); // // 4. Hide contents of myVT and change shape to cylinder setvarnum(myVT, "shapetypes", 5); // set "Visual Display" to "Imported Shape" set(luminous(myVT),0); // turn off luminous setsize(myVT,wellxdim,wellydim,wellzdim); setnodestr(node(">visual/shape", myVT), "fs3d\\General\\Cylinder.3ds"); setobjectshapeindex(myVT, 2); colorpurple(myVT); switch_hidecontents(myVT, 1); // // 5. Create the other objects needed at the origin, name them and set pipe sections H2OReservoir = createinstance(node("/FluidTank", library()), model()); tmpstring = concat("Reservoir", numtostring(wellnum)); setname(H2OReservoir, tmpstring); H2OPipe = createinstance(node("/FluidPipe", library()), model()); tmpstring = concat("H2OPipe", numtostring(wellnum)); setname(H2OPipe, tmpstring); CH4Pipe = createinstance(node("/FluidPipe", library()), model()); tmpstring = concat("CH4Pipe", numtostring(wellnum)); setname(CH4Pipe, tmpstring); H2OPipeR = createinstance(node("/FluidPipe", library()), model()); tmpstring = concat("H2OPipeR", numtostring(wellnum)); setname(H2OPipeR, tmpstring); // // 6. Setup the Pipe Sections // 6a. H2OPipe Sections SectionTable = getvarnode(H2OPipe, "pipesections"); settablenum(SectionTable, 1, 1, 1.0); // length section 1 set to 1m settablenum(SectionTable, 1, 3, 90); //rotate 2nd section 90 degrees in the z plane addtablerow(SectionTable); // section 2 settablenum(SectionTable, 2, 1, 4.5); // 2nd section length 4.5 m settablenum(SectionTable, 2, 2, 0.2); settablenum(SectionTable, 2, 3, 90); //rotate 3rd section 90 degrees in the z plane settablenum(SectionTable, 2, 5, 1.0); settablenum(SectionTable, 2, 8, 0.14); settablenum(SectionTable, 2, 9, 0.21); addtablerow(SectionTable); // section 3 settablenum(SectionTable, 3, 1, 1.5); // project slightly into reservoir settablenum(SectionTable, 3, 2, 0.2); settablenum(SectionTable, 3, 5, 1.0); settablenum(SectionTable, 3, 8, 0.14); settablenum(SectionTable, 3, 9, 0.21); // 6b. H2OPipeR Sections SectionTable = getvarnode(H2OPipeR, "pipesections"); settablenum(SectionTable, 1, 1, -0.5); // Extend back into the Reservoir addtablerow(SectionTable); //Section 2 settablenum(SectionTable, 2, 1, 1.5); // Extend 1m from its x,y location settablenum(SectionTable, 2, 2, 0.2); settablenum(SectionTable, 2, 5, 1.0); settablenum(SectionTable, 2, 8, 0.14); settablenum(SectionTable, 2, 9, 0.21); // 6c. CH4Pipe Sections SectionTable = getvarnode(CH4Pipe, "pipesections"); settablenum(SectionTable, 1, 1, 1.0); //Extend 1 m from CH4Pipe x,y // // 7. Connect the objects together contextdragconnection(GasGen, CH4Pipe, "A"); contextdragconnection(H2OGen, H2OPipe, "A"); contextdragconnection(H2OPipe, H2OReservoir, "A"); contextdragconnection(H2OReservoir, H2OPipeR, "A"); contextdragconnection(CH4Pipe, EPipe, "A"); contextdragconnection(H2OPipeR, RPipe, "A"); // // // 8. Determine angle between extractor pipe and SISWell wellx = (myx - EPipeXY[1])/(sqrt(sqr(myx - EPipeXY[1]) + sqr(myy - EPipeXY[2]))); welly = (myy - EPipeXY[2])/(sqrt(sqr(myx - EPipeXY[1]) + sqr(myy - EPipeXY[2]))); alpha = acos((wellx*EPipex + welly*EPipey)/(sqrt(sqr(wellx)+sqr(welly)) * sqrt(sqr(EPipex)+sqr(EPipey)) )); alpha = alpha * 180/pi(); if(myy < EPipeXY[2]) alpha = 360 - alpha; // // 9. Choose layout case and layout the well, then call the PipeConnect User command // a. cannot draw, b. 90 degree layout, c. standard layout, d. -90 degree layout // // 9a. test for cannot draw case if(alpha <45 || alpha > 315) { // we cannot draw this case msg("Error Message", "Cannot Draw SISWell because of potential collisions", 0); } // // 9b. test for 90 degree case if(alpha >45 && alpha <135) { // draw the 90 degree layout setloc(myVT, myx, myy, 0); setloc(H2OReservoir, (myx - 5), myy, 0); setloc(CH4Pipe, (myx + wellxdim/2 - 0.5), (myy - wellydim), 0); setrot(CH4Pipe, 0, 0, -90); setloc(H2OPipe, (myx + wellxdim/2 - 0.5), (myy + 1), 0); setrot(H2OPipe, 0, 0, 90); setloc(H2OPipeR, (myx - 4.5), (myy - 2), 0); setrot(H2OPipeR, 0, 0, -90); // connect the pipes ConnectPipes(CH4Pipe, (myx + wellxdim/2), (myy - wellydim - 1), EPipeXY[1], EPipeXY[2], alpha); ConnectPipes(H2OPipeR, (myx - 4), (myy - 3), RPipeXY[1], RPipeXY[2], alpha); } // // 9c. test for standard case if(alpha > 135 && alpha < 225) { // draw the standard case setloc(myVT, myx, myy, 0); setloc(H2OReservoir, myx , (myy - wellydim - 3), 0); setloc(CH4Pipe, (myx + wellxdim), (myy - wellydim/2 + 0.5), 0); setloc(H2OPipe, (myx - 1), (myy - wellydim/2 + 0.5), 0); setrot(H2OPipe, 0, 0, 180); setloc(H2OPipeR, (myx + 2), (myy - wellydim - 3.5), 0); // connect the pipes ConnectPipes(CH4Pipe, (myx + wellxdim + 1), (myy - wellydim/2), EPipeXY[1], EPipeXY[2], alpha); ConnectPipes(H2OPipeR, (myx + 3), (myy - wellydim -4), RPipeXY[1], RPipeXY[2], alpha); } // // 9d. test for the -90 degree case if(alpha > 225 && alpha < 315) { // draw the -90 degree case setloc(myVT, myx, myy, 0); setloc(H2OReservoir, (myx + wellxdim + 3) , (myy + 1), 0); setloc(CH4Pipe, (myx + wellxdim/2 - 0.5), (myy + 1), 0); setrot(CH4Pipe, 0, 0, 90); setloc(H2OPipe, (myx + wellxdim/2 - 0.5), (myy - wellydim), 0); setrot(H2OPipe, 0, 0, -90); setloc(H2OPipeR, (myx + wellxdim + 3.5), (myy + 2), 0); setrot(H2OPipeR, 0, 0, 90); // connect the pipes ConnectPipes(CH4Pipe, (myx + wellxdim/2), (myy + 1), EPipeXY[1], EPipeXY[2], alpha); ConnectPipes(H2OPipeR, (myx + wellxdim + 4), (myy + 2), RPipeXY[1], RPipeXY[2], alpha); } |
#2
|
|||
|
|||
Automated Layout of Pipes - Stage 1 (cont.)
Hi,
Continued from the previous post.... The user command DrawSISWell(num x, num y, num wellnum) calls a second user command as a sub-process. This second user command is ConnectPipe(treenode frompipe, num fpx, num fpy, num tpx, num tpy, num alpha), and this time I use trigonometry to solve the angles needed. Again see the attached powerpoint for details on the calculations. The code for the ConnectPipes user command is as follows: Code:
/**Custom Code*/ //Connect two pipes by setting sections in the "frompipe" layout table to // connect from point fpx, fpy down to point tpx, tpy // // Declare variables treenode frompipe; treenode mySectionTable; double fpx; // from pt x double fpy; // from pt y double tpx; // to pt x double tpy; // to pt y double distx; double disty; double length; double alpha; double theta; double returntheta; int row; // // Get function parameters frompipe = parnode(1); fpx = parval(2); fpy = parval(3); tpx = parval(4); tpy = parval(5); alpha = parval(6); // // Get pipe length distx = sqrt(sqr(fpx - tpx)); disty = sqrt(sqr(fpy - tpy)); length = sqrt(sqr(distx) + sqr(disty)); // // // // //Setup the angles depending on which case it is // // test for 90 degree case if(alpha >45 && alpha <135) { theta = atan( distx/disty ); theta = theta * 180/pi(); if(tpx < fpx) { theta = -theta; } returntheta = -theta + 90; } // // test for standard case if(alpha > 135 && alpha < 225) { theta = atan( disty/distx ); theta = theta * 180/pi(); if(tpy < fpy) { theta = -theta; } returntheta = -theta; } // // test for the -90 degree case if(alpha > 225 && alpha < 315) { theta = atan( distx/disty ); theta = theta * 180/pi(); if(tpx > fpx) { theta = -theta; } returntheta = -theta - 90; } // // Write section table on the "from" pipe // mySectionTable = getvarnode(frompipe, "pipesections"); addtablerow(mySectionTable); // add new section onto the end row = gettablerows(mySectionTable); // get the row number settablenum(mySectionTable, (row-1), 3, theta); // set the angle on the section before settablenum(mySectionTable, row, 1, length); // Extra section length settablenum(mySectionTable, row, 2, 0.2); settablenum(mySectionTable, row, 3, returntheta); //rotate next section to follow the to pipe settablenum(mySectionTable, row, 5, 1.0); settablenum(mySectionTable, row, 8, 0.14); settablenum(mySectionTable, row, 9, 0.21); addtablerow(mySectionTable); // End section settablenum(mySectionTable, (row+1), 1, 1.0); // Project into pipe settablenum(mySectionTable, (row+1), 2, 0.2); settablenum(mySectionTable, (row+1), 3, 0); settablenum(mySectionTable, (row+1), 5, 1.0); settablenum(mySectionTable, (row+1), 8, 0.14); settablenum(mySectionTable, (row+1), 9, 0.21); If you download the attached zip file and extract the model, it has the central reservoir and extraction plant, the global variables and the global table needed for my example. You can examine the effect of the user commands by using the following commands in the script window Code:
DrawSISWell(8,30,2); DrawSISWell(0,30,2); DrawSISWell(-20, 12, 3); DrawSISWell(-20, 2, 3); DrawSISWell(-3, -20, 4); DrawSISWell(8, -20, 4); I note that in actual practice the wells are km's apart from each other and at a 1m resolution (i.e. the wells are 5,000 squares away from the central plant) it is expected that the interactions will not be as annoying. Also in an industrial plant, the pipes tend to get laid out into common channels once they get close to the central equipment. If I get motivated I may improve these user commands by including collision detection and common channels, and if so I will post this code for those who are interested. Anyway, this post is for those in the community who are interested in automated layout and I provide it as thanks to Jason, Phil and others who have helped me. Thanks Brett PS If you read through the code, you will notice that the efficiency of loading information into tables using Flexscript is very low (i.e. takes many lines), and as a consequence I propose some improvements to the Flexscript language in another post on this website. http://www.flexsim.com/community/for...1444#post11444 Last edited by brett forbes; 03-25-2013 at 09:32 PM. Reason: Added link to proposal to extend Flexscript |
Thread | Thread Starter | Forum | Replies | Last Post |
optimization of layout | naveenkumar | Tips and Tricks | 7 | 02-02-2012 06:00 AM |
Transfer Area using at automated yard. | smin Jeon | Container Terminal (CT) Library | 2 | 06-30-2011 01:07 AM |
Automated Tree Traversal in node() command? | Nischith Kashyap | Q&A | 8 | 02-07-2011 12:43 AM |
Automated creation of connections between ports | Matthias Hofmann | Q&A | 3 | 06-25-2008 03:04 AM |
Automated runs / Exporting mdb file.. | Nico Zahn | Q&A | 14 | 12-17-2007 11:26 AM |