ATTENTION

This 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

Go Back   FlexSim Community Forum > FlexSim Software > Tips and Tricks
Downloads

Tips and Tricks Share helpful modeling ideas

  #1  
Old 03-25-2013
brett forbes brett forbes is offline
Flexsim User
 
Join Date: Oct 2011
Posts: 34
Downloads: 0
Uploads: 0
Thanks: 27
Thanked 3 Times in 2 Posts
Rep Power: 107
brett forbes is on a distinguished road
Cool 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);
}
Please see next post for continuation
Attached Files
File Type: zip Automated PipeLayout.zip (328.8 KB, 117 views)
  #2  
Old 03-25-2013
brett forbes brett forbes is offline
Flexsim User
 
Join Date: Oct 2011
Posts: 34
Downloads: 0
Uploads: 0
Thanks: 27
Thanked 3 Times in 2 Posts
Rep Power: 107
brett forbes is on a distinguished road
Cool 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);
What you see when you run this command is that there is some level of interaction/collision between the two pipes and I agree that this first version of the pipe connector is a bit clumsy. Also I do not draw the SISWells where the angle between them and the extractor pipe is between -45 degrees and 45 degrees, because of a lack of collision detection.

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


All times are GMT -6.
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.
Copyright 1993-2018 FlexSim Software Products, Inc.