Jump to content
Mr. Bodo Hasubek

Run a design engine on an arbitrary external parameter set

Recommended Posts

Dear all,

 

I have a hull design that depends on several parameters. In a first run I used an EnsembleInvestigation to find feasable designs whereby I did some parameter optimization.

Now I would like to use the feasable designs as input parameters combinations for a SHIPFLOW analysis. Of course this could be all done in one step, but I would like to be able to add some designs manually in an Excelsheet to compare my intuition with hard mathematics. Furthermore, I would like to accumulate the results from several SHIPFLOW calculations per design (basically looking at different Froude numbers) to calculate a parameter to rank the designs based on these several SHIPFLOW calculations. Manual evaluation is out, because we are talking about several thousand results. Obviously I would like to name the designs in a way that I can easily trace them back to my input parameter sets. I already wrote a nice feature to import an arbitrary CSV file into a table to get my desired parameter combinations into SHIPFLOW.

Now I see two principle ways to solve that:

  1. I could try to create an EnsembleInvestigation in which I try to manipulate the hull parameters before I start SHIPFLOW and after it finishes. I tried that, but ran into some problems (furthermore this is not my preferred way)
  2. I could create a feature that sets my hull parameters, creates a copy of the design and runs SHIPFLOW in this newly created design (this approach looks preferable to me since better control is possible).

I am experiencing some difficulties with approach 2 as well: I cannot create a copy of the baseline within the feature that apperears properly in the Optimization tab. If I do a copy from the console using design(getcurrentdesign()) I only get an empty new design. How can I create a complete copy within a feature that properly appears in the Optimization tab? How do I change the current design? Can I start an EnsembleInvestigation in such a new design from within my feature?

 

Or: Is there an easier better way to achieve my goal?

The easiest solution -at least to start with - would be if I could provide the EnsembleInvestigation with valid tupels of parameters only, but I couldn't figure out if that's possible.

 

Many thanks,

Bodo

Share this post


Link to post
Share on other sites

Hi Bodo,

 

Approach 2 cannot work and is not supposed to work, to be honest. The problem here is that the Feature "lives" and executes in a certain design. So when creating a new design and changing into it within a Feature, this will cause undefined behavior, and we actually took some measures to avoid it being possible to even change in new designs from within a feature. If we've missed a way, please tell us which one, so we can block it ;)

 

So, on to your problem: I think the best way is to use an fsc script to achieve this "manual design engine". You could, for example, write a feature that creates such a script. The Feature gets the tuples of variable values that you want and creates a script that looks somewhat like this (assuming, that you have, for example, two variables called v1 and v2 and two evaluations called e1 and e2):

 

Feature input:

  • objectlist "values": containing objectlists with the tuples for the variable values
  • design "baselineDesign": the baseline design to run the "manual design engine" from
FFile myScript("/path/to/script.fsc")
if (!myScript.openWrite())
  echo("Could not open file for writing")
  break()
endif

//make sure the script starts in the desired baseline
myScript.writeLine(baselineDesign.getName() + ".edit()")

foreach(FObjectList tuple in values)
  // this creates the new design based on the current design
  // and makes it the current one
  myScript.writeLine("design(NULL, NULL)")

  //set the variable values
  myScript.writeLine("v1 = tuple.at(0).castTo(FDouble)")
  myScript.writeLine("v2 = tuple.at(1).castTo(FDouble)")

  // trigger the evaluations
  myScript.writeLine("e1.getValue()")
  myScript.writeLine("e2.getValue()")

  // change back into the baseline design for the next design
  myScript.writeLine(baselineDesign.getName() + ".edit()")
endfor

myScript.close()

You could then wrap this into another fsc, for example runManualDesignEngine.fsc (assuming you have created a feature from the previous definition called "scriptCreator" and filled the arguments with the desired values):

scriptCreator.run()
source("/path/to/script.fsc")

Now, in order to run your manual design engine just call "source("runManualDesignEngine.fsc")" on the console or use the "Execute Script" entry from the file menu.

 

To be honest, I did not test this, but I think, it should work.

 

Of course, if you want the variable values to be extracted from an Excelsheet, instead of passing them into the Feature, you can communicate with Excel using the COM interface, but I'd have to pass on to someone else regarding that, since I don't have any experience with it, so far.

 

I hope this helps.

 

Arne

 

P.S.: Of course, you can also use your favorite external script language to create your fsc script, but I think it's nicer to keep it all inside the Framework since you already have the object names and all that...

  • Upvote 1

Share this post


