CFD Online Discussion Forums

CFD Online Discussion Forums (https://www.cfd-online.com/Forums/)
-   OpenFOAM Community Contributions (https://www.cfd-online.com/Forums/openfoam-community-contributions/)
-   -   [PyFoam] Parallel running with PyFoam (not parallelized) (https://www.cfd-online.com/Forums/openfoam-community-contributions/133169-parallel-running-pyfoam-not-parallelized.html)

Jakob1 April 11, 2014 07:16

Parallel running with PyFoam (not parallelized)
 
I want to carry out a series of simulations with PyFoam. So far they run serially, so one after another, which works fine. To save time, I would like to run them paralley on lets say 4 cores, but not one simulation split onto 4 cores, but 4 simulations, each on one core.

Is there some prepackaged PyFoam utility for that?

Or how would you go about doing that?

gschaider April 13, 2014 09:44

Quote:

Originally Posted by Jakob1 (Post 485434)
I want to carry out a series of simulations with PyFoam. So far they run serially, so one after another, which works fine. To save time, I would like to run them paralley on lets say 4 cores, but not one simulation split onto 4 cores, but 4 simulations, each on one core.

Is there some prepackaged PyFoam utility for that?

Or how would you go about doing that?

There is no support for that in PyFoam because I usually use a proper queuing system to do such a thing (in my case SunGridEngine but there are others too). I then collect the data from the information the cases have written to disk.

If you want it all in one script (job scheduling and data processing) you'll have to write something yourself. There are a number of libraries in Python that support something like this (built in threading or multiprocessing or more advanced stuff - google "python worker thread pool". For a very basic implementation see http://codereview.stackexchange.com/...thread-pooling)

Jakob1 April 15, 2014 13:41

Thanks for your very helpful response Bernhard! As I am not the most proficient programer my solution might be a bit clumsy, but this seems to work, for anyone facing a similar question:

The task was to vary the temperature on a certain boundary of a template case between two limits (tmin, tmax) with a certain step width (tstep), which I enter through a wx GUI. Non-working code snippet containing the crucial parts:

Code:


[...]

        it=tmin
        while it <=tmax:
            if threading.activeCount()<=3:
                case = templateCase.cloneCase(template+"_T_%i" %it)
                bc=ParsedParameterFile(path.join(case.name,"0","T"))
                bc["boundaryField"]self.cbound.GetValue()]"value"].setUniform(it)
                bc.writeFile()
                mFRunner(self.solvers.GetValue(), "-case", case.name).start()
                it+=tstep   
                paths.append(case.name)
        self.Analyze(paths)

[...]

class mFRunner (threading.Thread):
    def __init__(self, solver, id, casename):
        threading.Thread.__init__(self)
        self.solver=solver
        self.id=id
        self.casename=casename
    def run(self):
        BasicRunner(argv=[self.solver, self.id, self.casename]).start()

I am only running on a 4 core CPU right now, so I can't run more than 2 PyFoam threads at the same time (one reserved for the OS, one for the Python script), but as soon as I get to an 8 core I'll try more.

However, monitoring the cores via htop shows 3 cores being ~100% busy (why 3 with 2 PyFoam threads!?). Also they seem to take turns working (the non >95% core keeps switching). Is that normal? Like some kind of internal balancing mechanism inside of the threading class?

gschaider April 15, 2014 19:01

Quote:

Originally Posted by Jakob1 (Post 486318)
Thanks for your very helpful response Bernhard! As I am not the most proficient programer my solution might be a bit clumsy, but this seems to work, for anyone facing a similar question:

The task was to vary the temperature on a certain boundary of a template case between two limits (tmin, tmax) with a certain step width (tstep), which I enter through a wx GUI. Non-working code snippet containing the crucial parts:

Code:


[...]

        it=tmin
        while it <=tmax:
            if threading.activeCount()<=3:
                case = templateCase.cloneCase(template+"_T_%i" %it)
                bc=ParsedParameterFile(path.join(case.name,"0","T"))
                bc["boundaryField"]self.cbound.GetValue()]"value"].setUniform(it)
                bc.writeFile()
                mFRunner(self.solvers.GetValue(), "-case", case.name).start()
                it+=tstep   
                paths.append(case.name)
        self.Analyze(paths)

[...]

class mFRunner (threading.Thread):
    def __init__(self, solver, id, casename):
        threading.Thread.__init__(self)
        self.solver=solver
        self.id=id
        self.casename=casename
    def run(self):
        BasicRunner(argv=[self.solver, self.id, self.casename]).start()

I am only running on a 4 core CPU right now, so I can't run more than 2 PyFoam threads at the same time (one reserved for the OS, one for the Python script), but as soon as I get to an 8 core I'll try more.

However, monitoring the cores via htop shows 3 cores being ~100% busy (why 3 with 2 PyFoam threads!?). Also they seem to take turns working (the non >95% core keeps switching). Is that normal? Like some kind of internal balancing mechanism inside of the threading class?

At first: not bad for a first stab at parallel computation, but this is a field where one learns most from experience ....

About your load: 2 CPUs are your two workers. The third one is your script running in circles "Anyone finished yet? No. Anyone finished yet? No. Anyone finished yet? No. ........." You've got to admire it. It gets a negative answer but it keeps trying .... all the time. That's where the third CPU comes from.

That is what these worker pools are for: When a job is finished the master is woken up and starts a new run.

Anyway. The quick fix to your script would be (I think "sleep" is in the "time" module)
Code:

if threading.activeCount()<=3:
    start new job
else:
    sleep(1)

That way the script only polls every second whether a job has finished using an amount of CPU that is barely measurable. Of course you've got to live with the fact that some runs are started 0.99999 seconds later than they could have been, but the extra CPU (because now you can use that for computations too) should make up for this

Jakob1 April 16, 2014 13:48

That persistence! It is almost ... machine like!

All jokes aside, that really helped again.

However I don't understand how the thread count works.
If I allow max 1 thread, it runs 1 simulation in parallel.
If I allow max 2-5 threads, it runs 2 simulations in parallel.
If I allow max 6-9 threads, it runs 3 simulations in parallel.
4,5,6, you see where this is going. And I would not even want to swear that this is reproducible.

Is there any particular thing I have missed? This does not make much sense to me.


All times are GMT -4. The time now is 16:16.