Link to post
Share on other sites

Oh, I forgot to mention, in case you are not aware of it, of course you can, once all designs are evaluated, create a new Design Results Table from it and drag-and-drop the designs, variables and evaluations into it for post-processing.

 

And one more thing:

In case your evaluations trigger asynchrounous processes (i.e. external programs that will cause objects to become pending), this should work as well. The only pitfall is that, as opposed with regular design engines, the changing back into the designs once they are finished is not done automatically. However, to achive this is not straight forward (unfortunately). Here's my proposal:

 

Create another FeatureDefinition, e.g. called "reEvaluator". It gets one argument of type FObjectList "designs" which is defaulted to an empty list (i.e. "[]"). Create an instance of it in the baselineDesign.

Now, expand your script generation feature like this:

unsigned designCounter(0)
foreach(FObjectList tuple in values)
  // this creates the new design based on the current design
  // and makes it the current one, also rename it to "design + counter"
  myScript.writeLine("design(NULL, NULL).setName(\"design" + designCounter + "\"")

  //set the variable values
  myScript.writeLine("v1 = tuple.at(0).castTo(FDouble)")
  myScript.writeLine("v2 = tuple.at(1).castTo(FDouble)")

  // trigger the evaluations
  myScript.writeLine("e1.getValue()")
  myScript.writeLine("e2.getValue()")

  // change back into the baseline design for the next design
  myScript.writeLine(baselineDesign.getName() + ".edit()")
  myScript.writeLine("reEvaluator.getDesigns().add(\"design"+designCounter+"\")")
  designCounter += 1
endfor

Now, the creator of the "reEvaluator" feature:

FFile reEvaluationScript("/path/to/reEvaluatorScript.fsc")
reEvaluatorScript.openWrite()

foreach(FDesign des in designs)
  // this will change into each design and cause it to leave
  // the pending state
  reEvaluatorScript.writeLine("des.edit()"")
endfor

reEvaluatorScript.close()

Once all designs have left the pending state (i.e. they are blue in the object tree), you can execute the second feature and then run the generated script.

 

As I said, this is not very straight forward, but definitately easier than changing back into all designs manually...

 

Also be aware, that there are some more pitfalls. E.g. when running multiple of those special "design engines" after another, the names given to the designs inside the script will not match the actual names (as they are made unique by the Framework automatically). In that case the re-evaluation will not work like proposed. This is due to the shortcoming of fsc that you cannot create variables of basic types (in this case fobjectlist) inside of them. One command that may come in handy when you play around with this, may be the FDesign.isWaitingOnPendingObjects() command, which is hidden (using that, you could make sure that only those designs are added to the re-evaluation list that are actually pending).

  • Upvote 1

Share this post


Link to post
Share on other sites

Hi Arne,

 

thank you so much!

This is exactly what I wanted to do. The trick appears to be that when the commands are executed in the console they "follow" the changes of the design before the remaining commands are executed.  Obviously this could be done at least theoretically by walking along the object tree of a project within a feature, but that would create much more overhead than a simple context (design) switch as used in your proposal. It should be easy enough to create the appropriate scripts using a feature.

 

I am quite happy reading my design parameters from a CSV file. COM's not needed, but thanks for mentioning.

 

Regarding, the last part of collecting the results in a new design results table. This is clearly desirable. Could that be achieved automatically/programatically? (As I said we are looking at several thousand designs, thus just touching them all only once is a pain). If it's just about values I could probaly just add a few script lines to store those to an external file, if the file access functions in CAESES allow for appending to an existing file (I haven't checked that).

 

Thank you very much again,

Bodo

Share this post


Link to post
Share on other sites

Collecting the values in the ResultsTable could be done in the "reEvaluator" feature. It would need additional input: The FDesignResultsTable to populate ("table"), and a list of variables, evaluations and constraints that should be in the table ("columns"). The full FeatureDefinition could look like this:

FFile reEvaluationScript("/path/to/reEvaluatorScript.fsc")
reEvaluatorScript.openWrite()

bool first(true)

foreach(FDesign des in designs)
  // this will change into each design and cause it to leave
  // the pending state
  reEvaluatorScript.writeLine("des.edit()")

  // add the design to the table
  reEvaluatorScript.writeLine("table.addDesign("+des.getName()+")")
  if (first)
    // add the column objects for the first design (will be added automatically for all others)
    first = false
    foreach(FManaged man in columns)
      reEvaluatorScript.writeLine("table.addColumn("+man.getFullName()+")")
    endfor     
  endif
endfor

reEvaluatorScript.close()

If you don't need the re-evaluator (because your computations are set to synchronous update), you can incorporate this into the original feature.

Edited by Mr. Arne Bergmann
fixed addDesign / addColumn calls to be written out correctly (i.e. including the actual design/object name)
  • Upvote 1

Share this post


Link to post
Share on other sites

Hi Arne,

 

thanks again. You kind of lost me at the following sentcence:

 

"In case your evaluations trigger asynchrounous processes (i.e. external programs that will cause objects to become pending), this should work as well. The only pitfall is that, as opposed with regular design engines, the changing back into the designs once they are finished is not done automatically. However, to achive this is not straight forward (unfortunately)."

 

As far as I understand, my SHIPFLOW calulation are normally run asynchronously by my EnsembleInvestigation (they all turn orange until they are finished)

I don't understand the bold print sentence. What exactly happens or does not happen when asynchronous evaluations are run and why is it a problem?

As far as I understood your feature snipped you basically creates an FObjectList of the names of all the design names.

Since FObjectLists cannot be created on the console, you put this list as an argument  to the feature reEvaluateScript which you then access in the script using ".getDesigns()". In the feature you reEvaluateScript simply "re-visit" all designs.

So far so good, but why is that necessary? Because the script doesn't wait for the pending evaluations? Do I have to wait for the calulations to finish before I run the reEvaluateScript?

 

Just enlighten me, please ;-)

 

Regards,

Bodo

Share this post


Link to post
Share on other sites

Hi,

 

Ok, no problem, I'll try to make it clearer (don't hesitate to ask again if I fail in doing so ;)).

 

Usually when you run a Design Engine like the EnsembleInvestigation, it will create one design, trigger the computation and go on to the next design (leaving the first one in "orange", i.e. "pending"). Now, before a new design is created the DesignEngine will actually check whether one of the pending designs has finished and re-visit it which will trigger the final update (including reading the computation's result files).

 

So, in a "regular" design engine, evaluating a design is (at least) a two step process (provided the evaluations/constraints triffer asynchronous computations): 1. Create design and trigger evaluation, 2. re-visit the design once the computation has left the pending state. I wrote "at least" because the re-visiting could cause another computation to be started (one that depended on the results of the first one), leaving the design in the pending state again.

 

Ok, now, when you start a computation manually in one design that causes objects to become pending, and switch to another design (which is basically what the fsc script is doing), the Framework will NOT automatically re-visit the design once the computation has finished. It will, instead, show the design as blue in the object tree, indicating that the computation has finished. This is the case so the user is not disrupted in his work.

 

So, basically, you are right, all the reEvaluate Script does is re-visit all designs. Otherwise they will stay in the "finished pending" state.

No, the first script does not wait for all pending evaluations. This could only be done, if there were while-loops inside scripts.

Yes, you will need to wait for all calculations to finish before updating the reEvaluate-Feature and running the script generated from that feature.

 

I hope this clears it up.

  • Upvote 1

Share this post


Link to post
Share on other sites

Hi Arne,

 

thank you for your explanations. They brought some light into the darkness, but as you anticipated new answers trigger new questions ;-)

 

The setup given above only revisits the main designs created within the script using the design(NULL, NULL) command while the design engine(s) within these designs will create a bunch of sub-designs for the SHIPFLOW evaluations. Is it enough to only revisit the main designs in order to trigger a proper update of all sub-designs?

 

Since I don't know when the SHIPFLOW calculation finishes, how do I know, when to run the revisiting? What do you mean with making the script wait if there is a while-loop?

 

As long as only one computer is used for the SHIPFLOW computations, the simplest way to avoid these update problems would be to serialize all the evaluations of the DesignEngine, i.e. Let the DesignEngine only create one sub-design with one SHIPFLOW calculation at a time, completely finish it and go on with the next SHIPFLOW evaluation. After the DesignEngine is done, continue with the script. But this is apparently not possible when I use external calculations, is it? 

 

Thanks again,

Bodo

Share this post


Link to post
Share on other sites

Hi Bodo,

 

Apparently, I did not understand the whole plan correctly. I thought, what you want to do is to only evaluate those designs that were created from the script.

 

So, inside those designs there will be a design engine running? If that's the case, the design engine in each design will take care of the pending mechanism in the sub-designs, so, those designs created from the engine will not need to be re-visited manually. However, you will need to also generate the line into the fsc script that runs the design engine.

 

You will see that all SHIPFLOW calculations have finished in the manually created designs by looking at the object tree. Once all of those designs have turned blue (instead of orange), it is time to execute the revisiting.

 

What I meant with the while loop is, that if it was possible to write feature code inside an fsc script, you could keep all designs that are left in the pending state in an objectlist, then do a while loop that checks whether one of those designs is no longer waiting on pending objects, re-visit it and remove it from the objectlist. This would continue until the objectlist is empty. As I said, this is, however, not possible.

 

If you run an EnsembleInvestigation with the manually created design as the baseline, the script will pause until the EnsembleInvestigation is finished (i.e. all computations have finished and all designs have left the pending state). You will only need the re-visiting script, if asynchronous evaluations are triggered within the manually created (i.e. created by the script) designs.

 

Does this help?

 

Arne

  • Upvote 1

Share this post


Link to post
Share on other sites

Hi Arne,

 

yes, your answers hepl a lot. I think now I have most things sorted out.

I'll let you know if I run into trouble during the implementation fro my particular case.

 

Thank you again. I really like your competent and thorough answers.

 

Regards,

Bodo

Share this post


Link to post
Share on other sites

Dear Bodo,

 

I'm glad that my answers were helpful. Please consider to up-vote helpful answers in the forum (bottom right of each post), so other members of the community can identify posts that contain correct and helpful information. Also, consider to down-vote those answers that are not contributing to finding an answer to the question. Afterall, the purpose of a forum like this is to create a knowledge base that helps current and future users of CAESES. Thanks.

 

If further questions arise, I'll be glad to answer them...

 

Regards,

Arne

Share this post


Link to post
Share on other sites

Hi Arne,

 

I promised to give some feedback. Here once again what I am trying to solve:

I have an Excel/CSV table with a set of parameters that define the geometry of my sail boat hull which I created as a parametric model in FFW. Now I want to use an EnsembleInvestigation to run SHIPFLOW for different Froude numbers on each design variant defined by those parameters to find out which ones are most favourable for my desired speed range.

 

In the course of my experiments I figured out that in theory it would be sufficient to change some model parameters and run my EnsembleInvestiagation. The driving force was make data recovery from all calculations as simple as possible. So I tried the following approaches:

  1. Write a Feature that loops through my design parameters and starts my EnsembleInvestigation (EI) after the parameters are set.
    Result: The designs created by the EI.run() did not show up properly in the designs tab and I ended up with a currupted fdb-file.
  2. Do the same as in 1., but with a scipt. The specialty was to change the EI's name each time around to be able to identify the designs later.
    Looks like this (whereby ModelNo = n selects my model parameters from a table and thus changes the model, "MeinEnsemble is the orginal name of the EI):
    baseline.edit()
    ModelNo = 1
    MeinEnsemble.setName("Design_0012")
    Design_0012.setUseResultPool(FALSE)
    Design_0012.run()
    ModelNo = 2
    Design_0012.setName("Design_0013")
    Design_0013.setUseResultPool(FALSE)
    Design_0013.run()
    ModelNo = 3 ...
    Result: Worked nicely up to the second design. Then for no reason the name of the EI changed back to its original ("MeinEnsemble") in the middle of the second run of the EI. Obviously the remaining script lines crashed because the name of the EI had changed (and thus did not exist as expected). The advantage of this approach would have been that all EIs belong to the same design which makes data collection easy.
  3. Implements the original idea as discussed at the beginning of this thread, that is to create a new design manually properly naming for each geometric variant (parameter set) and run the EI there. In order to avoid reloading of the model parameter data table in each Design, the model parameters are set explicitly using the script. Looks like this:
    baseline.edit()
    design(NULL,NULL).setName("Design_0012")
    dDraft = -0.95
    dDraftX = 9
    dBWLmax = 0.8
    dBWLmaxX = 9
    dBWLrear = 0.75
    dSecAX = 9
    dSecA = 1.3588743
    MeinEnsemble.setUseResultPool(FALSE)
    MeinEnsemble.run()
    baseline.edit()
    design(NULL,NULL).setName("Design_0013")
    dDraft = -0.95 ...
    Result: Could be run sucessfully for 5 Designs for 5 Froude numbers in each design. Apparently a re-visiting of the designs is not necessary since the EI appears to take care of all updates. I'll try this approach for 200 designs now. Data collection not solved yet, but I put all the values of interest into the result table views, which are easy enough to access. We'll see what I'll do with it.
    Obviously the scripts were all created using different features.

I hope this helps if anyone is interested in trying similar things.

 

Regards,

Bodo

  • Upvote 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...